summaryrefslogtreecommitdiffstats
path: root/libexec
diff options
context:
space:
mode:
Diffstat (limited to 'libexec')
-rw-r--r--libexec/Makefile95
-rw-r--r--libexec/Makefile.amd645
-rw-r--r--libexec/Makefile.i3865
-rw-r--r--libexec/Makefile.inc7
-rw-r--r--libexec/Makefile.pc984
-rw-r--r--libexec/atf/Makefile30
-rw-r--r--libexec/atf/Makefile.inc32
-rw-r--r--libexec/atf/atf-check/Makefile49
-rw-r--r--libexec/atf/atf-check/Makefile.inc3
-rw-r--r--libexec/atf/atf-check/tests/Makefile12
-rw-r--r--libexec/atf/atf-sh/Makefile59
-rw-r--r--libexec/atf/atf-sh/tests/Makefile30
-rw-r--r--libexec/atf/tests/Makefile10
-rw-r--r--libexec/atrun/LEGAL31
-rw-r--r--libexec/atrun/Makefile32
-rw-r--r--libexec/atrun/atrun.c589
-rw-r--r--libexec/atrun/atrun.man82
-rw-r--r--libexec/atrun/gloadavg.c72
-rw-r--r--libexec/atrun/gloadavg.h29
-rw-r--r--libexec/bootpd/Announce65
-rw-r--r--libexec/bootpd/Changes294
-rwxr-xr-xlibexec/bootpd/ConvOldTab.sh141
-rw-r--r--libexec/bootpd/Installation29
-rw-r--r--libexec/bootpd/Makefile18
-rw-r--r--libexec/bootpd/Makefile.UNIX204
-rw-r--r--libexec/bootpd/Makefile.inc5
-rw-r--r--libexec/bootpd/Problems66
-rw-r--r--libexec/bootpd/README136
-rw-r--r--libexec/bootpd/ToDo61
-rw-r--r--libexec/bootpd/bootp.h149
-rw-r--r--libexec/bootpd/bootpd.8310
-rw-r--r--libexec/bootpd/bootpd.c1408
-rw-r--r--libexec/bootpd/bootpd.h213
-rw-r--r--libexec/bootpd/bootpgw/Makefile12
-rw-r--r--libexec/bootpd/bootpgw/bootpgw.c677
-rw-r--r--libexec/bootpd/bootptab.5430
-rw-r--r--libexec/bootpd/bootptab.cmu124
-rw-r--r--libexec/bootpd/bootptab.mcs91
-rw-r--r--libexec/bootpd/bptypes.h23
-rw-r--r--libexec/bootpd/dovend.c408
-rw-r--r--libexec/bootpd/dovend.h6
-rw-r--r--libexec/bootpd/dumptab.c378
-rw-r--r--libexec/bootpd/getether.c389
-rw-r--r--libexec/bootpd/getether.h4
-rw-r--r--libexec/bootpd/getif.c147
-rw-r--r--libexec/bootpd/getif.h4
-rw-r--r--libexec/bootpd/hash.c416
-rw-r--r--libexec/bootpd/hash.h148
-rw-r--r--libexec/bootpd/hwaddr.c352
-rw-r--r--libexec/bootpd/hwaddr.h36
-rw-r--r--libexec/bootpd/lookup.c129
-rw-r--r--libexec/bootpd/lookup.h8
-rw-r--r--libexec/bootpd/patchlevel.h8
-rw-r--r--libexec/bootpd/readfile.c2084
-rw-r--r--libexec/bootpd/readfile.h11
-rw-r--r--libexec/bootpd/report.c138
-rw-r--r--libexec/bootpd/report.h6
-rw-r--r--libexec/bootpd/rtmsg.c239
-rw-r--r--libexec/bootpd/syslog.conf63
-rw-r--r--libexec/bootpd/tools/Makefile6
-rw-r--r--libexec/bootpd/tools/Makefile.inc6
-rw-r--r--libexec/bootpd/tools/bootpef/Makefile13
-rw-r--r--libexec/bootpd/tools/bootpef/bootpef.864
-rw-r--r--libexec/bootpd/tools/bootpef/bootpef.c331
-rw-r--r--libexec/bootpd/tools/bootptest/Makefile12
-rw-r--r--libexec/bootpd/tools/bootptest/bootptest.877
-rw-r--r--libexec/bootpd/tools/bootptest/bootptest.c520
-rw-r--r--libexec/bootpd/tools/bootptest/bootptest.h23
-rw-r--r--libexec/bootpd/tools/bootptest/print-bootp.c491
-rw-r--r--libexec/bootpd/trygetea.c55
-rw-r--r--libexec/bootpd/trygetif.c74
-rw-r--r--libexec/bootpd/trylook.c58
-rw-r--r--libexec/bootpd/tzone.c48
-rw-r--r--libexec/bootpd/tzone.h3
-rw-r--r--libexec/casper/Makefile11
-rw-r--r--libexec/casper/dns/Makefile22
-rw-r--r--libexec/casper/dns/dns.c425
-rw-r--r--libexec/casper/grp/Makefile22
-rw-r--r--libexec/casper/grp/grp.c384
-rw-r--r--libexec/casper/pwd/Makefile22
-rw-r--r--libexec/casper/pwd/pwd.c429
-rw-r--r--libexec/casper/random/Makefile22
-rw-r--r--libexec/casper/random/random.c81
-rw-r--r--libexec/casper/sysctl/Makefile22
-rw-r--r--libexec/casper/sysctl/sysctl.c249
-rw-r--r--libexec/comsat/Makefile7
-rw-r--r--libexec/comsat/comsat.8111
-rw-r--r--libexec/comsat/comsat.c284
-rw-r--r--libexec/dma-mbox-create/Makefile18
-rw-r--r--libexec/dma/Makefile40
-rw-r--r--libexec/fingerd/Makefile12
-rw-r--r--libexec/fingerd/fingerd.8177
-rw-r--r--libexec/fingerd/fingerd.c231
-rw-r--r--libexec/fingerd/pathnames.h34
-rw-r--r--libexec/ftpd/Makefile40
-rw-r--r--libexec/ftpd/config.h281
-rw-r--r--libexec/ftpd/extern.h114
-rw-r--r--libexec/ftpd/ftpchroot.5120
-rw-r--r--libexec/ftpd/ftpcmd.y1813
-rw-r--r--libexec/ftpd/ftpd.8574
-rw-r--r--libexec/ftpd/ftpd.c3490
-rw-r--r--libexec/ftpd/logwtmp.c77
-rw-r--r--libexec/ftpd/pathnames.h40
-rw-r--r--libexec/ftpd/popen.c201
-rw-r--r--libexec/getty/Makefile14
-rw-r--r--libexec/getty/chat.c488
-rw-r--r--libexec/getty/extern.h56
-rw-r--r--libexec/getty/getty.8124
-rw-r--r--libexec/getty/gettytab.5537
-rw-r--r--libexec/getty/gettytab.h173
-rw-r--r--libexec/getty/init.c150
-rw-r--r--libexec/getty/main.c814
-rw-r--r--libexec/getty/pathnames.h36
-rw-r--r--libexec/getty/subr.c693
-rw-r--r--libexec/getty/ttys.5170
-rw-r--r--libexec/hyperv/Makefile10
-rw-r--r--libexec/mail.local/Makefile33
-rw-r--r--libexec/mknetid/Makefile10
-rw-r--r--libexec/mknetid/hash.c168
-rw-r--r--libexec/mknetid/hash.h54
-rw-r--r--libexec/mknetid/mknetid.8152
-rw-r--r--libexec/mknetid/mknetid.c306
-rw-r--r--libexec/mknetid/netid.591
-rw-r--r--libexec/mknetid/parse_group.c157
-rw-r--r--libexec/pppoed/Makefile11
-rw-r--r--libexec/pppoed/pppoed.8219
-rw-r--r--libexec/pppoed/pppoed.c692
-rw-r--r--libexec/rbootd/Makefile11
-rw-r--r--libexec/rbootd/bpf.c406
-rw-r--r--libexec/rbootd/conf.c89
-rw-r--r--libexec/rbootd/defs.h182
-rw-r--r--libexec/rbootd/parseconf.c358
-rw-r--r--libexec/rbootd/pathnames.h49
-rw-r--r--libexec/rbootd/rbootd.8153
-rw-r--r--libexec/rbootd/rbootd.c449
-rw-r--r--libexec/rbootd/rmp.h93
-rw-r--r--libexec/rbootd/rmp_var.h242
-rw-r--r--libexec/rbootd/rmpproto.c583
-rw-r--r--libexec/rbootd/utils.c544
-rw-r--r--libexec/revnetgroup/Makefile10
-rw-r--r--libexec/revnetgroup/hash.c208
-rw-r--r--libexec/revnetgroup/hash.h67
-rw-r--r--libexec/revnetgroup/parse_netgroup.c360
-rw-r--r--libexec/revnetgroup/revnetgroup.8159
-rw-r--r--libexec/revnetgroup/revnetgroup.c179
-rw-r--r--libexec/rlogind/Makefile16
-rw-r--r--libexec/rlogind/rlogind.8193
-rw-r--r--libexec/rlogind/rlogind.c585
-rw-r--r--libexec/rpc.rquotad/Makefile10
-rw-r--r--libexec/rpc.rquotad/rpc.rquotad.867
-rw-r--r--libexec/rpc.rquotad/rquotad.c222
-rw-r--r--libexec/rpc.rstatd/Makefile12
-rw-r--r--libexec/rpc.rstatd/rpc.rstatd.861
-rw-r--r--libexec/rpc.rstatd/rstat_proc.c477
-rw-r--r--libexec/rpc.rstatd/rstatd.c129
-rw-r--r--libexec/rpc.rusersd/Makefile15
-rw-r--r--libexec/rpc.rusersd/extern.h34
-rw-r--r--libexec/rpc.rusersd/rpc.rusersd.864
-rw-r--r--libexec/rpc.rusersd/rusers_proc.c332
-rw-r--r--libexec/rpc.rusersd/rusersd.c112
-rw-r--r--libexec/rpc.rwalld/Makefile12
-rw-r--r--libexec/rpc.rwalld/rpc.rwalld.879
-rw-r--r--libexec/rpc.rwalld/rwalld.c204
-rw-r--r--libexec/rpc.sprayd/Makefile12
-rw-r--r--libexec/rpc.sprayd/rpc.sprayd.859
-rw-r--r--libexec/rpc.sprayd/sprayd.c166
-rw-r--r--libexec/rshd/Makefile13
-rw-r--r--libexec/rshd/rshd.8268
-rw-r--r--libexec/rshd/rshd.c589
-rw-r--r--libexec/rtld-aout/shlib.c325
-rw-r--r--libexec/rtld-aout/shlib.h43
-rw-r--r--libexec/rtld-aout/support.c82
-rw-r--r--libexec/rtld-aout/support.h35
-rw-r--r--libexec/rtld-elf/Makefile90
-rw-r--r--libexec/rtld-elf/Symbol.map34
-rw-r--r--libexec/rtld-elf/amd64/Makefile.inc6
-rw-r--r--libexec/rtld-elf/amd64/elf_rtld.x131
-rw-r--r--libexec/rtld-elf/amd64/reloc.c471
-rw-r--r--libexec/rtld-elf/amd64/rtld_machdep.h82
-rw-r--r--libexec/rtld-elf/amd64/rtld_start.S159
-rw-r--r--libexec/rtld-elf/arm/Makefile.inc1
-rw-r--r--libexec/rtld-elf/arm/reloc.c475
-rw-r--r--libexec/rtld-elf/arm/rtld_machdep.h78
-rw-r--r--libexec/rtld-elf/arm/rtld_start.S99
-rw-r--r--libexec/rtld-elf/debug.c143
-rw-r--r--libexec/rtld-elf/debug.h66
-rw-r--r--libexec/rtld-elf/i386/Makefile.inc6
-rw-r--r--libexec/rtld-elf/i386/Symbol.map7
-rw-r--r--libexec/rtld-elf/i386/elf_rtld.x131
-rw-r--r--libexec/rtld-elf/i386/reloc.c439
-rw-r--r--libexec/rtld-elf/i386/rtld_machdep.h83
-rw-r--r--libexec/rtld-elf/i386/rtld_start.S93
-rw-r--r--libexec/rtld-elf/libmap.c478
-rw-r--r--libexec/rtld-elf/libmap.h8
-rw-r--r--libexec/rtld-elf/malloc.c491
-rw-r--r--libexec/rtld-elf/map_object.c449
-rw-r--r--libexec/rtld-elf/mips/reloc.c648
-rw-r--r--libexec/rtld-elf/mips/rtld_machdep.h78
-rw-r--r--libexec/rtld-elf/mips/rtld_start.S166
-rw-r--r--libexec/rtld-elf/powerpc/Makefile.inc1
-rw-r--r--libexec/rtld-elf/powerpc/reloc.c656
-rw-r--r--libexec/rtld-elf/powerpc/rtld_machdep.h93
-rw-r--r--libexec/rtld-elf/powerpc/rtld_start.S200
-rw-r--r--libexec/rtld-elf/powerpc64/Makefile.inc1
-rw-r--r--libexec/rtld-elf/powerpc64/reloc.c520
-rw-r--r--libexec/rtld-elf/powerpc64/rtld_machdep.h85
-rw-r--r--libexec/rtld-elf/powerpc64/rtld_start.S162
-rw-r--r--libexec/rtld-elf/rtld.1295
-rw-r--r--libexec/rtld-elf/rtld.c5021
-rw-r--r--libexec/rtld-elf/rtld.h406
-rw-r--r--libexec/rtld-elf/rtld_lock.c400
-rw-r--r--libexec/rtld-elf/rtld_lock.h75
-rw-r--r--libexec/rtld-elf/rtld_printf.c501
-rw-r--r--libexec/rtld-elf/rtld_printf.h46
-rw-r--r--libexec/rtld-elf/rtld_tls.h69
-rw-r--r--libexec/rtld-elf/sparc64/Makefile.inc1
-rw-r--r--libexec/rtld-elf/sparc64/reloc.c855
-rw-r--r--libexec/rtld-elf/sparc64/rtld_machdep.h74
-rw-r--r--libexec/rtld-elf/sparc64/rtld_start.S170
-rw-r--r--libexec/rtld-elf/tests/Makefile10
-rw-r--r--libexec/rtld-elf/tests/ld_library_pathfds.c221
-rw-r--r--libexec/rtld-elf/tests/libpythagoras/Makefile16
-rw-r--r--libexec/rtld-elf/tests/libpythagoras/pythagoras.c42
-rw-r--r--libexec/rtld-elf/tests/libpythagoras/pythagoras.h28
-rw-r--r--libexec/rtld-elf/tests/target/Makefile16
-rw-r--r--libexec/rtld-elf/tests/target/target.c39
-rw-r--r--libexec/rtld-elf/xmalloc.c97
-rw-r--r--libexec/save-entropy/Makefile6
-rwxr-xr-xlibexec/save-entropy/save-entropy.sh92
-rw-r--r--libexec/smrsh/Makefile32
-rw-r--r--libexec/talkd/Makefile10
-rw-r--r--libexec/talkd/announce.c165
-rw-r--r--libexec/talkd/extern.h43
-rw-r--r--libexec/talkd/print.c89
-rw-r--r--libexec/talkd/process.c221
-rw-r--r--libexec/talkd/table.c232
-rw-r--r--libexec/talkd/talkd.876
-rw-r--r--libexec/talkd/talkd.c136
-rw-r--r--libexec/tcpd/Makefile23
-rw-r--r--libexec/telnetd/Makefile47
-rw-r--r--libexec/tests/Makefile10
-rw-r--r--libexec/tftp-proxy/Makefile11
-rw-r--r--libexec/tftpd/Makefile13
-rw-r--r--libexec/tftpd/tftp-file.c284
-rw-r--r--libexec/tftpd/tftp-file.h37
-rw-r--r--libexec/tftpd/tftp-io.c475
-rw-r--r--libexec/tftpd/tftp-io.h47
-rw-r--r--libexec/tftpd/tftp-options.c388
-rw-r--r--libexec/tftpd/tftp-options.h62
-rw-r--r--libexec/tftpd/tftp-transfer.c322
-rw-r--r--libexec/tftpd/tftp-transfer.h31
-rw-r--r--libexec/tftpd/tftp-utils.c319
-rw-r--r--libexec/tftpd/tftp-utils.h124
-rw-r--r--libexec/tftpd/tftpd.8306
-rw-r--r--libexec/tftpd/tftpd.c837
-rw-r--r--libexec/ulog-helper/Makefile11
-rw-r--r--libexec/ulog-helper/ulog-helper.c97
-rw-r--r--libexec/ypxfr/Makefile47
-rw-r--r--libexec/ypxfr/yp_dbwrite.c110
-rw-r--r--libexec/ypxfr/ypxfr.8308
-rw-r--r--libexec/ypxfr/ypxfr_extern.h62
-rw-r--r--libexec/ypxfr/ypxfr_getmap.c99
-rw-r--r--libexec/ypxfr/ypxfr_main.c577
-rw-r--r--libexec/ypxfr/ypxfr_misc.c293
-rw-r--r--libexec/ypxfr/ypxfrd_getmap.c144
265 files changed, 56454 insertions, 0 deletions
diff --git a/libexec/Makefile b/libexec/Makefile
new file mode 100644
index 0000000..7d1c1f8
--- /dev/null
+++ b/libexec/Makefile
@@ -0,0 +1,95 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+# $FreeBSD$
+
+.include <src.opts.mk>
+
+SUBDIR= ${_atf} \
+ ${_atrun} \
+ bootpd \
+ ${_casper} \
+ ${_comsat} \
+ ${_dma} \
+ ${_dma-mbox-create} \
+ fingerd \
+ ftpd \
+ getty \
+ ${_mail.local} \
+ ${_mknetid} \
+ ${_pppoed} \
+ rbootd \
+ revnetgroup \
+ ${_rlogind} \
+ rpc.rquotad \
+ rpc.rstatd \
+ rpc.rusersd \
+ rpc.rwalld \
+ rpc.sprayd \
+ ${_rshd} \
+ ${_rtld-elf} \
+ save-entropy \
+ ${_smrsh} \
+ talkd \
+ tcpd \
+ ${_telnetd} \
+ ${_tests} \
+ tftpd \
+ ${_tftp-proxy} \
+ ulog-helper \
+ ${_ypxfr}
+
+.if ${MK_AT} != "no"
+_atrun= atrun
+.endif
+
+.if ${MK_CASPER} != "no"
+_casper= casper
+.endif
+
+.if ${MK_MAIL} != "no"
+_comsat= comsat
+.endif
+
+.if ${MK_DMAGENT} != "no"
+_dma= dma
+_dma-mbox-create= dma-mbox-create
+.endif
+
+.if ${MK_NIS} != "no"
+_mknetid= mknetid
+_ypxfr= ypxfr
+.endif
+
+.if ${MK_NETGRAPH} != "no"
+_pppoed= pppoed
+.endif
+
+.if ${MK_PF} != "no"
+_tftp-proxy= tftp-proxy
+.endif
+
+.if !defined(NO_PIC) && !defined(NO_RTLD)
+_rtld-elf= rtld-elf
+.endif
+
+.if ${MK_RCMDS} != "no"
+_rlogind= rlogind
+_rshd= rshd
+.endif
+
+.if ${MK_SENDMAIL} != "no"
+_mail.local= mail.local
+_smrsh= smrsh
+.endif
+
+.if ${MK_TELNET} != "no"
+_telnetd= telnetd
+.endif
+
+.if ${MK_TESTS} != "no"
+_atf= atf
+_tests= tests
+.endif
+
+.include <bsd.arch.inc.mk>
+
+.include <bsd.subdir.mk>
diff --git a/libexec/Makefile.amd64 b/libexec/Makefile.amd64
new file mode 100644
index 0000000..1092a29
--- /dev/null
+++ b/libexec/Makefile.amd64
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+.if ${MK_HYPERV} != "no"
+SUBDIR+= hyperv
+.endif
diff --git a/libexec/Makefile.i386 b/libexec/Makefile.i386
new file mode 100644
index 0000000..1092a29
--- /dev/null
+++ b/libexec/Makefile.i386
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+.if ${MK_HYPERV} != "no"
+SUBDIR+= hyperv
+.endif
diff --git a/libexec/Makefile.inc b/libexec/Makefile.inc
new file mode 100644
index 0000000..7b6a65f
--- /dev/null
+++ b/libexec/Makefile.inc
@@ -0,0 +1,7 @@
+# @(#)Makefile.inc 8.1 (Berkeley) 6/4/93
+# $FreeBSD$
+
+BINDIR?= /usr/libexec
+
+WARNS?= 6
+WFORMAT?= 1
diff --git a/libexec/Makefile.pc98 b/libexec/Makefile.pc98
new file mode 100644
index 0000000..a755298
--- /dev/null
+++ b/libexec/Makefile.pc98
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+# Because i386 adds extra stuff we don't need or want for PC98 we need
+# an empty file so it doesn't get added.
diff --git a/libexec/atf/Makefile b/libexec/atf/Makefile
new file mode 100644
index 0000000..db7554d
--- /dev/null
+++ b/libexec/atf/Makefile
@@ -0,0 +1,30 @@
+#-
+# Copyright (c) 2011 Google, Inc.
+# 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.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+#
+# $FreeBSD$
+
+SUBDIR= atf-check atf-sh tests
+
+.include <bsd.subdir.mk>
diff --git a/libexec/atf/Makefile.inc b/libexec/atf/Makefile.inc
new file mode 100644
index 0000000..2521be6
--- /dev/null
+++ b/libexec/atf/Makefile.inc
@@ -0,0 +1,32 @@
+#-
+# Copyright (c) 2011 Google, Inc.
+# 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.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+#
+# $FreeBSD$
+
+CFLAGS+= -DHAVE_CONFIG_H
+
+WARNS?= 3
+
+.include "../Makefile.inc"
diff --git a/libexec/atf/atf-check/Makefile b/libexec/atf/atf-check/Makefile
new file mode 100644
index 0000000..2a9851e
--- /dev/null
+++ b/libexec/atf/atf-check/Makefile
@@ -0,0 +1,49 @@
+#-
+# Copyright (c) 2011 Google, Inc.
+# 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.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+#
+# $FreeBSD$
+
+.include <src.opts.mk>
+.include <bsd.init.mk>
+
+ATF= ${.CURDIR:H:H:H}/contrib/atf
+.PATH: ${ATF}/atf-sh
+
+PROG_CXX= atf-check
+SRCS= atf-check.cpp
+MAN= atf-check.1
+
+CFLAGS+= -I${ATF}
+CFLAGS+= -DATF_SHELL='"/bin/sh"'
+
+DPADD+= ${LIBATF_CXX} ${LIBATF_C}
+LDADD+= ${LDATF_CXX} ${LDATF_C}
+USEPRIVATELIB= atf-c++ atf-c
+
+.if ${MK_TESTS} != "no"
+SUBDIR+= tests
+.endif
+
+.include <bsd.prog.mk>
diff --git a/libexec/atf/atf-check/Makefile.inc b/libexec/atf/atf-check/Makefile.inc
new file mode 100644
index 0000000..265f86d
--- /dev/null
+++ b/libexec/atf/atf-check/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+.include "../Makefile.inc"
diff --git a/libexec/atf/atf-check/tests/Makefile b/libexec/atf/atf-check/tests/Makefile
new file mode 100644
index 0000000..43a7498
--- /dev/null
+++ b/libexec/atf/atf-check/tests/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+TESTSDIR= ${TESTSBASE}/libexec/atf/atf-check
+
+ATF= ${.CURDIR:H:H:H:H}/contrib/atf
+.PATH: ${ATF}/atf-sh
+
+ATF_TESTS_SH= atf-check_test
+
+.include <bsd.test.mk>
diff --git a/libexec/atf/atf-sh/Makefile b/libexec/atf/atf-sh/Makefile
new file mode 100644
index 0000000..4e14643
--- /dev/null
+++ b/libexec/atf/atf-sh/Makefile
@@ -0,0 +1,59 @@
+#-
+# Copyright (c) 2011 Google, Inc.
+# 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.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+#
+# $FreeBSD$
+
+.include <src.opts.mk>
+.include <bsd.init.mk>
+
+ATF= ${.CURDIR:H:H:H}/contrib/atf
+.PATH: ${ATF}/atf-sh
+
+PROG_CXX= atf-sh
+SRCS= atf-sh.cpp
+MAN= atf-sh.1 atf-sh.3
+MLINKS+= atf-sh.3 atf-sh-api.3 # Backwards compatibility.
+
+CFLAGS+= -DHAVE_CONFIG_H
+CFLAGS+= -DATF_LIBEXECDIR='"${LIBEXECDIR}"'
+CFLAGS+= -DATF_PKGDATADIR='"${SHAREDIR}/atf"'
+CFLAGS+= -DATF_SHELL='"/bin/sh"'
+CFLAGS+= -I${ATF}
+
+DPADD+= ${LIBATF_C} ${LIBATF_CXX}
+LDADD+= ${LDATF_C} ${LDATF_CXX}
+USEPRIVATELIB= atf-c++ atf-c
+
+FILESGROUPS= SUBR
+
+SUBRDIR= ${SHAREDIR}/atf
+SUBR= libatf-sh.subr
+
+.if ${MK_TESTS} != "no"
+SUBDIR+= tests
+.endif
+
+.include "../../../lib/atf/common.mk"
+.include <bsd.prog.mk>
diff --git a/libexec/atf/atf-sh/tests/Makefile b/libexec/atf/atf-sh/tests/Makefile
new file mode 100644
index 0000000..3360b97
--- /dev/null
+++ b/libexec/atf/atf-sh/tests/Makefile
@@ -0,0 +1,30 @@
+# $FreeBSD$
+
+.include <bsd.init.mk>
+
+TESTSDIR= ${TESTSBASE}/libexec/atf/atf-sh
+
+ATF= ${.CURDIR:H:H:H:H}/contrib/atf
+.PATH: ${ATF}/atf-sh
+
+ATF_TESTS_SH+= atf_check_test
+ATF_TESTS_SH+= config_test
+ATF_TESTS_SH+= integration_test
+ATF_TESTS_SH+= normalize_test
+ATF_TESTS_SH+= tc_test
+ATF_TESTS_SH+= tp_test
+
+integration_test: Makefile
+ATF_TESTS_SH_SED_integration_test= \
+ -e 's,__ATF_SH__,/usr/libexec/atf-sh,g'
+
+SCRIPTS+= misc_helpers
+SCRIPTSDIR_misc_helpers=${TESTSDIR}
+CLEANFILES+= misc_helpers misc_helpers.tmp
+misc_helpers: misc_helpers.sh
+ echo '#! /usr/libexec/atf-sh' >${.TARGET}.tmp
+ cat ${.ALLSRC} >>${.TARGET}.tmp
+ chmod +x ${.TARGET}.tmp
+ mv ${.TARGET}.tmp ${.TARGET}
+
+.include <bsd.test.mk>
diff --git a/libexec/atf/tests/Makefile b/libexec/atf/tests/Makefile
new file mode 100644
index 0000000..7aa9601
--- /dev/null
+++ b/libexec/atf/tests/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/libexec/atf
+
+.PATH: ${.CURDIR:H:H:H}/tests
+KYUAFILE= yes
+
+.include <bsd.test.mk>
diff --git a/libexec/atrun/LEGAL b/libexec/atrun/LEGAL
new file mode 100644
index 0000000..af8be57
--- /dev/null
+++ b/libexec/atrun/LEGAL
@@ -0,0 +1,31 @@
+# $FreeBSD$
+
+-----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..2730559
--- /dev/null
+++ b/libexec/atrun/Makefile
@@ -0,0 +1,32 @@
+# $FreeBSD$
+
+MAINSRC=${.CURDIR}/../../usr.bin/at
+
+.include "${MAINSRC}/Makefile.inc"
+
+PROG= atrun
+SRCS= atrun.c gloadavg.c
+MAN= atrun.8
+
+BINDIR= ${ATLIB_DIR}
+CLEANFILES= ${MAN}
+
+CFLAGS+=-I${MAINSRC} -I${.CURDIR}
+CFLAGS+=-DLOGIN_CAP -DPAM
+
+WARNS?= 2
+WFORMAT=0
+
+DPADD= ${LIBPAM} ${LIBUTIL}
+LDADD= ${MINUSLPAM} -lutil
+
+atrun.8: atrun.man
+ @${ECHO} Making ${.TARGET:T} from ${.ALLSRC:T}; \
+ 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;" \
+ < ${.ALLSRC} > ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/libexec/atrun/atrun.c b/libexec/atrun/atrun.c
new file mode 100644
index 0000000..1e25766
--- /dev/null
+++ b/libexec/atrun/atrun.c
@@ -0,0 +1,589 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/* System Headers */
+
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef __FreeBSD__
+#include <sys/sysctl.h>
+#endif
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef __FreeBSD__
+#include <paths.h>
+#else
+#include <getopt.h>
+#endif
+#ifdef LOGIN_CAP
+#include <login_cap.h>
+#endif
+#ifdef PAM
+#include <security/pam_appl.h>
+#include <security/openpam.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 const char * const atrun = "atrun"; /* service name for syslog etc. */
+static int debug = 0;
+
+void perr(const char *fmt, ...);
+void perrx(const char *fmt, ...);
+static void usage(void);
+
+/* 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(void)
+{
+ 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 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[MAXLOGNAME], fmt[64];
+ 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;
+#ifdef PAM
+ pam_handle_t *pamh = NULL;
+ int pam_err;
+ struct pam_conv pamc = {
+ .conv = openpam_nullconv,
+ .appdata_ptr = NULL
+ };
+#endif
+
+ 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)
+ perrx("Userid %lu not found - aborting job %s",
+ (unsigned long) uid, filename);
+
+#ifdef PAM
+ PRIV_START
+
+ pam_err = pam_start(atrun, pentry->pw_name, &pamc, &pamh);
+ if (pam_err != PAM_SUCCESS)
+ perrx("cannot start PAM: %s", pam_strerror(pamh, pam_err));
+
+ pam_err = pam_acct_mgmt(pamh, PAM_SILENT);
+ /* Expired password shouldn't prevent the job from running. */
+ if (pam_err != PAM_SUCCESS && pam_err != PAM_NEW_AUTHTOK_REQD)
+ perrx("Account %s (userid %lu) unavailable for job %s: %s",
+ pentry->pw_name, (unsigned long)uid,
+ filename, pam_strerror(pamh, pam_err));
+
+ pam_end(pamh, pam_err);
+
+ PRIV_END
+#endif /* PAM */
+
+ PRIV_START
+
+ stream=fopen(filename, "r");
+
+ PRIV_END
+
+ if (stream == NULL)
+ perr("cannot open input file %s", filename);
+
+ 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))
+ perrx("Symbolic link encountered in job %s - aborting", filename);
+
+ 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))
+ perrx("Somebody changed files from under us for job %s - aborting",
+ filename);
+
+ if (buf.st_nlink > 1)
+ perrx("Somebody is trying to run a linked script for job %s", filename);
+
+ if ((fflags = fcntl(fd_in, F_GETFD)) <0)
+ perr("error in fcntl");
+
+ fcntl(fd_in, F_SETFD, fflags & ~FD_CLOEXEC);
+
+ snprintf(fmt, sizeof(fmt),
+ "#!/bin/sh\n# atrun uid=%%ld gid=%%ld\n# mail %%%ds %%d",
+ MAXLOGNAME - 1);
+
+ if (fscanf(stream, fmt, &nuid, &ngid, mailbuf, &send_mail) != 4)
+ perrx("File %s is in wrong format - aborting", filename);
+
+ if (mailbuf[0] == '-')
+ perrx("Illegal mail name %s in %s", mailbuf, filename);
+
+ mailname = mailbuf;
+
+ if (nuid != uid)
+ perrx("Job %s - userid %ld does not match file uid %lu",
+ filename, nuid, (unsigned long)uid);
+
+ if (ngid != gid)
+ perrx("Job %s - groupid %ld does not match file gid %lu",
+ filename, ngid, (unsigned long)gid);
+
+ fclose(stream);
+
+ if (chdir(ATSPOOL_DIR) < 0)
+ perr("cannot chdir to %s", 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 %s", ATJOB_DIR);
+
+ queue = *filename;
+
+ PRIV_START
+
+ nice(tolower(queue) - 'a');
+
+#ifdef LOGIN_CAP
+ /*
+ * For simplicity and safety, set all aspects of the user context
+ * except for a selected subset: Don't set priority, which was
+ * set based on the queue file name according to the tradition.
+ * Don't bother to set environment, including path vars, either
+ * because it will be discarded anyway. Although the job file
+ * should set umask, preset it here just in case.
+ */
+ if (setusercontext(NULL, pentry, uid, LOGIN_SETALL &
+ ~(LOGIN_SETPRIORITY | LOGIN_SETPATH | LOGIN_SETENV)) != 0)
+ exit(EXIT_FAILURE); /* setusercontext() logged the error */
+#else /* LOGIN_CAP */
+ if (initgroups(pentry->pw_name,pentry->pw_gid))
+ perr("cannot init group access list");
+
+ if (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0)
+ perr("cannot change group");
+
+ if (setlogin(pentry->pw_name))
+ perr("cannot set login name");
+
+ if (setuid(uid) < 0 || seteuid(uid) < 0)
+ perr("cannot set user id");
+#endif /* LOGIN_CAP */
+
+ if (chdir(pentry->pw_dir))
+ chdir("/");
+
+ 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
+
+#ifdef LOGIN_CAP
+ /*
+ * This time set full context to run the mailer.
+ */
+ if (setusercontext(NULL, pentry, uid, LOGIN_SETALL) != 0)
+ exit(EXIT_FAILURE); /* setusercontext() logged the error */
+#else /* LOGIN_CAP */
+ if (initgroups(pentry->pw_name,pentry->pw_gid))
+ perr("cannot init group access list");
+
+ if (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0)
+ perr("cannot change group");
+
+ if (setlogin(pentry->pw_name))
+ perr("cannot set login name");
+
+ if (setuid(uid) < 0 || seteuid(uid) < 0)
+ perr("cannot set user id");
+#endif /* LOGIN_CAP */
+
+ if (chdir(pentry->pw_dir))
+ chdir("/");
+
+#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 *fmt, ...)
+{
+ const char * const fmtadd = ": %m";
+ char nfmt[strlen(fmt) + strlen(fmtadd) + 1];
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (debug)
+ {
+ vwarn(fmt, ap);
+ }
+ else
+ {
+ snprintf(nfmt, sizeof(nfmt), "%s%s", fmt, fmtadd);
+ vsyslog(LOG_ERR, nfmt, ap);
+ }
+ va_end(ap);
+
+ exit(EXIT_FAILURE);
+}
+
+void
+perrx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (debug)
+ vwarnx(fmt, ap);
+ else
+ vsyslog(LOG_ERR, fmt, ap);
+ va_end(ap);
+
+ 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;
+#ifdef __FreeBSD__
+ size_t ncpu, ncpusz;
+ double load_avg = -1;
+#else
+ double load_avg = LOADAVG_MX;
+#endif
+
+/* 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;
+ while((c=getopt(argc, argv, "dl:"))!= -1)
+ {
+ switch (c)
+ {
+ case 'l':
+ if (sscanf(optarg, "%lf", &load_avg) != 1)
+ perr("garbled option -l");
+#ifndef __FreeBSD__
+ if (load_avg <= 0.)
+ load_avg = LOADAVG_MX;
+#endif
+ break;
+
+ case 'd':
+ debug ++;
+ break;
+
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ if (chdir(ATJOB_DIR) != 0)
+ perr("cannot change to %s", ATJOB_DIR);
+
+#ifdef __FreeBSD__
+ if (load_avg <= 0.) {
+ ncpusz = sizeof(size_t);
+ if (sysctlbyname("hw.ncpu", &ncpu, &ncpusz, NULL, 0) < 0)
+ ncpu = 1;
+ load_avg = LOADAVG_MX * ncpu;
+ }
+#endif
+
+ /* 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 %s", ATJOB_DIR);
+
+ if (flock(dirfd(spool), LOCK_EX) == -1)
+ perr("cannot lock %s", 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 %s", 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;
+ strlcpy(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);
+
+ if (flock(dirfd(spool), LOCK_UN) == -1)
+ perr("cannot unlock %s", ATJOB_DIR);
+
+ if (closedir(spool) == -1)
+ perr("cannot closedir %s", ATJOB_DIR);
+
+ closelog();
+ exit(EXIT_SUCCESS);
+}
+
+static void
+usage(void)
+{
+ if (debug)
+ fprintf(stderr, "usage: atrun [-l load_avg] [-d]\n");
+ else
+ syslog(LOG_ERR, "usage: atrun [-l load_avg] [-d]");
+
+ exit(EXIT_FAILURE);
+}
diff --git a/libexec/atrun/atrun.man b/libexec/atrun/atrun.man
new file mode 100644
index 0000000..cea322f
--- /dev/null
+++ b/libexec/atrun/atrun.man
@@ -0,0 +1,82 @@
+.\" $FreeBSD$
+.Dd October 30, 2012
+.Dt ATRUN 8
+.Os
+.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 .
+.Pp
+Root's
+.Xr crontab 5
+file
+.Pa /etc/crontab
+has to contain the line
+.Bd -literal
+*/5 * * * * root /usr/libexec/atrun
+.Ed
+.Pp
+so that
+.Nm
+gets invoked every five minutes.
+.Pp
+At every invocation,
+.Nm
+will start all the jobs in the lowercase queues whose start
+time has elapsed.
+In addition, if the load average over the last minute was less than
+the specified limit then a maximum of one batch job (denoted by the
+uppercase queues) is started.
+.Pp
+Before starting a job,
+.Nm
+will check the status of its owner's account with
+.Xr pam 3
+and refuse to run the job if the account is unavailable,
+e.g., locked out or expired.
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl l Ar load_avg
+Specify a limiting load factor, over which batch jobs should
+not be run, instead of the default of 1.5 * number of active CPUs.
+.It Fl d
+Debug; print error messages to standard error instead of using
+.Xr syslog 3 .
+.El
+.Sh WARNINGS
+For
+.Nm
+to work, you have to start up a
+.Xr cron 8
+daemon.
+.Sh FILES
+.Bl -tag -width /etc/pam.d/atrun -compact
+.It Pa /etc/pam.d/atrun
+.Xr pam.conf 5
+configuration file for
+.Nm
+.It Pa /var/at/jobs
+Directory containing job files
+.It Pa /var/at/spool
+Directory containing output spool files
+.El
+.Sh SEE ALSO
+.Xr at 1 ,
+.Xr crontab 1 ,
+.Xr pam 3 ,
+.Xr syslog 3 ,
+.Xr crontab 5 ,
+.Xr pam.conf 5 ,
+.Xr cron 8
+.Sh BUGS
+The functionality of
+.Nm
+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..f75c333
--- /dev/null
+++ b/libexec/atrun/gloadavg.c
@@ -0,0 +1,72 @@
+/*
+ * 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 lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#ifndef __FreeBSD__
+#define _POSIX_SOURCE 1
+
+/* System Headers */
+
+#include <stdio.h>
+#else
+#include <stdlib.h>
+#endif
+
+/* Local headers */
+
+#include "gloadavg.h"
+
+/* Global functions */
+
+void perr(const char *fmt, ...);
+
+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..48890ce
--- /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[] = "$FreeBSD$";
+#endif
diff --git a/libexec/bootpd/Announce b/libexec/bootpd/Announce
new file mode 100644
index 0000000..60838d5
--- /dev/null
+++ b/libexec/bootpd/Announce
@@ -0,0 +1,65 @@
+# $FreeBSD$
+
+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:
+ NetBSD-1.0 (BSD-4.4 derivative)
+ 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)
+ Linux 1.1.81
+ 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..d797ea1
--- /dev/null
+++ b/libexec/bootpd/Changes
@@ -0,0 +1,294 @@
+# $FreeBSD$
+
+Changes, most recent first
+Date, <email> Real Name
+ what...
+
+--> bootp-2.4.3
+
+03/27/96 gwr@mc.com (Gordon W. Ross)
+ Use LOG_NOTICE in place of LOG_INFO for messages related
+ to unsatisfied clients [at request of <otto@tukki.jyu.fi>]
+ Fix the irix Makefile targets, and other misc.
+
+03/25/95 gwr@mc.com (Gordon W. Ross)
+ Corrected a bug I introduced into SunOS setarp, where
+ bad IP address caused "network unreachable" errors.
+ [Thanks to andrew@ntplx.net (Andrew Lindh) for the fix!]
+
+--> bootp-2.4.2
+
+01/14/95 middelin@polyware.iaf.nl (Pauline Middelink)
+ Corrected support for the Linux networking code.
+ Fixed lots of warnings (gcc -Wall)
+ Added "linux" Makefile target.
+
+01/02/95 Jukka Ukkonen <ukkonen@csc.fi>
+ Allow bootptab syntax: ha="0:0:c0:80:e8:a7"
+
+11/30/94 Tonny van Lankveld <A.L.M.G.v.Lankveld@urc.tue.nl>
+ Fix reporting of duplicate Ethernet addresses.
+
+09/06/94 longyear@netcom.com (Al Longyear)
+ Better setarp for linux, allows non-ether types.
+
+09/02/94 Robert MacKinnon <rbm@montrouge.mis.slb.com>
+ Add support for IBM's AIX 3.2.5
+
+08/30/94 piercarl@ltd.c-d.com (Piercarlo Grandi)
+ Fix select calls on linux (modifies timeval arg).
+ Fix setarp (specify Ethernet type for now).
+
+08/27/94 drew@drewsun.FEITH.COM (Andrew B. Sudell)
+ Add support for Wollongong Win-TCP (SysVr4 variant).
+
+08/24/94 gwr@mc.com (Gordon W. Ross)
+ Use sigaction() on systems that define SA_NOCLDSTOP
+ (a symbol required by POSIX) for HP/UX and others.
+
+--> bootp-2.4.1
+
+08/24/94 gwr@mc.com (Gordon W. Ross)
+ Fix bug in boot file name generation (missing init)
+
+--> 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..6f02477
--- /dev/null
+++ b/libexec/bootpd/Makefile
@@ -0,0 +1,18 @@
+# bootpd/Makefile
+# $FreeBSD$
+
+PROG= bootpd
+CFLAGS+= -DETC_ETHERS
+CFLAGS+= -DSYSLOG -DDEBUG -DVEND_CMU
+
+WARNS?= 2
+
+SUBDIR= bootpgw tools
+
+SRCS= bootpd.c dovend.c readfile.c hash.c dumptab.c \
+ lookup.c getif.c hwaddr.c report.c tzone.c rtmsg.c
+
+MAN= bootptab.5 bootpd.8
+MLINKS= bootpd.8 bootpgw.8
+
+.include <bsd.prog.mk>
diff --git a/libexec/bootpd/Makefile.UNIX b/libexec/bootpd/Makefile.UNIX
new file mode 100644
index 0000000..5b2c186
--- /dev/null
+++ b/libexec/bootpd/Makefile.UNIX
@@ -0,0 +1,204 @@
+# $FreeBSD$
+#
+# 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)
+
+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 systems needing special treatment:
+# (Most POSIX systems should work with just "make all")
+#
+
+# 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"
+
+# IRIX 5.X (Silicon Graphics)
+irix:
+ $(MAKE) SYSDEFS= SYSLIBS=
+
+# Linux 1.1.80+ on [34]86
+linux:
+ $(MAKE) SYSDEFS="-O6 -Wall -fomit-frame-pointer"
+
+# 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"
+
+# Solaris 2.X (i.e. SunOS 5.X) with GCC. Note that GCC normally
+# defines __STDC__=1 which breaks many Solaris header files...
+sunos5gcc:
+ $(MAKE) SYSDEFS="-DSVR4 -DETC_ETHERS -D__STDC__=0" \
+ SYSLIBS="-lsocket -lnsl" CC="gcc -Wall"
+
+# UNIX System V Rel. 3
+svr3:
+ $(MAKE) SYSDEFS="-DSYSV"
+
+# UNIX System V Rel. 4
+svr4:
+ $(MAKE) SYSDEFS="-DSVR4" \
+ SYSLIBS="-lsocket -lnsl"
+
+# AT&T/GIS - Both AT&T StarServer and NCR 3000
+# may work for others using Wollongong's WIN-TCP
+wollongong gis :
+ $(MAKE) SYSDEFS="-DSVR4 -DWIN_TCP" \
+ 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/Makefile.inc b/libexec/bootpd/Makefile.inc
new file mode 100644
index 0000000..899c9b5
--- /dev/null
+++ b/libexec/bootpd/Makefile.inc
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+BINDIR?= /usr/libexec
+
+WARNS?= 1
diff --git a/libexec/bootpd/Problems b/libexec/bootpd/Problems
new file mode 100644
index 0000000..c7f9951
--- /dev/null
+++ b/libexec/bootpd/Problems
@@ -0,0 +1,66 @@
+# $FreeBSD$
+
+Common problems and ways to work around them:
+
+Bootpd complains: "bind: Address already in use" and fails to start.
+ You are already running something that has bound the
+ BOOTP listening port number. Check /etc/inetd.conf or
+ the equivalent for a bootp line (or in startup files).
+
+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)
+
+My PC clients running Sun's PC-NFS Pro v1.1 fail to receive
+acceptable responses from the bootp server.
+
+ These clients send a request with the DHCP "message length"
+ option and the (new) BOOTP "broadcast flag" both set.
+ The bootp server (on SunOS) will send a fragmented reply
+ unless you override the length with :ms=1024: (or less).
+ The "broadcast flag" is not yet supported, but there is
+ a simple work-around, just add :ra=255.255.255.255:
+ for any clients that need their reply broadcasted.
+ You may need to use a differnet broadcast address.
+ (Thanks to Ivan Auger <ivan.auger@wadsworth.org>)
+
diff --git a/libexec/bootpd/README b/libexec/bootpd/README
new file mode 100644
index 0000000..a88cca5
--- /dev/null
+++ b/libexec/bootpd/README
@@ -0,0 +1,136 @@
+# $FreeBSD$
+
+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
+ConvOldTab.sh Script to convert old (1.x) bootptab files
+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)
+getether.h header for above
+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/ToDo b/libexec/bootpd/ToDo
new file mode 100644
index 0000000..261d24c
--- /dev/null
+++ b/libexec/bootpd/ToDo
@@ -0,0 +1,61 @@
+ToDo: -*- text -*-
+
+----------------------------------------------------------------------
+Memory allocation locality:
+
+Currently mallocs memory in a very haphazard manner. As such, most of
+the program ends up core-resident all the time just to follow all the
+stupid pointers around. . . .
+
+----------------------------------------------------------------------
+Input parser:
+
+The reader implemented in readfile.c could use improvement. Some sort
+of "data-driven" parser should be used so the big switch statements
+would have only one case for each data type instead of one case for
+every recognized option symbol. Then adding a new tag would involve
+only adding a new element to the data table describing known symbols.
+Hopefully, this would shrink the code a bit too. -gwr
+
+----------------------------------------------------------------------
+SLIP Initialization via BOOTP:
+
+In the function handle_request(), both in bootpd and bootpgw,
+we might want to add code like the following just before testing
+the client IP address field for zero. (bp->bp_ciaddr == 0)
+(David suggests we leave this out for now. -gwr)
+
+#if 1 /* XXX - Experimental */
+ /*
+ * SLIP initialization support.
+ *
+ * If this packet came from a SLIP driver that does
+ * automatic IP address initialization, then the socket
+ * will have the IP address and the packet will
+ * have zeros for both the IP and HW addresses.
+ *
+ * Thanks to David P. Maynard <dpm@depend.com>
+ * for explaining how this works. -gwr
+ */
+ if ((bp->bp_ciaddr.s_addr == 0) &&
+ (bp->bp_htype == 0))
+ {
+ /* Pretend the client knows its address. It will soon. */
+ bp->bp_ciaddr = recv_addr.sin_addr;
+ if (debug)
+ report(LOG_INFO, "fixed blank request from IP addr %s",
+ inet_ntoa(recv_addr.sin_addr));
+ }
+#endif
+
+----------------------------------------------------------------------
+DHCP Support:
+
+There is a set of patches from Jeanette Pauline Middelink
+<middelin@calvin.polyware.iaf.nl> to add DHCP support.
+
+Those patches will be integrated into the BOOTP release stream
+very soon, but if you can't wait, you can get them from:
+nimbus.anu.edu.au:/pub/tridge/samba/contributed/DHCP.patch
+
+----------------------------------------------------------------------
diff --git a/libexec/bootpd/bootp.h b/libexec/bootpd/bootp.h
new file mode 100644
index 0000000..c3a3909
--- /dev/null
+++ b/libexec/bootpd/bootp.h
@@ -0,0 +1,149 @@
+/************************************************************************
+ 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.
+ *
+ * $FreeBSD$
+ *
+ *
+ * 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) */
+/* Overhead to fit a bootp message into an Ethernet packet. */
+#define BP_MSG_OVERHEAD (14 + 20 + 8) /* Ethernet + IP + UDP headers */
+
+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 */
+ u_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 */
+ u_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..d124800
--- /dev/null
+++ b/libexec/bootpd/bootpd.8
@@ -0,0 +1,310 @@
+.\" Copyright (c) 1988, 1989, 1991 Carnegie Mellon University
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 10, 2004
+.Dt BOOTPD 8
+.Os
+.Sh NAME
+.Nm bootpd , bootpgw
+.Nd Internet Boot Protocol server/gateway
+.Sh SYNOPSIS
+.Nm
+.Op Fl i | s
+.Op Fl c Ar chdir-path
+.Op Fl d Ar level
+.Op Fl h Ar hostname
+.Op Fl t Ar timeout
+.Oo
+.Ar bootptab
+.Op Ar dumpfile
+.Oc
+.Nm bootpgw
+.Op Fl i | s
+.Op Fl d Ar level
+.Op Fl h Ar hostname
+.Op Fl t Ar timeout
+.Ar server
+.Sh DESCRIPTION
+The
+.Nm
+utility
+implements an Internet Bootstrap Protocol (BOOTP) server as defined in
+RFC951, RFC1532, and RFC1533.
+The
+.Nm bootpgw
+utility 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.\&
+.Nm )
+on another subnet.
+While either
+.Nm
+or
+.Nm bootpgw
+will forward BOOTREPLY packets, only
+.Nm bootpgw
+will forward BOOTREQUEST packets.
+.Pp
+One host on each network segment is normally configured to run either
+.Nm
+or
+.Nm bootpgw
+from
+.Xr inetd 8
+by including one of the following lines in the file
+.Pa /etc/inetd.conf :
+.Pp
+.Dl bootps dgram udp wait root /usr/libexec/bootpd bootpd /etc/bootptab
+.Dl bootps dgram udp wait root /usr/libexec/bootpgw bootpgw server
+.Pp
+This mode of operation is referred to as "inetd mode" and causes
+.Nm
+(or
+.Nm 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
+.Fl t
+option controls this timeout (see OPTIONS).
+.Pp
+It is also possible to run
+.Nm
+(or
+.Nm bootpgw )
+in "standalone mode" (without
+.Xr inetd 8 )
+by simply invoking it from a shell like any other regular command.
+Standalone mode is particularly useful when
+.Nm
+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
+.Nm
+from within
+.Pa /etc/rc.local ,
+for example.)
+Standalone mode is less useful for
+.Nm bootpgw
+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
+.Fl s
+or
+.Fl i
+option may be used to force standalone or inetd mode respectively
+(see OPTIONS).
+.Sh OPTIONS
+The following options are available:
+.Bl -tag -width indent
+.It Fl t Ar timeout
+Specify the
+.Ar timeout
+value (in minutes) that a
+.Nm
+or
+.Nm bootpgw
+process will wait for a BOOTP packet before exiting.
+If no packets are received for
+.Ar timeout
+minutes, then the program will exit.
+A timeout value of zero means "run forever".
+In standalone mode, this option is forced to zero.
+.It Fl d Ar debug-level
+Set the
+.Ar debug-level
+variable that controls the amount of debugging messages generated.
+For example,
+.Fl d Ns 4
+or
+.Fl d
+4 will set the debugging level to 4.
+For compatibility with older versions of
+.Nm ,
+omitting the numeric parameter (i.e., just
+.Fl d )
+will simply increment the debug level by one.
+.It Fl c Ar chdir-path
+Set the current directory used by
+.Nm
+while checking the existence and size of client boot files.
+This is
+useful when client boot files are specified as relative pathnames, and
+.Nm
+needs to use the same current directory as the TFTP server
+(typically
+.Pa /tftpboot ) .
+This option is not recognized by
+.Nm bootpgw .
+.It Fl h Ar hostname
+Specify the hostname corresponding to the IP address to listen on.
+By default,
+.Nm
+listens on the IP address corresponding to the machine's hostname, as
+returned by
+.Xr gethostname 3 .
+.It Fl i
+Force inetd mode.
+This option is obsolete, but remains for
+compatibility with older versions of
+.Nm .
+.It Fl s
+Force standalone mode.
+This option is obsolete, but remains for
+compatibility with older versions of
+.Nm .
+.It Ar bootptab
+Specify the name of the configuration file from which
+.Nm
+loads its database of known clients and client options
+.No ( Nm
+only).
+.It Ar dumpfile
+Specify the name of the file that
+.Nm
+will dump its internal database into when it receives a
+SIGUSR1 signal
+.No ( Nm
+only).
+This option is only recognized if
+.Nm
+was compiled with the -DDEBUG flag.
+.It Ar server
+Specify the name of a BOOTP server to which
+.Nm bootpgw
+will forward all BOOTREQUEST packets it receives
+.Pf ( Nm bootpgw
+only).
+.El
+.Sh OPERATION
+Both
+.Nm
+and
+.Nm bootpgw
+operate similarly in that both listen for any packets sent to the
+.Em bootps
+port, and both simply forward any BOOTREPLY packets.
+They differ in their handling of BOOTREQUEST packets.
+.Pp
+When
+.Nm bootpgw
+is started, it determines the address of a BOOTP server
+whose name is provided as a command line parameter.
+When
+.Nm 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
+.Nm
+is started it reads a configuration file, (normally
+.Pa /etc/bootptab )
+that initializes the internal database of known clients and client
+options.
+This internal database is reloaded
+from the configuration file when
+.Nm
+receives a hangup signal (SIGHUP) or when it discovers that the
+configuration file has changed.
+.Pp
+When
+.Nm
+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,
+.Nm
+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
+.Nm
+is compiled with the -DDEBUG option, receipt of a SIGUSR1 signal causes
+it to dump its internal database to the file
+.Pa /tmp/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
+.Xr getservbyname 3
+(which normally uses
+.Pa /etc/services ) .
+Two service names (and port numbers) are used:
+.Pp
+.Dl bootps BOOTP Server listening port
+.Dl bootpc BOOTP Client destination port
+.Pp
+If the port numbers cannot be determined using
+.Xr getservbyname 3
+then the values default to bootps=67 and bootpc=68.
+.Sh FILES
+.Bl -tag -width /tmp/bootpd.dump -compact
+.It Pa /etc/bootptab
+Database file read by
+.Nm .
+.It Pa /tmp/bootpd.dump
+Debugging dump file created by
+.Nm .
+.It Pa /etc/services
+Internet service numbers.
+.It Pa /tftpboot
+Current directory typically used by the TFTP server and
+.Nm .
+.El
+.Sh "SEE ALSO"
+.Xr bootptab 5 ,
+.Xr inetd 8 ,
+.Xr tftpd 8
+.Pp
+DARPA Internet Request For Comments:
+.Bl -tag -width RFC1533 -compact
+.It RFC951
+Bootstrap Protocol
+.It RFC1532
+Clarifications and Extensions for the Bootstrap Protocol
+.It RFC1533
+DHCP Options and BOOTP Vendor Extensions
+.El
+.Sh AUTHORS
+This distribution is currently maintained by
+.An Walter L. Wimer Aq Mt walt+@cmu.edu .
+.Pp
+The original BOOTP server was created by
+.An Bill Croft
+at Stanford University in January 1986.
+.Pp
+The current version of
+.Nm
+is primarily the work of
+.An David Kovar ,
+.An Drew D. Perkins ,
+and
+.An Walter L. Wimer ,
+at Carnegie Mellon University.
+.Pp
+Enhancements and bug-fixes have been contributed by:
+.Pp
+(in alphabetical order)
+.Pp
+.An -split
+.An Danny Backx Aq Mt db@sunbim.be
+.An John Brezak Aq Mt brezak@ch.hp.com
+.An Frank da Cruz Aq Mt fdc@cc.columbia.edu
+.An David R. Linn Aq Mt drl@vuse.vanderbilt.edu
+.An Jim McKim Aq Mt mckim@lerc.nasa.gov
+.An Gordon W. Ross Aq Mt gwr@mc.com
+.An Jason Zions Aq Mt jazz@hal.com .
+.Sh BUGS
+Individual host entries must not exceed 1024 characters.
diff --git a/libexec/bootpd/bootpd.c b/libexec/bootpd/bootpd.c
new file mode 100644
index 0000000..fe9cefa
--- /dev/null
+++ b/libexec/bootpd/bootpd.c
@@ -0,0 +1,1408 @@
+/************************************************************************
+ 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.
+
+************************************************************************/
+
+/*
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#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 <sys/utsname.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 <paths.h>
+#include <syslog.h>
+#include <assert.h>
+#include <inttypes.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 "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
+ */
+
+extern void dumptab(char *);
+
+PRIVATE void catcher(int);
+PRIVATE int chk_access(char *, int32 *);
+#ifdef VEND_CMU
+PRIVATE void dovend_cmu(struct bootp *, struct host *);
+#endif
+PRIVATE void dovend_rfc1048(struct bootp *, struct host *, int32);
+PRIVATE void handle_reply(void);
+PRIVATE void handle_request(void);
+PRIVATE void sendreply(int forward, int32 dest_override);
+PRIVATE void usage(void);
+
+/*
+ * 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;
+struct in_addr my_ip_addr;
+
+static const char *hostname;
+static char default_hostname[MAXHOSTNAMELEN];
+
+/* 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.
+ */
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct timeval *timeout;
+ struct bootp *bp;
+ struct servent *servp;
+ struct hostent *hep;
+ char *stmp;
+ socklen_t ba_len, ra_len;
+ int n;
+ int nfound;
+ fd_set readfds;
+ int standalone;
+#ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */
+ struct sigaction sa;
+#endif
+
+ 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;
+
+ if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) {
+ report(LOG_ERR, "bootpd: can't get hostname\n");
+ exit(1);
+ }
+ default_hostname[sizeof(default_hostname) - 1] = '\0';
+ hostname = default_hostname;
+
+ /*
+ * 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] != '/')) {
+ report(LOG_ERR,
+ "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)) {
+ report(LOG_ERR,
+ "%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) {
+ report(LOG_ERR,
+ "bootpd: missing hostname\n");
+ break;
+ }
+ hostname = stmp;
+ 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)) {
+ report(LOG_ERR,
+ "%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:
+ report(LOG_ERR, "%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.
+ */
+
+ hep = gethostbyname(hostname);
+ if (!hep) {
+ report(LOG_ERR, "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(_PATH_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,
+ "bootps/udp: unknown service -- using 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,
+ "bootpc/udp: unknown service -- using port %d",
+ IPPORT_BOOTPC);
+ bootpc_port = (u_short) IPPORT_BOOTPC;
+ }
+
+ /*
+ * Set up signals to read or dump the table.
+ */
+#ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */
+ sa.sa_handler = catcher;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ if (sigaction(SIGHUP, &sa, NULL) < 0) {
+ report(LOG_ERR, "sigaction: %s", get_errmsg());
+ exit(1);
+ }
+ if (sigaction(SIGUSR1, &sa, NULL) < 0) {
+ report(LOG_ERR, "sigaction: %s", get_errmsg());
+ exit(1);
+ }
+#else /* SA_NOCLDSTOP */
+ /* Old-fashioned UNIX signals */
+ 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);
+ }
+#endif /* SA_NOCLDSTOP */
+
+ /*
+ * Process incoming requests.
+ */
+ FD_ZERO(&readfds);
+ for (;;) {
+ struct timeval tv;
+
+ FD_SET(s, &readfds);
+ if (timeout)
+ tv = *timeout;
+
+ nfound = select(s + 1, &readfds, NULL, NULL,
+ (timeout) ? &tv : NULL);
+ 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 (!FD_ISSET(s, &readfds)) {
+ if (debug > 1)
+ report(LOG_INFO, "exiting after %jd minutes of inactivity",
+ (intmax_t)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_NOTICE, "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;
+ }
+ }
+ return 0;
+}
+
+
+
+
+/*
+ * Print "usage" message and exit
+ */
+
+PRIVATE void
+usage()
+{
+ fprintf(stderr,
+ "usage: bootpd [-i | -s] [-c chdir-path] [-d level] [-h hostname] [-t timeout]\n");
+ fprintf(stderr, " [bootptab [dumpfile]]\n");
+ fprintf(stderr, "\t -c n\tset current directory\n");
+ fprintf(stderr, "\t -d n\tset debug level\n");
+ fprintf(stderr, "\t -h n\tset the hostname to listen on\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;
+#if !defined(SA_NOCLDSTOP) && defined(SYSV)
+ /* For older "System V" derivatives with no 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;
+
+ bp->bp_file[sizeof(bp->bp_file)-1] = '\0';
+
+ /* 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 doesn't 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 %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)
+ report(LOG_NOTICE, "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) {
+ 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) {
+ snprintf(realpath, sizeof(realpath), "%s", 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);
+
+#ifdef CHECK_FILE_ACCESS
+null_file_name:
+#endif /* CHECK_FILE_ACCESS */
+
+
+ /*
+ * 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, haf;
+
+ /*
+ * 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 compatibility)
+ * 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;
+ haf = (int) bp->bp_htype;
+ if (haf == 0)
+ haf = HTYPE_ETHERNET;
+
+ if (debug > 1)
+ report(LOG_INFO, "setarp %s - %s",
+ inet_ntoa(dst), haddrtoa(ha, len));
+ setarp(s, &dst, haf, 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 publicly 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;
+
+ static const 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) + BP_MSG_OVERHEAD) {
+ if (debug > 1)
+ report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
+ pktlen = msgsz - BP_MSG_OVERHEAD;
+ }
+ }
+ }
+
+ 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..86c1b3d
--- /dev/null
+++ b/libexec/bootpd/bootpd.h
@@ -0,0 +1,213 @@
+/************************************************************************
+ 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.
+ *
+ * $FreeBSD$
+ */
+
+#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;
+ u_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/bootpgw/Makefile b/libexec/bootpd/bootpgw/Makefile
new file mode 100644
index 0000000..b7adadf
--- /dev/null
+++ b/libexec/bootpd/bootpgw/Makefile
@@ -0,0 +1,12 @@
+# Makefile
+# $FreeBSD$
+
+PROG= bootpgw
+MAN=
+SRCS= bootpgw.c getif.c hwaddr.c report.c rtmsg.c
+
+SRCDIR= ${.CURDIR}/..
+CFLAGS+=-I${SRCDIR}
+.PATH: ${SRCDIR}
+
+.include <bsd.prog.mk>
diff --git a/libexec/bootpd/bootpgw/bootpgw.c b/libexec/bootpd/bootpgw/bootpgw.c
new file mode 100644
index 0000000..16bb66b
--- /dev/null
+++ b/libexec/bootpd/bootpgw/bootpgw.c
@@ -0,0 +1,677 @@
+/*
+ * 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.
+************************************************************************/
+
+/*
+ * BOOTPGW is typically used to forward BOOTP client requests from
+ * one subnet to a BOOTP server on a different subnet.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#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 <sys/utsname.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+
+#ifndef NO_UNISTD
+#include <unistd.h>
+#endif
+
+#include <err.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <paths.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
+ */
+
+static void usage(void);
+static void handle_reply(void);
+static void handle_request(void);
+
+/*
+ * 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_char 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. */
+
+struct in_addr my_ip_addr;
+
+struct utsname my_uname;
+char *hostname;
+
+
+
+
+
+/*
+ * Initialization such as command-line processing is done and then the
+ * main server loop is started.
+ */
+
+int
+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;
+
+ if (uname(&my_uname) < 0)
+ errx(1, "can't get hostname");
+ hostname = my_uname.nodename;
+
+ hep = gethostbyname(hostname);
+ 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)) {
+ warnx("invalid debug level");
+ 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))
+ {
+ warnx("invalid hop count limit");
+ break;
+ }
+ maxhops = (u_char)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)) {
+ warnx("invalid timeout specification");
+ 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))
+ {
+ warnx("invalid wait time");
+ break;
+ }
+ minwait = (u_int)n;
+ break;
+
+ default:
+ warnx("unknown switch: -%c", argv[0][1]);
+ usage();
+ break;
+
+ } /* switch */
+ } /* for args */
+
+ /* Make sure server name argument is suplied. */
+ servername = argv[0];
+ if (!servername) {
+ warnx("missing server name");
+ usage();
+ }
+ /*
+ * Get address of real bootp server.
+ */
+ if (isdigit(servername[0]))
+ server_ipa = inet_addr(servername);
+ else {
+ hep = gethostbyname(servername);
+ if (!hep)
+ errx(1, "can't get addr for %s", servername);
+ 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(_PATH_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,
+ "bootps/udp: unknown service -- using 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,
+ "bootpc/udp: unknown service -- using port %d",
+ IPPORT_BOOTPC);
+ bootpc_port = (u_short) IPPORT_BOOTPC;
+ }
+
+ /* no signal catchers */
+
+ /*
+ * Process incoming requests.
+ */
+ for (;;) {
+ struct timeval tv;
+
+ readfds = 1 << s;
+ if (timeout)
+ tv = *timeout;
+
+ nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL,
+ (timeout) ? &tv : NULL);
+ 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",
+ (long)(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;
+ }
+ }
+ return 0;
+}
+
+
+
+
+/*
+ * 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;
+ u_short secs;
+ u_char 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 = bp->bp_hops;
+ if (++hops > maxhops) {
+ report(LOG_NOTICE, "request from %s reached hop limit",
+ inet_ntoa(recv_addr.sin_addr));
+ return;
+ }
+ bp->bp_hops = 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;
+ struct ifreq *ifr;
+ /*
+ * 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;
+ unsigned char *ha;
+ int len, haf;
+
+ 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;
+ haf = (int) bp->bp_htype;
+ if (haf == 0)
+ haf = HTYPE_ETHERNET;
+
+ if (debug > 1)
+ report(LOG_INFO, "setarp %s - %s",
+ inet_ntoa(bp->bp_yiaddr), haddrtoa(ha, len));
+ setarp(s, &bp->bp_yiaddr, haf, ha, len);
+
+ /* Send reply with same size packet as request used. */
+ if (sendto(s, pktbuf, pktlen, 0,
+ (struct sockaddr *) &send_addr,
+ sizeof(send_addr)) < 0)
+ {
+ report(LOG_ERR, "sendto: %s", get_network_errmsg());
+ }
+}
+
+/*
+ * Local Variables:
+ * tab-width: 4
+ * c-indent-level: 4
+ * c-argdecl-indent: 4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: -4
+ * c-label-offset: -4
+ * c-brace-offset: 0
+ * End:
+ */
diff --git a/libexec/bootpd/bootptab.5 b/libexec/bootpd/bootptab.5
new file mode 100644
index 0000000..0ba2b69
--- /dev/null
+++ b/libexec/bootpd/bootptab.5
@@ -0,0 +1,430 @@
+.\" Copyright (c) 1988, 1989, 1991 Carnegie Mellon University
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 31, 1991
+.Dt BOOTPTAB 5
+.Os
+.Sh NAME
+.Nm bootptab
+.Nd Internet Bootstrap Protocol server database
+.Sh DESCRIPTION
+The
+.Nm
+file is the configuration database file for
+.Xr bootpd 8 ,
+the Internet Bootstrap Protocol server.
+Its format is similar to that of
+.Xr 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
+.Dl "hostname:tg=value. . . :tg=value. . . :tg=value. . . ."
+.Pp
+where
+.Em hostname
+is the actual name of a bootp client (or a "dummy entry"), and
+.Em 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
+.Em 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.\&
+.Em :tg: ) .
+The currently recognized tags are:
+.Pp
+.Bl -tag -width xxx -compact
+.It bf
+Bootfile
+.It bs
+Bootfile size in 512-octet blocks
+.It cs
+Cookie server address list
+.It df
+Merit dump file
+.It dn
+Domain name
+.It ds
+Domain name server address list
+.It ef
+Extension file
+.It gw
+Gateway address list
+.It ha
+Host hardware address
+.It hd
+Bootfile home directory
+.It hn
+Send client's hostname to client
+.It ht
+Host hardware type (see Assigned Numbers RFC)
+.It im
+Impress server address list
+.It ip
+Host IP address
+.It lg
+Log server address list
+.It lp
+LPR server address list
+.It ns
+IEN-116 name server address list
+.It nt
+NTP (time) Server (RFC 1129)
+.It ra
+Reply address override
+.It rl
+Resource location protocol server address list
+.It rp
+Root path to mount as root
+.It sa
+TFTP server address client should use
+.It sm
+Host subnet mask
+.It sw
+Swap server address
+.It tc
+Table continuation (points to similar "template" host entry)
+.It td
+TFTP root directory used by "secure" TFTP servers
+.It to
+Time offset in seconds from UTC
+.It ts
+Time server address list
+.It vm
+Vendor magic cookie selector
+.It yd
+YP (NIS) domain name
+.It ys
+YP (NIS) server address
+.El
+.Pp
+There is also a generic tag,
+.Pf T Em n ,
+where
+.Em 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
+.Nm bootpd
+first.
+Generic data may be represented as either a stream of hexadecimal
+numbers or as a quoted string of
+.Tn 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:
+.Em cs ,
+.Em ds ,
+.Em gw ,
+.Em im ,
+.Em lg ,
+.Em lp ,
+.Em ns ,
+.Em nt ,
+.Em ra ,
+.Em rl ,
+and
+.Em ts .
+The
+.Em ip ,
+.Em sa ,
+.Em sw ,
+.Em sm ,
+and
+.Em 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
+.Nm bootpd
+to lookup the IP address for that host name using
+.Xr gethostbyname 3 .
+If the
+.Em ip
+tag is not specified,
+.Nm 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
+.Em ht
+tag specifies the hardware type code as either an unsigned decimal, octal, or
+hexadecimal integer or one of the following symbolic names:
+.Em ethernet
+or
+.Em ether
+for 10Mb Ethernet,
+.Em ethernet3
+or
+.Em ether3
+for 3Mb experimental Ethernet,
+.Em ieee802 ,
+.Em tr ,
+or
+.Em token-ring
+for IEEE 802 networks,
+.Em pronet
+for Proteon ProNET Token Ring, or
+.Em chaos ,
+.Em arcnet ,
+or
+.Em ax.25
+for Chaos, ARCNET, and AX.25 Amateur Radio networks, respectively.
+The
+.Em ha
+tag takes a hardware address which may be specified as a host name
+or in numeric form.
+Note that the numeric form
+.Em must
+be specified in hexadecimal; optional periods and/or a leading '0x' may be
+included for readability.
+The
+.Em ha
+tag must be preceded by the
+.Em ht
+tag (either explicitly or implicitly; see
+.Em tc
+below).
+If the hardware address is not specified and the type is specified
+as either "ethernet" or "ieee802", then
+.Nm bootpd
+will try to determine the hardware address using
+.Xr ether_hostton 3 .
+.Pp
+The hostname, home directory, and bootfile are
+.Tn ASCII
+strings which may be
+optionally surrounded by double quotes (").
+The client's request and the
+values of the
+.Em hd
+and
+.Em 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
+.Em bf
+option is specified its value is copied into the reply packet.
+If the
+.Em 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
+.Em bs Ns =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
+.Xr tftpd 8
+provide a security feature to change their root directory using
+the
+.Xr chroot 2
+system call.
+The
+.Em td
+tag may be used to inform
+.Nm bootpd
+of this special root directory used by
+.Nm tftpd .
+(One may alternatively use the
+.Nm bootpd
+.Fl c Ar chdir
+option.)
+The
+.Em hd
+tag is actually relative to the root directory specified by the
+.Em td
+tag.
+For example, if the real absolute path to your BOOTP client bootfile is
+.Pa /tftpboot/bootfiles/bootimage ,
+and
+.Nm tftpd
+uses
+.Pa /tftpboot
+as its "secure" directory, then specify the following in
+.Pa bootptab :
+.Pp
+.Dl :td=/tftpboot:hd=/bootfiles:bf=bootimage:
+.Pp
+If your bootfiles are located directly in
+.Pa /tftpboot ,
+use:
+.Pp
+.Dl :td=/tftpboot:hd=/:bf=bootimage:
+.Pp
+The
+.Em 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,
+.Nm bootpd
+will tell the client to perform TFTP to the same machine
+.Nm bootpd
+is running on.
+.Pp
+The time offset
+.Em to
+may be either a signed decimal integer specifying the client's
+time zone offset in seconds from UTC, or the keyword
+.Em auto
+which uses the server's time zone offset.
+Specifying the
+.Em to
+symbol as a boolean has the same effect as specifying
+.Em auto
+as its value.
+.Pp
+The bootfile size
+.Em bs
+may be either a decimal, octal, or hexadecimal integer specifying the size of
+the bootfile in 512-octet blocks, or the keyword
+.Em auto
+which causes the server to automatically calculate the bootfile size at each
+request.
+As with the time offset, specifying the
+.Em bs
+symbol as a boolean has the same effect as specifying
+.Em auto
+as its value.
+.Pp
+The vendor magic cookie selector (the
+.Em vm
+tag) may take one of the following keywords:
+.Em auto
+(indicating that vendor information is determined by the client's request),
+.Em rfc1048
+or
+.Em rfc1084
+(which always forces an RFC1084-style reply), or
+.Em cmu
+(which always forces a CMU-style reply).
+.Pp
+The
+.Em hn
+tag is strictly a boolean tag; it does not take the usual equals-sign and
+value.
+Its presence indicates that the hostname should be sent to RFC1084
+clients.
+.Nm 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
+.Em tc
+(table continuation) mechanism.
+Often, the template entry is a dummy host which does not actually exist and
+never sends bootp requests.
+This feature is similar to the
+.Em tc
+feature of
+.Xr termcap 5
+for similar terminals.
+Note that
+.Nm bootpd
+allows the
+.Em tc
+tag symbol to appear anywhere in the host entry, unlike
+.Pa termcap
+which requires it to be the last tag.
+Information explicitly specified for a
+host always overrides information implied by a
+.Em tc
+tag symbol, regardless of its location within the entry.
+The
+value of the
+.Em 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
+.Em tc .
+This can be done using the construction
+.Em tag Ns @
+which removes the effect of
+.Em tag
+as in
+.Xr termcap 5 .
+For example, to completely undo an IEN-116 name server specification, use
+.Em :ns@:
+at an appropriate place in the configuration entry.
+After removal
+with
+.Em @ ,
+a tag is eligible to be set again through the
+.Em 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
+.Pa /etc/bootptab
+file follows:
+.Bd -literal -offset indent
+# 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:
+.Ed
+.Sh FILES
+.Bl -tag -width /etc/bootptab -compact
+.It Pa /etc/bootptab
+.El
+.Sh "SEE ALSO"
+.Xr bootpd 8 ,
+.Xr tftpd 8
+.Pp
+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..9bce37e
--- /dev/null
+++ b/libexec/bootpd/bootptab.mcs
@@ -0,0 +1,91 @@
+# /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.
+#
+# $FreeBSD$
+#
+# 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
+bach: tc=.subnet16:ha="08:00:20:04:98:8d":bf=boot.sun4m:
+xanadu:tc=.subnet17:ha="00:80:42:42:04:c7":bf=boot.sun4c:
diff --git a/libexec/bootpd/bptypes.h b/libexec/bootpd/bptypes.h
new file mode 100644
index 0000000..e27cc83
--- /dev/null
+++ b/libexec/bootpd/bptypes.h
@@ -0,0 +1,23 @@
+/* $FreeBSD$
+ */
+
+#ifndef BPTYPES_H
+#define BPTYPES_H
+
+#include <sys/types.h>
+
+/*
+ * 32 bit integers are different types on various architectures
+ */
+
+#define int32 int32_t
+#define u_int32 u_int32_t
+
+/*
+ * 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..3b2bd97
--- /dev/null
+++ b/libexec/bootpd/dovend.c
@@ -0,0 +1,408 @@
+/*
+ * dovend.c : Inserts all but the first few vendor options.
+ *
+ * $FreeBSD$
+ */
+
+#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"
+
+PRIVATE int insert_generic(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;
+
+ static const 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).
+ */
+ char *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..ef3b62d
--- /dev/null
+++ b/libexec/bootpd/dovend.h
@@ -0,0 +1,6 @@
+/* dovend.h */
+/* $FreeBSD$ */
+
+extern int dovend_rfc1497(struct host *hp, u_char *buf, int len);
+extern int insert_ip(int, struct in_addr_list *, u_char **, int *);
+extern void insert_u_long(u_int32, u_char **);
diff --git a/libexec/bootpd/dumptab.c b/libexec/bootpd/dumptab.c
new file mode 100644
index 0000000..43e94ec
--- /dev/null
+++ b/libexec/bootpd/dumptab.c
@@ -0,0 +1,378 @@
+/*
+ * dumptab.c - handles dumping the database
+ *
+ * $FreeBSD$
+ */
+
+#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 DEBUG
+static void dump_generic(FILE *, struct shared_bindata *);
+static void dump_host(FILE *, struct host *);
+static void list_ipaddresses(FILE *, struct in_addr_list *);
+#endif
+
+#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;
+ time_t 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, "%lu:", (u_long)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=%lu:", (u_long)hp->msg_size);
+ }
+ if (hp->flags.min_wait) {
+ fprintf(fp, "\\\n\t:mw=%lu:", (u_long)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:", (long)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..e4dc1b6
--- /dev/null
+++ b/libexec/bootpd/getether.c
@@ -0,0 +1,389 @@
+/*
+ * 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>
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#ifndef NO_UNISTD
+#include <unistd.h>
+#endif
+
+#include <ctype.h>
+#include <paths.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "getether.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));
+ strlcpy(&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 <sys/time.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+
+int
+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];
+ 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>
+#include <string.h>
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+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 */
+
+ snprintf(devname, sizeof(devname), "%s%s", _PATH_DEV, 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 a 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> and
+ * updated by Pauline Middelink <middelin@polyware.iaf.nl>
+ *
+ * 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 <memory.h>
+#include <sys/ioctl.h>
+#include <net/if.h> /* struct ifreq */
+#include <sys/socketio.h> /* Needed for IOCTL defs */
+
+int
+getether(ifname, eap)
+ char *ifname, *eap;
+{
+ int rc = -1;
+ int fd;
+ struct ifreq phys;
+
+ memset(&phys, 0, 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 {
+ memcpy(eap, &phys.ifr_hwaddr.sa_data, 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
+int
+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/getether.h b/libexec/bootpd/getether.h
new file mode 100644
index 0000000..026df15
--- /dev/null
+++ b/libexec/bootpd/getether.h
@@ -0,0 +1,4 @@
+/* getether.h */
+/* $FreeBSD$ */
+
+extern int getether(char *ifname, char *eaptr);
diff --git a/libexec/bootpd/getif.c b/libexec/bootpd/getif.c
new file mode 100644
index 0000000..64e5eda
--- /dev/null
+++ b/libexec/bootpd/getif.c
@@ -0,0 +1,147 @@
+/*
+ * getif.c : get an interface structure
+ *
+ * $FreeBSD$
+ */
+
+#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 <sys/time.h> /* for struct timeval in net/if.h */
+#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((u_char *)addrp, (u_char *)&(sip->sin_addr));
+ if (m > maxmatch) {
+ maxmatch = m;
+ ifrmax = ifrq;
+ }
+#ifndef IFNAMSIZ
+ /* BSD not defined or earlier than 4.3 */
+ incr = sizeof(*ifrq);
+#else
+ incr = ifrq->ifr_addr.sa_len + IFNAMSIZ;
+#endif
+
+ 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..a2c86cf
--- /dev/null
+++ b/libexec/bootpd/getif.h
@@ -0,0 +1,4 @@
+/* getif.h */
+/* $FreeBSD$ */
+
+extern struct ifreq *getif(int, struct in_addr *);
diff --git a/libexec/bootpd/hash.c b/libexec/bootpd/hash.c
new file mode 100644
index 0000000..146e87e
--- /dev/null
+++ b/libexec/bootpd/hash.c
@@ -0,0 +1,416 @@
+/************************************************************************
+ 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.
+
+ $FreeBSD$
+
+************************************************************************/
+
+/*
+ * 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
+
+PRIVATE void hashi_FreeMembers(hash_member *, hash_freefp);
+
+
+
+
+/*
+ * 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..969703d
--- /dev/null
+++ b/libexec/bootpd/hash.h
@@ -0,0 +1,148 @@
+#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
+ * $FreeBSD$
+ *
+ * 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
+ * guaranteed to use the best hash algorithm in existence. . . .
+ */
+
+
+
+/*
+ * Various hash table definitions
+ */
+
+
+/*
+ * Define "hash_datum" as a universal data type
+ */
+typedef void hash_datum;
+
+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? */
+
+typedef int (*hash_cmpfp)(hash_datum *, hash_datum *);
+typedef void (*hash_freefp)(hash_datum *);
+
+extern hash_tbl *hash_Init(u_int tablesize);
+
+extern void hash_Reset(hash_tbl *tbl, hash_freefp);
+
+extern unsigned hash_HashFunction(u_char *str, u_int len);
+
+extern int hash_Exists(hash_tbl *, u_int code,
+ hash_cmpfp, hash_datum *key);
+
+extern int hash_Insert(hash_tbl *, u_int code,
+ hash_cmpfp, hash_datum *key,
+ hash_datum *element);
+
+extern int hash_Delete(hash_tbl *, u_int code,
+ hash_cmpfp, hash_datum *key,
+ hash_freefp);
+
+extern hash_datum *hash_Lookup(hash_tbl *, u_int code,
+ hash_cmpfp, hash_datum *key);
+
+extern hash_datum *hash_FirstEntry(hash_tbl *);
+
+extern hash_datum *hash_NextEntry(hash_tbl *);
+
+#endif /* HASH_H */
diff --git a/libexec/bootpd/hwaddr.c b/libexec/bootpd/hwaddr.c
new file mode 100644
index 0000000..de264cc
--- /dev/null
+++ b/libexec/bootpd/hwaddr.c
@@ -0,0 +1,352 @@
+/*
+ * hwaddr.c - routines that deal with hardware addresses.
+ * (i.e. Ethernet)
+ *
+ * $FreeBSD$
+ */
+
+#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
+
+#ifdef _AIX32
+#include <sys/time.h> /* for struct timeval in net/if.h */
+#include <net/if.h> /* for struct ifnet in net/if_arp.h */
+#endif
+
+#include <net/if_arp.h>
+#include <netinet/in.h>
+
+#ifdef WIN_TCP
+#include <netinet/if_ether.h>
+#include <sys/dlpi.h>
+#endif
+
+#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
+
+#ifndef ATF_INUSE /* Not defined on some systems (i.e. Linux) */
+#define ATF_INUSE 0
+#endif
+
+/* For BSD 4.4, set arp entry by writing to routing socket */
+#if defined(BSD)
+#if BSD >= 199306
+extern int bsd_arp_set(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, hafamily, haddr, halen)
+ int s; /* socket fd */
+ struct in_addr *ia; /* protocol address */
+ int hafamily; /* HW address family */
+ u_char *haddr; /* HW address data */
+ int halen;
+{
+#ifdef SIOCSARP
+#ifdef WIN_TCP
+ /* This is an SVR4 with different networking code from
+ * Wollongong WIN-TCP. Not quite like the Lachman code.
+ * Code from: drew@drewsun.FEITH.COM (Andrew B. Sudell)
+ */
+#undef SIOCSARP
+#define SIOCSARP ARP_ADD
+ struct arptab arpreq; /* Arp table entry */
+
+ bzero((caddr_t) &arpreq, sizeof(arpreq));
+ arpreq.at_flags = ATF_COM;
+
+ /* Set up IP address */
+ arpreq.at_in = ia->s_addr;
+
+ /* Set up Hardware Address */
+ bcopy(haddr, arpreq.at_enaddr, halen);
+
+ /* Set the Date Link type. */
+ /* XXX - Translate (hafamily) to dltype somehow? */
+ arpreq.at_dltype = DL_ETHER;
+
+#else /* WIN_TCP */
+ /* Good old Berkeley way. */
+ struct arpreq arpreq; /* Arp request ioctl block */
+ struct sockaddr_in *si;
+ char *p;
+
+ 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. */
+#ifdef __linux__ /* XXX - Do others need this? -gwr */
+ /*
+ * Linux requires the sa_family field set.
+ * longyear@netcom.com (Al Longyear)
+ */
+ arpreq.arp_ha.sa_family = hafamily;
+#endif /* linux */
+
+ /* This variable is just to help catch type mismatches. */
+ p = arpreq.arp_ha.sa_data;
+ bcopy(haddr, p, halen);
+#endif /* WIN_TCP */
+
+#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>,
+ */
+ {
+ int fd;
+ struct strioctl iocb;
+
+ 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, haddr, halen);
+#else
+ /*
+ * Oh well, SIOCSARP is not defined. Just run arp(8).
+ * Need to delete partial entry first on some systems.
+ * XXX - Gag!
+ */
+ int status;
+ char buf[256];
+ char *a;
+ extern char *inet_ntoa();
+
+ a = inet_ntoa(*ia);
+ snprintf(buf, sizeof(buf), "arp -d %s; arp -s %s %s temp",
+ a, a, haddrtoa(haddr, halen));
+ if (debug > 2)
+ report(LOG_INFO, "%s", 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..bd68857
--- /dev/null
+++ b/libexec/bootpd/hwaddr.h
@@ -0,0 +1,36 @@
+/*
+ * hwaddr.h
+ *
+ * $FreeBSD$
+ */
+
+#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;
+
+extern void setarp(int, struct in_addr *, int, u_char *, int);
+extern char *haddrtoa(u_char *, int);
+extern void haddr_conv802(u_char *, u_char *, int);
+
+/*
+ * 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..54b3f62
--- /dev/null
+++ b/libexec/bootpd/lookup.c
@@ -0,0 +1,129 @@
+/*
+ * lookup.c - Lookup IP address, HW address, netmask
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <sys/time.h> /* for struct timeval in net/if.h */
+#include <net/if.h>
+#include <netinet/in.h>
+
+#ifdef ETC_ETHERS
+#include <net/ethernet.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..4940f05
--- /dev/null
+++ b/libexec/bootpd/lookup.h
@@ -0,0 +1,8 @@
+/* lookup.h */
+/* $FreeBSD$ */
+
+#include "bptypes.h" /* for int32, u_int32 */
+
+extern u_char *lookup_hwa(char *hostname, int htype);
+extern int lookup_ipa(char *hostname, u_int32 *addr);
+extern int lookup_netmask(u_int32 addr, u_int32 *mask);
diff --git a/libexec/bootpd/patchlevel.h b/libexec/bootpd/patchlevel.h
new file mode 100644
index 0000000..fc79f18
--- /dev/null
+++ b/libexec/bootpd/patchlevel.h
@@ -0,0 +1,8 @@
+/*
+ * patchlevel.h
+ *
+ * $FreeBSD$
+ */
+
+#define VERSION "2.4"
+#define PATCHLEVEL 3
diff --git a/libexec/bootpd/readfile.c b/libexec/bootpd/readfile.c
new file mode 100644
index 0000000..ab583d4
--- /dev/null
+++ b/libexec/bootpd/readfile.c
@@ -0,0 +1,2084 @@
+/************************************************************************
+ 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.
+
+ $FreeBSD$
+
+************************************************************************/
+
+/*
+ * 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.
+ */
+
+extern boolean iplookcmp();
+boolean nmcmp(hash_datum *, hash_datum *);
+
+PRIVATE void
+ adjust(char **);
+PRIVATE void
+ del_string(struct shared_string *);
+PRIVATE void
+ del_bindata(struct shared_bindata *);
+PRIVATE void
+ del_iplist(struct in_addr_list *);
+PRIVATE void
+ eat_whitespace(char **);
+PRIVATE int
+ eval_symbol(char **, struct host *);
+PRIVATE void
+ fill_defaults(struct host *, char **);
+PRIVATE void
+ free_host(hash_datum *);
+PRIVATE struct in_addr_list *
+ get_addresses(char **);
+PRIVATE struct shared_string *
+ get_shared_string(char **);
+PRIVATE char *
+ get_string(char **, char *, u_int *);
+PRIVATE u_int32
+ get_u_long(char **);
+PRIVATE boolean
+ goodname(char *);
+PRIVATE boolean
+ hwinscmp(hash_datum *, hash_datum *);
+PRIVATE int
+ interp_byte(char **, byte *);
+PRIVATE void
+ makelower(char *);
+PRIVATE boolean
+ nullcmp(hash_datum *, hash_datum *);
+PRIVATE int
+ process_entry(struct host *, char *);
+PRIVATE int
+ process_generic(char **, struct shared_bindata **, u_int);
+PRIVATE byte *
+ prs_haddr(char **, u_int);
+PRIVATE int
+ prs_inetaddr(char **, u_int32 *);
+PRIVATE void
+ read_entry(FILE *, char *, u_int *);
+PRIVATE char *
+ smalloc(u_int);
+
+
+/*
+ * 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, haddrlength(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 preceded by a backslash cause
+ * line-continuation onto the next line. The entry is terminated by a
+ * newline character which is not preceded 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. . . .*/
+ }
+ /* FALLTHROUGH */
+ 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 */
+ }
+ }
+ /* FALLTHROUGH 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 '/')";
+ break;
+ case E_BAD_VALUE:
+ msg = "bad value";
+ break;
+ default:
+ msg = "unknown 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 unsigned integer value for MEMBER */
+#define PARSE_UINT(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 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);
+ snprintf(current_tagname, sizeof(current_tagname),
+ "T%d", (int)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", (int*)&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_UINT(msg_size);
+ if (hp->msg_size < BP_MINPKTSZ ||
+ hp->msg_size > MAX_MSG_SIZE)
+ return E_BAD_VALUE;
+ break;
+
+ case SYM_MIN_WAIT:
+ PARSE_UINT(min_wait);
+ 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;
+
+ /* 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;
+ }
+
+ /*
+ * 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 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. */
+ }
+
+ hap = haddr;
+ while (hap < haddr + hal) {
+ if ((*p == '.') || (*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..be9cada
--- /dev/null
+++ b/libexec/bootpd/readfile.h
@@ -0,0 +1,11 @@
+/* readfile.h */
+/* $FreeBSD$ */
+
+#include "bptypes.h"
+#include "hash.h"
+
+extern boolean hwlookcmp(hash_datum *, hash_datum *);
+extern boolean iplookcmp(hash_datum *, hash_datum *);
+extern boolean nmcmp(hash_datum *, hash_datum *);
+extern void readtab(int);
+extern void rdtab_init(void);
diff --git a/libexec/bootpd/report.c b/libexec/bootpd/report.c
new file mode 100644
index 0000000..290e19e
--- /dev/null
+++ b/libexec/bootpd/report.c
@@ -0,0 +1,138 @@
+/* $FreeBSD$ */
+
+/*
+ * report() - calls syslog
+ */
+
+#include <stdarg.h>
+
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.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.
+ */
+void
+report(int priority, const char *fmt,...)
+{
+ va_list ap;
+ static char buf[128];
+
+ if ((priority < 0) || (priority >= numlevels)) {
+ priority = numlevels - 1;
+ }
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(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.
+ */
+const char *
+get_errmsg()
+{
+ 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..55cec5c
--- /dev/null
+++ b/libexec/bootpd/report.h
@@ -0,0 +1,6 @@
+/* report.h */
+/* $FreeBSD$ */
+
+extern void report_init(int nolog);
+extern void report(int, const char *, ...) __printflike(2, 3);
+extern const char *get_errmsg(void);
diff --git a/libexec/bootpd/rtmsg.c b/libexec/bootpd/rtmsg.c
new file mode 100644
index 0000000..09886fd
--- /dev/null
+++ b/libexec/bootpd/rtmsg.c
@@ -0,0 +1,239 @@
+/*
+ * 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. 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
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#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 <sys/time.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 <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "report.h"
+
+
+static int rtmsg(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_in 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, 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_in *sin = &sin_m;
+ register struct sockaddr_dl *sdl;
+ register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
+ u_char *ea;
+ struct timespec tp;
+ 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 = expire_time = 0;
+
+ /* make arp entry temporary */
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ expire_time = tp.tv_sec + 20 * 60;
+
+tryagain:
+ if (rtmsg(RTM_GET) < 0) {
+ report(LOG_WARNING, "rtmget: %s", strerror(errno));
+ return (1);
+ }
+ sin = (struct sockaddr_in *)(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_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);
+ }
+ 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 | RTF_LLDATA);
+ if (doing_proxy) {
+ 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..e905bff
--- /dev/null
+++ b/libexec/bootpd/tools/Makefile
@@ -0,0 +1,6 @@
+# Makefile
+# $FreeBSD$
+
+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..f4a306f
--- /dev/null
+++ b/libexec/bootpd/tools/Makefile.inc
@@ -0,0 +1,6 @@
+# Makefile.inc
+# $FreeBSD$
+
+BINDIR= /usr/sbin
+
+WARNS?= 1
diff --git a/libexec/bootpd/tools/bootpef/Makefile b/libexec/bootpd/tools/bootpef/Makefile
new file mode 100644
index 0000000..9436561
--- /dev/null
+++ b/libexec/bootpd/tools/bootpef/Makefile
@@ -0,0 +1,13 @@
+# Makefile
+# $FreeBSD$
+
+PROG= bootpef
+MAN= 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..841b01c
--- /dev/null
+++ b/libexec/bootpd/tools/bootpef/bootpef.8
@@ -0,0 +1,64 @@
+.\" $FreeBSD$
+.\"
+.\" bootpef.8
+.Dd December 4, 1993
+.Dt BOOTPEF 8
+.Os
+.Sh NAME
+.Nm bootpef
+.Nd "BOOTP Extension File compiler"
+.Sh SYNOPSIS
+.Bk -words
+.Nm
+.Op Fl c Ar chdir\-path
+.Op Fl d Ar debug\-level
+.Op Fl f Ar config\-file
+.Op Ar client\-name ...
+.Ek
+.Sh DESCRIPTION
+The
+.Nm
+utility builds the
+.Em "Extension Path"
+files described by
+.%T "RFC 1497"
+(tag 18).
+If any
+.Ar client\-name
+arguments are specified, then
+.Nm
+compiles the extension files for only those clients.
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl c Ar chdir\-path
+Sets the current directory used by
+.Nm
+while creating extension files.
+This is useful when the
+extension file names are specified as relative pathnames, and
+.Nm
+needs to use the same current directory as the TFTP server
+(typically
+.Pa /tftpboot ) .
+.It Fl d Ar debug\-level
+Sets the
+.Ar debug\-level
+variable that controls the amount of debugging messages generated.
+For example,
+.Fl d Ar 4
+will set the debugging level to 4.
+.It Fl f Ar config\-file
+Set the name of the config file that specifies the option
+data to be sent to each client.
+.El
+.Sh SEE ALSO
+.Xr bootpd 8 ,
+.Xr tftpd 8
+.Rs
+.%O RFC951
+.%T "BOOTSTRAP PROTOCOL (BOOTP)"
+.Re
+.Rs
+.%O RFC1497
+.%T "BOOTP Vendor Information Extensions"
+.Re
diff --git a/libexec/bootpd/tools/bootpef/bootpef.c b/libexec/bootpd/tools/bootpef/bootpef.c
new file mode 100644
index 0000000..04089c8
--- /dev/null
+++ b/libexec/bootpd/tools/bootpef/bootpef.c
@@ -0,0 +1,331 @@
+/************************************************************************
+ 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.
+
+ $FreeBSD$
+
+************************************************************************/
+
+/*
+ * 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
+ */
+
+
+
+#include <stdarg.h>
+
+#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
+ */
+
+static void mktagfile(struct host *);
+static void usage(void);
+
+/*
+ * 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.
+ */
+int
+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);
+ }
+ return (0);
+}
+
+
+
+/*
+ * 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;
+
+ 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);
+
+} /* mktagfile */
+
+/*
+ * 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..fae5127
--- /dev/null
+++ b/libexec/bootpd/tools/bootptest/Makefile
@@ -0,0 +1,12 @@
+# Makefile
+# $FreeBSD$
+
+PROG= bootptest
+MAN= 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..4ba2724
--- /dev/null
+++ b/libexec/bootpd/tools/bootptest/bootptest.8
@@ -0,0 +1,77 @@
+.\" $FreeBSD$
+.\"
+.\" bootptest.8
+.Dd June 10, 1993
+.Dt BOOTPTEST 8
+.Os
+.Sh NAME
+.Nm bootptest
+.Nd "send BOOTP queries and print responses"
+.Sh SYNOPSIS
+.Nm
+.Op Fl f Ar bootfile
+.Op Fl h
+.Op Fl m Ar magic_number
+.Ar server\-name
+.Op Ar template\-file
+.Sh DESCRIPTION
+The
+.Nm
+utility sends BOOTP requests to the host specified as
+.Ar server\-name
+at one\-second intervals until either a response is received,
+or until ten requests have gone unanswered.
+After a response is received,
+.Nm
+will wait one more second listening for additional responses.
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl f Ar bootfile
+Fill in the boot file field of the request with
+.Ar bootfile .
+.It Fl 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.
+.It Fl m Ar magic_number
+Initialize the first word of the vendor options field with
+.Ar magic_number .
+.El
+.Pp
+A
+.Ar template\-file
+may be specified, in which case
+.Nm
+uses the (binary) contents of this file to initialize the
+.Em options
+area of the request packet.
+.Sh SEE ALSO
+.Xr bootpd 8
+.Rs
+.%O RFC951
+.%T "BOOTSTRAP PROTOCOL (BOOTP)"
+.Re
+.Rs
+.%O RFC1048
+.%T "BOOTP Vendor Information Extensions"
+.Re
+.Sh AUTHORS
+The
+.Nm
+utility is a combination of original and derived works.
+The main program module
+.Pq Pa bootptest.c
+is original work by
+.An Gordon W. Ross Aq Mt gwr@mc.com .
+The packet printing module
+.Pq Pa print\-bootp.c
+is a slightly modified
+version of a file from the
+.Bx
+.Xr tcpdump 1
+program.
+.Pp
+This program includes software developed by the University of
+California, Lawrence Berkeley Laboratory and its contributors.
+(See the copyright notice in
+.Pa print\-bootp.c . )
diff --git a/libexec/bootpd/tools/bootptest/bootptest.c b/libexec/bootpd/tools/bootptest/bootptest.c
new file mode 100644
index 0000000..bf27470
--- /dev/null
+++ b/libexec/bootpd/tools/bootptest/bootptest.c
@@ -0,0 +1,520 @@
+/*
+ * 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>
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+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 <sys/utsname.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+
+#ifndef NO_UNISTD
+#include <unistd.h>
+#endif
+
+#include <err.h>
+#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 "getether.h"
+
+#include "patchlevel.h"
+
+static void send_request();
+
+#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 *sndbuf; /* Send packet buffer */
+char *rcvbuf; /* Receive packet buffer */
+
+struct utsname my_uname;
+char *hostname;
+
+/*
+ * 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.
+ */
+
+int
+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, 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);
+
+ if (uname(&my_uname) < 0)
+ errx(1, "can't get hostname");
+ hostname = my_uname.nodename;
+
+ 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 request. */
+ 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 {
+ warnx("bootps/udp: unknown service -- using port %d",
+ 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)
+ errx(1, "%s: unknown host", servername);
+ 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 {
+ warnx("bootpc/udp: unknown service -- using port %d",
+ 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) {
+ if (errno == EACCES) {
+ warn("bind BOOTPC port");
+ errx(1, "you need to run this as root");
+ }
+ else
+ err(1, "bind BOOTPC port");
+ }
+ /*
+ * 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, (char*)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. */
+ 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.
+ */
+ }
+ errx(1, "no response from %s", servername);
+}
+
+static void
+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++) != '\0') {
+ 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;
+ snprintf(b, sizeof(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..a1891fe
--- /dev/null
+++ b/libexec/bootpd/tools/bootptest/bootptest.h
@@ -0,0 +1,23 @@
+/* bootptest.h */
+/* $FreeBSD$ */
+/*
+ * 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;
+
+extern char *ipaddr_string(struct in_addr *);
diff --git a/libexec/bootpd/tools/bootptest/print-bootp.c b/libexec/bootpd/tools/bootptest/print-bootp.c
new file mode 100644
index 0000000..3cdb65a
--- /dev/null
+++ b/libexec/bootpd/tools/bootptest/print-bootp.c
@@ -0,0 +1,491 @@
+/*
+ * 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. 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>
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <sys/time.h> /* for struct timeval in net/if.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. */
+extern int printfn();
+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:%ld", (long)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 described 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
+rfc1048_print(bp, length)
+ register u_char *bp;
+ int length;
+{
+ u_char tag;
+ u_char *ep;
+ register int len;
+ 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>%td)", 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("%ld", (long)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 */
+
+ /* 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..0ae63a3
--- /dev/null
+++ b/libexec/bootpd/trygetea.c
@@ -0,0 +1,55 @@
+/*
+ * trygetea.c - test program for getether.c
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#if defined(SUNOS) || defined(SVR4)
+#include <sys/sockio.h>
+#endif
+
+#ifdef _AIX32
+#include <sys/time.h> /* for struct timeval in net/if.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 "getether.h"
+
+int debug = 0;
+char *progname;
+
+void
+main(argc, argv)
+ int argc;
+ 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], (char*)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..0e2cca1
--- /dev/null
+++ b/libexec/bootpd/trygetif.c
@@ -0,0 +1,74 @@
+/*
+ * trygetif.c - test program for getif.c
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#if defined(SUNOS) || defined(SVR4)
+#include <sys/sockio.h>
+#endif
+
+#ifdef _AIX32
+#include <sys/time.h> /* for struct timeval in net/if.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;
+
+void
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct hostent *hep;
+ struct sockaddr_in *sip; /* Interface address */
+ struct ifreq *ifr;
+ struct in_addr dst_addr;
+ struct in_addr *dap;
+ int 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..9484d04
--- /dev/null
+++ b/libexec/bootpd/trylook.c
@@ -0,0 +1,58 @@
+/*
+ * trylook.c - test program for lookup.c
+ *
+ * $FreeBSD$
+ */
+
+#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;
+
+void
+main(argc, argv)
+ int argc;
+ 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 */
+ printf(" hwa=");
+ hwa = lookup_hwa(argv[i], 1);
+ if (!hwa)
+ printf("?\n");
+ else {
+ int i;
+ for (i = 0; i < 6; i++)
+ printf(":%x", hwa[i] & 0xFF);
+ putchar('\n');
+ }
+
+ }
+ exit(0);
+}
diff --git a/libexec/bootpd/tzone.c b/libexec/bootpd/tzone.c
new file mode 100644
index 0000000..354fb54
--- /dev/null
+++ b/libexec/bootpd/tzone.c
@@ -0,0 +1,48 @@
+/*
+ * tzone.c - get the timezone
+ *
+ * This is shared by bootpd and bootpef
+ *
+ * $FreeBSD$
+ */
+
+#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 <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 tm *tm;
+ time_t now;
+
+ (void)time(&now);
+ if ((tm = localtime(&now)) == NULL) {
+ secondswest = 0; /* Assume GMT for lack of anything better */
+ report(LOG_ERR, "localtime() failed");
+ } else {
+ secondswest = -tm->tm_gmtoff;
+ }
+#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/casper/Makefile b/libexec/casper/Makefile
new file mode 100644
index 0000000..ed6bd7b
--- /dev/null
+++ b/libexec/casper/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+SUBDIR= dns
+SUBDIR+=grp
+SUBDIR+=pwd
+SUBDIR+=random
+SUBDIR+=sysctl
+
+.include <bsd.subdir.mk>
diff --git a/libexec/casper/dns/Makefile b/libexec/casper/dns/Makefile
new file mode 100644
index 0000000..245493e
--- /dev/null
+++ b/libexec/casper/dns/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR} ${.CURDIR}/../../../sbin/casper
+
+PROG= dns
+
+SRCS= dns.c
+
+DPADD= ${LIBCAPSICUM} ${LIBCASPER} ${LIBNV} ${LIBPJDLOG} ${LIBUTIL}
+LDADD= -lcapsicum -lcasper -lnv -lpjdlog -lutil
+
+BINDIR= /libexec/casper
+
+CFLAGS+=-I${.CURDIR}
+CFLAGS+=-I${.CURDIR}/../../../lib/libcapsicum
+CFLAGS+=-I${.CURDIR}/../../../lib/libcasper
+CFLAGS+=-I${.CURDIR}/../../../lib/libpjdlog
+CFLAGS+=-I${.CURDIR}/../../../sbin/casper
+
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/libexec/casper/dns/dns.c b/libexec/casper/dns/dns.c
new file mode 100644
index 0000000..6be022a
--- /dev/null
+++ b/libexec/casper/dns/dns.c
@@ -0,0 +1,425 @@
+/*-
+ * Copyright (c) 2012-2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Pawel Jakub Dawidek under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libcapsicum.h>
+#include <libcapsicum_dns.h>
+#include <libcasper.h>
+#include <nv.h>
+#include <pjdlog.h>
+
+static bool
+dns_allowed_type(const nvlist_t *limits, const char *type)
+{
+ const char *name;
+ bool notypes;
+ void *cookie;
+
+ if (limits == NULL)
+ return (true);
+
+ notypes = true;
+ cookie = NULL;
+ while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
+ if (strncmp(name, "type", sizeof("type") - 1) != 0)
+ continue;
+ notypes = false;
+ if (strcmp(nvlist_get_string(limits, name), type) == 0)
+ return (true);
+ }
+
+ /* If there are no types at all, allow any type. */
+ if (notypes)
+ return (true);
+
+ return (false);
+}
+
+static bool
+dns_allowed_family(const nvlist_t *limits, int family)
+{
+ const char *name;
+ bool nofamilies;
+ void *cookie;
+
+ if (limits == NULL)
+ return (true);
+
+ nofamilies = true;
+ cookie = NULL;
+ while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
+ if (strncmp(name, "family", sizeof("family") - 1) != 0)
+ continue;
+ nofamilies = false;
+ if (family == AF_UNSPEC)
+ continue;
+ if (nvlist_get_number(limits, name) == (uint64_t)family)
+ return (true);
+ }
+
+ /* If there are no families at all, allow any family. */
+ if (nofamilies)
+ return (true);
+
+ return (false);
+}
+
+static void
+hostent_pack(const struct hostent *hp, nvlist_t *nvl)
+{
+ unsigned int ii;
+
+ nvlist_add_string(nvl, "name", hp->h_name);
+ nvlist_add_number(nvl, "addrtype", (uint64_t)hp->h_addrtype);
+ nvlist_add_number(nvl, "length", (uint64_t)hp->h_length);
+
+ if (hp->h_aliases == NULL) {
+ nvlist_add_number(nvl, "naliases", 0);
+ } else {
+ for (ii = 0; hp->h_aliases[ii] != NULL; ii++) {
+ nvlist_addf_string(nvl, hp->h_aliases[ii], "alias%u",
+ ii);
+ }
+ nvlist_add_number(nvl, "naliases", (uint64_t)ii);
+ }
+
+ if (hp->h_addr_list == NULL) {
+ nvlist_add_number(nvl, "naddrs", 0);
+ } else {
+ for (ii = 0; hp->h_addr_list[ii] != NULL; ii++) {
+ nvlist_addf_binary(nvl, hp->h_addr_list[ii],
+ (size_t)hp->h_length, "addr%u", ii);
+ }
+ nvlist_add_number(nvl, "naddrs", (uint64_t)ii);
+ }
+}
+
+static int
+dns_gethostbyname(const nvlist_t *limits, const nvlist_t *nvlin,
+ nvlist_t *nvlout)
+{
+ struct hostent *hp;
+ int family;
+
+ if (!dns_allowed_type(limits, "NAME"))
+ return (NO_RECOVERY);
+
+ family = (int)nvlist_get_number(nvlin, "family");
+
+ if (!dns_allowed_family(limits, family))
+ return (NO_RECOVERY);
+
+ hp = gethostbyname2(nvlist_get_string(nvlin, "name"), family);
+ if (hp == NULL)
+ return (h_errno);
+ hostent_pack(hp, nvlout);
+ return (0);
+}
+
+static int
+dns_gethostbyaddr(const nvlist_t *limits, const nvlist_t *nvlin,
+ nvlist_t *nvlout)
+{
+ struct hostent *hp;
+ const void *addr;
+ size_t addrsize;
+ int family;
+
+ if (!dns_allowed_type(limits, "ADDR"))
+ return (NO_RECOVERY);
+
+ family = (int)nvlist_get_number(nvlin, "family");
+
+ if (!dns_allowed_family(limits, family))
+ return (NO_RECOVERY);
+
+ addr = nvlist_get_binary(nvlin, "addr", &addrsize);
+ hp = gethostbyaddr(addr, (socklen_t)addrsize, family);
+ if (hp == NULL)
+ return (h_errno);
+ hostent_pack(hp, nvlout);
+ return (0);
+}
+
+static int
+dns_getnameinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
+{
+ struct sockaddr_storage sast;
+ const void *sabin;
+ char *host, *serv;
+ size_t sabinsize, hostlen, servlen;
+ socklen_t salen;
+ int error, flags;
+
+ if (!dns_allowed_type(limits, "NAME"))
+ return (NO_RECOVERY);
+
+ error = 0;
+ host = serv = NULL;
+ memset(&sast, 0, sizeof(sast));
+
+ hostlen = (size_t)nvlist_get_number(nvlin, "hostlen");
+ servlen = (size_t)nvlist_get_number(nvlin, "servlen");
+
+ if (hostlen > 0) {
+ host = calloc(1, hostlen + 1);
+ if (host == NULL) {
+ error = EAI_MEMORY;
+ goto out;
+ }
+ }
+ if (servlen > 0) {
+ serv = calloc(1, servlen + 1);
+ if (serv == NULL) {
+ error = EAI_MEMORY;
+ goto out;
+ }
+ }
+
+ sabin = nvlist_get_binary(nvlin, "sa", &sabinsize);
+ if (sabinsize > sizeof(sast)) {
+ error = EAI_FAIL;
+ goto out;
+ }
+
+ memcpy(&sast, sabin, sabinsize);
+ salen = (socklen_t)sabinsize;
+
+ if ((sast.ss_family != AF_INET ||
+ salen != sizeof(struct sockaddr_in)) &&
+ (sast.ss_family != AF_INET6 ||
+ salen != sizeof(struct sockaddr_in6))) {
+ error = EAI_FAIL;
+ goto out;
+ }
+
+ if (!dns_allowed_family(limits, (int)sast.ss_family))
+ return (NO_RECOVERY);
+
+ flags = (int)nvlist_get_number(nvlin, "flags");
+
+ error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen,
+ serv, servlen, flags);
+ if (error != 0)
+ goto out;
+
+ nvlist_move_string(nvlout, "host", host);
+ nvlist_move_string(nvlout, "serv", serv);
+out:
+ if (error != 0) {
+ free(host);
+ free(serv);
+ }
+ return (error);
+}
+
+static nvlist_t *
+addrinfo_pack(const struct addrinfo *ai)
+{
+ nvlist_t *nvl;
+
+ nvl = nvlist_create(0);
+ nvlist_add_number(nvl, "ai_flags", (uint64_t)ai->ai_flags);
+ nvlist_add_number(nvl, "ai_family", (uint64_t)ai->ai_family);
+ nvlist_add_number(nvl, "ai_socktype", (uint64_t)ai->ai_socktype);
+ nvlist_add_number(nvl, "ai_protocol", (uint64_t)ai->ai_protocol);
+ nvlist_add_binary(nvl, "ai_addr", ai->ai_addr, (size_t)ai->ai_addrlen);
+ nvlist_add_string(nvl, "ai_canonname", ai->ai_canonname);
+
+ return (nvl);
+}
+
+static int
+dns_getaddrinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
+{
+ struct addrinfo hints, *hintsp, *res, *cur;
+ const char *hostname, *servname;
+ nvlist_t *elem;
+ unsigned int ii;
+ int error, family;
+
+ if (!dns_allowed_type(limits, "ADDR"))
+ return (NO_RECOVERY);
+
+ hostname = nvlist_get_string(nvlin, "hostname");
+ servname = nvlist_get_string(nvlin, "servname");
+ if (nvlist_exists_number(nvlin, "hints.ai_flags")) {
+ size_t addrlen;
+
+ hints.ai_flags = (int)nvlist_get_number(nvlin,
+ "hints.ai_flags");
+ hints.ai_family = (int)nvlist_get_number(nvlin,
+ "hints.ai_family");
+ hints.ai_socktype = (int)nvlist_get_number(nvlin,
+ "hints.ai_socktype");
+ hints.ai_protocol = (int)nvlist_get_number(nvlin,
+ "hints.ai_protocol");
+ hints.ai_addrlen = 0;
+ hints.ai_addr = NULL;
+ hints.ai_canonname = NULL;
+ hintsp = &hints;
+ family = hints.ai_family;
+ } else {
+ hintsp = NULL;
+ family = AF_UNSPEC;
+ }
+
+ if (!dns_allowed_family(limits, family))
+ return (NO_RECOVERY);
+
+ error = getaddrinfo(hostname, servname, hintsp, &res);
+ if (error != 0)
+ goto out;
+
+ for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) {
+ elem = addrinfo_pack(cur);
+ nvlist_movef_nvlist(nvlout, elem, "res%u", ii);
+ }
+
+ freeaddrinfo(res);
+ error = 0;
+out:
+ return (error);
+}
+
+static bool
+limit_has_entry(const nvlist_t *limits, const char *prefix)
+{
+ const char *name;
+ size_t prefixlen;
+ void *cookie;
+
+ if (limits == NULL)
+ return (false);
+
+ prefixlen = strlen(prefix);
+
+ cookie = NULL;
+ while ((name = nvlist_next(limits, NULL, &cookie)) != NULL) {
+ if (strncmp(name, prefix, prefixlen) == 0)
+ return (true);
+ }
+
+ return (false);
+}
+
+static int
+dns_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
+{
+ const char *name;
+ void *cookie;
+ int nvtype;
+ bool hastype, hasfamily;
+
+ hastype = false;
+ hasfamily = false;
+
+ cookie = NULL;
+ while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) {
+ if (nvtype == NV_TYPE_STRING) {
+ const char *type;
+
+ if (strncmp(name, "type", sizeof("type") - 1) != 0)
+ return (EINVAL);
+ type = nvlist_get_string(newlimits, name);
+ if (strcmp(type, "ADDR") != 0 &&
+ strcmp(type, "NAME") != 0) {
+ return (EINVAL);
+ }
+ if (!dns_allowed_type(oldlimits, type))
+ return (ENOTCAPABLE);
+ hastype = true;
+ } else if (nvtype == NV_TYPE_NUMBER) {
+ int family;
+
+ if (strncmp(name, "family", sizeof("family") - 1) != 0)
+ return (EINVAL);
+ family = (int)nvlist_get_number(newlimits, name);
+ if (!dns_allowed_family(oldlimits, family))
+ return (ENOTCAPABLE);
+ hasfamily = true;
+ } else {
+ return (EINVAL);
+ }
+ }
+
+ /*
+ * If the new limit doesn't mention type or family we have to
+ * check if the current limit does have those. Missing type or
+ * family in the limit means that all types or families are
+ * allowed.
+ */
+ if (!hastype) {
+ if (limit_has_entry(oldlimits, "type"))
+ return (ENOTCAPABLE);
+ }
+ if (!hasfamily) {
+ if (limit_has_entry(oldlimits, "family"))
+ return (ENOTCAPABLE);
+ }
+
+ return (0);
+}
+
+static int
+dns_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
+ nvlist_t *nvlout)
+{
+ int error;
+
+ if (strcmp(cmd, "gethostbyname") == 0)
+ error = dns_gethostbyname(limits, nvlin, nvlout);
+ else if (strcmp(cmd, "gethostbyaddr") == 0)
+ error = dns_gethostbyaddr(limits, nvlin, nvlout);
+ else if (strcmp(cmd, "getnameinfo") == 0)
+ error = dns_getnameinfo(limits, nvlin, nvlout);
+ else if (strcmp(cmd, "getaddrinfo") == 0)
+ error = dns_getaddrinfo(limits, nvlin, nvlout);
+ else
+ error = NO_RECOVERY;
+
+ return (error);
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ return (service_start("system.dns", PARENT_FILENO, dns_limit,
+ dns_command, argc, argv));
+}
diff --git a/libexec/casper/grp/Makefile b/libexec/casper/grp/Makefile
new file mode 100644
index 0000000..502cd26
--- /dev/null
+++ b/libexec/casper/grp/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR} ${.CURDIR}/../../../sbin/casper
+
+PROG= grp
+
+SRCS= grp.c
+
+DPADD= ${LIBCAPSICUM} ${LIBCASPER} ${LIBNV} ${LIBPJDLOG} ${LIBUTIL}
+LDADD= -lcapsicum -lcasper -lnv -lpjdlog -lutil
+
+BINDIR= /libexec/casper
+
+CFLAGS+=-I${.CURDIR}
+CFLAGS+=-I${.CURDIR}/../../../lib/libcapsicum
+CFLAGS+=-I${.CURDIR}/../../../lib/libcasper
+CFLAGS+=-I${.CURDIR}/../../../lib/libpjdlog
+CFLAGS+=-I${.CURDIR}/../../../sbin/casper
+
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/libexec/casper/grp/grp.c b/libexec/casper/grp/grp.c
new file mode 100644
index 0000000..ba22f62
--- /dev/null
+++ b/libexec/casper/grp/grp.c
@@ -0,0 +1,384 @@
+/*-
+ * Copyright (c) 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Pawel Jakub Dawidek under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libcapsicum.h>
+#include <libcasper.h>
+#include <nv.h>
+#include <pjdlog.h>
+
+static bool
+grp_allowed_cmd(const nvlist_t *limits, const char *cmd)
+{
+
+ if (limits == NULL)
+ return (true);
+
+ /*
+ * If no limit was set on allowed commands, then all commands
+ * are allowed.
+ */
+ if (!nvlist_exists_nvlist(limits, "cmds"))
+ return (true);
+
+ limits = nvlist_get_nvlist(limits, "cmds");
+ return (nvlist_exists_null(limits, cmd));
+}
+
+static int
+grp_allowed_cmds(const nvlist_t *oldlimits, const nvlist_t *newlimits)
+{
+ const char *name;
+ void *cookie;
+ int type;
+
+ cookie = NULL;
+ while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
+ if (type != NV_TYPE_NULL)
+ return (EINVAL);
+ if (!grp_allowed_cmd(oldlimits, name))
+ return (ENOTCAPABLE);
+ }
+
+ return (0);
+}
+
+static bool
+grp_allowed_group(const nvlist_t *limits, const char *gname, gid_t gid)
+{
+ const char *name;
+ void *cookie;
+ int type;
+
+ if (limits == NULL)
+ return (true);
+
+ /*
+ * If no limit was set on allowed groups, then all groups are allowed.
+ */
+ if (!nvlist_exists_nvlist(limits, "groups"))
+ return (true);
+
+ limits = nvlist_get_nvlist(limits, "groups");
+ cookie = NULL;
+ while ((name = nvlist_next(limits, &type, &cookie)) != NULL) {
+ switch (type) {
+ case NV_TYPE_NUMBER:
+ if (gid != (gid_t)-1 &&
+ nvlist_get_number(limits, name) == (uint64_t)gid) {
+ return (true);
+ }
+ break;
+ case NV_TYPE_STRING:
+ if (gname != NULL &&
+ strcmp(nvlist_get_string(limits, name),
+ gname) == 0) {
+ return (true);
+ }
+ break;
+ default:
+ PJDLOG_ABORT("Unexpected type %d.", type);
+ }
+ }
+
+ return (false);
+}
+
+static int
+grp_allowed_groups(const nvlist_t *oldlimits, const nvlist_t *newlimits)
+{
+ const char *name, *gname;
+ void *cookie;
+ gid_t gid;
+ int type;
+
+ cookie = NULL;
+ while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
+ switch (type) {
+ case NV_TYPE_NUMBER:
+ gid = (gid_t)nvlist_get_number(newlimits, name);
+ gname = NULL;
+ break;
+ case NV_TYPE_STRING:
+ gid = (gid_t)-1;
+ gname = nvlist_get_string(newlimits, name);
+ break;
+ default:
+ return (EINVAL);
+ }
+ if (!grp_allowed_group(oldlimits, gname, gid))
+ return (ENOTCAPABLE);
+ }
+
+ return (0);
+}
+
+static bool
+grp_allowed_field(const nvlist_t *limits, const char *field)
+{
+
+ if (limits == NULL)
+ return (true);
+
+ /*
+ * If no limit was set on allowed fields, then all fields are allowed.
+ */
+ if (!nvlist_exists_nvlist(limits, "fields"))
+ return (true);
+
+ limits = nvlist_get_nvlist(limits, "fields");
+ return (nvlist_exists_null(limits, field));
+}
+
+static int
+grp_allowed_fields(const nvlist_t *oldlimits, const nvlist_t *newlimits)
+{
+ const char *name;
+ void *cookie;
+ int type;
+
+ cookie = NULL;
+ while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
+ if (type != NV_TYPE_NULL)
+ return (EINVAL);
+ if (!grp_allowed_field(oldlimits, name))
+ return (ENOTCAPABLE);
+ }
+
+ return (0);
+}
+
+static bool
+grp_pack(const nvlist_t *limits, const struct group *grp, nvlist_t *nvl)
+{
+
+ if (grp == NULL)
+ return (true);
+
+ /*
+ * If either name or GID is allowed, we allow it.
+ */
+ if (!grp_allowed_group(limits, grp->gr_name, grp->gr_gid))
+ return (false);
+
+ if (grp_allowed_field(limits, "gr_name"))
+ nvlist_add_string(nvl, "gr_name", grp->gr_name);
+ else
+ nvlist_add_string(nvl, "gr_name", "");
+ if (grp_allowed_field(limits, "gr_passwd"))
+ nvlist_add_string(nvl, "gr_passwd", grp->gr_passwd);
+ else
+ nvlist_add_string(nvl, "gr_passwd", "");
+ if (grp_allowed_field(limits, "gr_gid"))
+ nvlist_add_number(nvl, "gr_gid", (uint64_t)grp->gr_gid);
+ else
+ nvlist_add_number(nvl, "gr_gid", (uint64_t)-1);
+ if (grp_allowed_field(limits, "gr_mem") && grp->gr_mem[0] != NULL) {
+ unsigned int ngroups;
+
+ for (ngroups = 0; grp->gr_mem[ngroups] != NULL; ngroups++) {
+ nvlist_addf_string(nvl, grp->gr_mem[ngroups],
+ "gr_mem[%u]", ngroups);
+ }
+ nvlist_add_number(nvl, "gr_nmem", (uint64_t)ngroups);
+ }
+
+ return (true);
+}
+
+static int
+grp_getgrent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
+{
+ struct group *grp;
+
+ for (;;) {
+ errno = 0;
+ grp = getgrent();
+ if (errno != 0)
+ return (errno);
+ if (grp_pack(limits, grp, nvlout))
+ return (0);
+ }
+
+ /* NOTREACHED */
+}
+
+static int
+grp_getgrnam(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
+{
+ struct group *grp;
+ const char *name;
+
+ if (!nvlist_exists_string(nvlin, "name"))
+ return (EINVAL);
+ name = nvlist_get_string(nvlin, "name");
+ PJDLOG_ASSERT(name != NULL);
+
+ errno = 0;
+ grp = getgrnam(name);
+ if (errno != 0)
+ return (errno);
+
+ (void)grp_pack(limits, grp, nvlout);
+
+ return (0);
+}
+
+static int
+grp_getgrgid(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
+{
+ struct group *grp;
+ gid_t gid;
+
+ if (!nvlist_exists_number(nvlin, "gid"))
+ return (EINVAL);
+
+ gid = (gid_t)nvlist_get_number(nvlin, "gid");
+
+ errno = 0;
+ grp = getgrgid(gid);
+ if (errno != 0)
+ return (errno);
+
+ (void)grp_pack(limits, grp, nvlout);
+
+ return (0);
+}
+
+static int
+grp_setgroupent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
+{
+ int stayopen;
+
+ if (!nvlist_exists_bool(nvlin, "stayopen"))
+ return (EINVAL);
+
+ stayopen = nvlist_get_bool(nvlin, "stayopen") ? 1 : 0;
+
+ return (setgroupent(stayopen) == 0 ? EFAULT : 0);
+}
+
+static int
+grp_setgrent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
+{
+
+ return (setgrent() == 0 ? EFAULT : 0);
+}
+
+static int
+grp_endgrent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
+{
+
+ endgrent();
+
+ return (0);
+}
+
+static int
+grp_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
+{
+ const nvlist_t *limits;
+ const char *name;
+ void *cookie;
+ int error, type;
+
+ if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "cmds") &&
+ !nvlist_exists_nvlist(newlimits, "cmds")) {
+ return (ENOTCAPABLE);
+ }
+ if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "fields") &&
+ !nvlist_exists_nvlist(newlimits, "fields")) {
+ return (ENOTCAPABLE);
+ }
+ if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "groups") &&
+ !nvlist_exists_nvlist(newlimits, "groups")) {
+ return (ENOTCAPABLE);
+ }
+
+ cookie = NULL;
+ while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
+ if (type != NV_TYPE_NVLIST)
+ return (EINVAL);
+ limits = nvlist_get_nvlist(newlimits, name);
+ if (strcmp(name, "cmds") == 0)
+ error = grp_allowed_cmds(oldlimits, limits);
+ else if (strcmp(name, "fields") == 0)
+ error = grp_allowed_fields(oldlimits, limits);
+ else if (strcmp(name, "groups") == 0)
+ error = grp_allowed_groups(oldlimits, limits);
+ else
+ error = EINVAL;
+ if (error != 0)
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+grp_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
+ nvlist_t *nvlout)
+{
+ int error;
+
+ if (!grp_allowed_cmd(limits, cmd))
+ return (ENOTCAPABLE);
+
+ if (strcmp(cmd, "getgrent") == 0 || strcmp(cmd, "getgrent_r") == 0)
+ error = grp_getgrent(limits, nvlin, nvlout);
+ else if (strcmp(cmd, "getgrnam") == 0 || strcmp(cmd, "getgrnam_r") == 0)
+ error = grp_getgrnam(limits, nvlin, nvlout);
+ else if (strcmp(cmd, "getgrgid") == 0 || strcmp(cmd, "getgrgid_r") == 0)
+ error = grp_getgrgid(limits, nvlin, nvlout);
+ else if (strcmp(cmd, "setgroupent") == 0)
+ error = grp_setgroupent(limits, nvlin, nvlout);
+ else if (strcmp(cmd, "setgrent") == 0)
+ error = grp_setgrent(limits, nvlin, nvlout);
+ else if (strcmp(cmd, "endgrent") == 0)
+ error = grp_endgrent(limits, nvlin, nvlout);
+ else
+ error = EINVAL;
+
+ return (error);
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ return (service_start("system.grp", PARENT_FILENO, grp_limit,
+ grp_command, argc, argv));
+}
diff --git a/libexec/casper/pwd/Makefile b/libexec/casper/pwd/Makefile
new file mode 100644
index 0000000..ebf6cac
--- /dev/null
+++ b/libexec/casper/pwd/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR} ${.CURDIR}/../../../sbin/casper
+
+PROG= pwd
+
+SRCS= pwd.c
+
+DPADD= ${LIBCAPSICUM} ${LIBCASPER} ${LIBNV} ${LIBPJDLOG} ${LIBUTIL}
+LDADD= -lcapsicum -lcasper -lnv -lpjdlog -lutil
+
+BINDIR= /libexec/casper
+
+CFLAGS+=-I${.CURDIR}
+CFLAGS+=-I${.CURDIR}/../../../lib/libcapsicum
+CFLAGS+=-I${.CURDIR}/../../../lib/libcasper
+CFLAGS+=-I${.CURDIR}/../../../lib/libpjdlog
+CFLAGS+=-I${.CURDIR}/../../../sbin/casper
+
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/libexec/casper/pwd/pwd.c b/libexec/casper/pwd/pwd.c
new file mode 100644
index 0000000..be51ad0
--- /dev/null
+++ b/libexec/casper/pwd/pwd.c
@@ -0,0 +1,429 @@
+/*-
+ * Copyright (c) 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Pawel Jakub Dawidek under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libcapsicum.h>
+#include <libcasper.h>
+#include <nv.h>
+#include <pjdlog.h>
+
+static bool
+pwd_allowed_cmd(const nvlist_t *limits, const char *cmd)
+{
+
+ if (limits == NULL)
+ return (true);
+
+ /*
+ * If no limit was set on allowed commands, then all commands
+ * are allowed.
+ */
+ if (!nvlist_exists_nvlist(limits, "cmds"))
+ return (true);
+
+ limits = nvlist_get_nvlist(limits, "cmds");
+ return (nvlist_exists_null(limits, cmd));
+}
+
+static int
+pwd_allowed_cmds(const nvlist_t *oldlimits, const nvlist_t *newlimits)
+{
+ const char *name;
+ void *cookie;
+ int type;
+
+ cookie = NULL;
+ while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
+ if (type != NV_TYPE_NULL)
+ return (EINVAL);
+ if (!pwd_allowed_cmd(oldlimits, name))
+ return (ENOTCAPABLE);
+ }
+
+ return (0);
+}
+
+static bool
+pwd_allowed_user(const nvlist_t *limits, const char *uname, uid_t uid)
+{
+ const char *name;
+ void *cookie;
+ int type;
+
+ if (limits == NULL)
+ return (true);
+
+ /*
+ * If no limit was set on allowed users, then all users are allowed.
+ */
+ if (!nvlist_exists_nvlist(limits, "users"))
+ return (true);
+
+ limits = nvlist_get_nvlist(limits, "users");
+ cookie = NULL;
+ while ((name = nvlist_next(limits, &type, &cookie)) != NULL) {
+ switch (type) {
+ case NV_TYPE_NUMBER:
+ if (uid != (uid_t)-1 &&
+ nvlist_get_number(limits, name) == (uint64_t)uid) {
+ return (true);
+ }
+ break;
+ case NV_TYPE_STRING:
+ if (uname != NULL &&
+ strcmp(nvlist_get_string(limits, name),
+ uname) == 0) {
+ return (true);
+ }
+ break;
+ default:
+ PJDLOG_ABORT("Unexpected type %d.", type);
+ }
+ }
+
+ return (false);
+}
+
+static int
+pwd_allowed_users(const nvlist_t *oldlimits, const nvlist_t *newlimits)
+{
+ const char *name, *uname;
+ void *cookie;
+ uid_t uid;
+ int type;
+
+ cookie = NULL;
+ while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
+ switch (type) {
+ case NV_TYPE_NUMBER:
+ uid = (uid_t)nvlist_get_number(newlimits, name);
+ uname = NULL;
+ break;
+ case NV_TYPE_STRING:
+ uid = (uid_t)-1;
+ uname = nvlist_get_string(newlimits, name);
+ break;
+ default:
+ return (EINVAL);
+ }
+ if (!pwd_allowed_user(oldlimits, uname, uid))
+ return (ENOTCAPABLE);
+ }
+
+ return (0);
+}
+
+static bool
+pwd_allowed_field(const nvlist_t *limits, const char *field)
+{
+
+ if (limits == NULL)
+ return (true);
+
+ /*
+ * If no limit was set on allowed fields, then all fields are allowed.
+ */
+ if (!nvlist_exists_nvlist(limits, "fields"))
+ return (true);
+
+ limits = nvlist_get_nvlist(limits, "fields");
+ return (nvlist_exists_null(limits, field));
+}
+
+static int
+pwd_allowed_fields(const nvlist_t *oldlimits, const nvlist_t *newlimits)
+{
+ const char *name;
+ void *cookie;
+ int type;
+
+ cookie = NULL;
+ while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
+ if (type != NV_TYPE_NULL)
+ return (EINVAL);
+ if (!pwd_allowed_field(oldlimits, name))
+ return (ENOTCAPABLE);
+ }
+
+ return (0);
+}
+
+static bool
+pwd_pack(const nvlist_t *limits, const struct passwd *pwd, nvlist_t *nvl)
+{
+ int fields;
+
+ if (pwd == NULL)
+ return (true);
+
+ /*
+ * If either name or UID is allowed, we allow it.
+ */
+ if (!pwd_allowed_user(limits, pwd->pw_name, pwd->pw_uid))
+ return (false);
+
+ fields = pwd->pw_fields;
+
+ if (pwd_allowed_field(limits, "pw_name")) {
+ nvlist_add_string(nvl, "pw_name", pwd->pw_name);
+ } else {
+ nvlist_add_string(nvl, "pw_name", "");
+ fields &= ~_PWF_NAME;
+ }
+ if (pwd_allowed_field(limits, "pw_uid")) {
+ nvlist_add_number(nvl, "pw_uid", (uint64_t)pwd->pw_uid);
+ } else {
+ nvlist_add_number(nvl, "pw_uid", (uint64_t)-1);
+ fields &= ~_PWF_UID;
+ }
+ if (pwd_allowed_field(limits, "pw_gid")) {
+ nvlist_add_number(nvl, "pw_gid", (uint64_t)pwd->pw_gid);
+ } else {
+ nvlist_add_number(nvl, "pw_gid", (uint64_t)-1);
+ fields &= ~_PWF_GID;
+ }
+ if (pwd_allowed_field(limits, "pw_change")) {
+ nvlist_add_number(nvl, "pw_change", (uint64_t)pwd->pw_change);
+ } else {
+ nvlist_add_number(nvl, "pw_change", (uint64_t)0);
+ fields &= ~_PWF_CHANGE;
+ }
+ if (pwd_allowed_field(limits, "pw_passwd")) {
+ nvlist_add_string(nvl, "pw_passwd", pwd->pw_passwd);
+ } else {
+ nvlist_add_string(nvl, "pw_passwd", "");
+ fields &= ~_PWF_PASSWD;
+ }
+ if (pwd_allowed_field(limits, "pw_class")) {
+ nvlist_add_string(nvl, "pw_class", pwd->pw_class);
+ } else {
+ nvlist_add_string(nvl, "pw_class", "");
+ fields &= ~_PWF_CLASS;
+ }
+ if (pwd_allowed_field(limits, "pw_gecos")) {
+ nvlist_add_string(nvl, "pw_gecos", pwd->pw_gecos);
+ } else {
+ nvlist_add_string(nvl, "pw_gecos", "");
+ fields &= ~_PWF_GECOS;
+ }
+ if (pwd_allowed_field(limits, "pw_dir")) {
+ nvlist_add_string(nvl, "pw_dir", pwd->pw_dir);
+ } else {
+ nvlist_add_string(nvl, "pw_dir", "");
+ fields &= ~_PWF_DIR;
+ }
+ if (pwd_allowed_field(limits, "pw_shell")) {
+ nvlist_add_string(nvl, "pw_shell", pwd->pw_shell);
+ } else {
+ nvlist_add_string(nvl, "pw_shell", "");
+ fields &= ~_PWF_SHELL;
+ }
+ if (pwd_allowed_field(limits, "pw_expire")) {
+ nvlist_add_number(nvl, "pw_expire", (uint64_t)pwd->pw_expire);
+ } else {
+ nvlist_add_number(nvl, "pw_expire", (uint64_t)0);
+ fields &= ~_PWF_EXPIRE;
+ }
+ nvlist_add_number(nvl, "pw_fields", (uint64_t)fields);
+
+ return (true);
+}
+
+static int
+pwd_getpwent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
+{
+ struct passwd *pwd;
+
+ for (;;) {
+ errno = 0;
+ pwd = getpwent();
+ if (errno != 0)
+ return (errno);
+ if (pwd_pack(limits, pwd, nvlout))
+ return (0);
+ }
+
+ /* NOTREACHED */
+}
+
+static int
+pwd_getpwnam(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
+{
+ struct passwd *pwd;
+ const char *name;
+
+ if (!nvlist_exists_string(nvlin, "name"))
+ return (EINVAL);
+ name = nvlist_get_string(nvlin, "name");
+ PJDLOG_ASSERT(name != NULL);
+
+ errno = 0;
+ pwd = getpwnam(name);
+ if (errno != 0)
+ return (errno);
+
+ (void)pwd_pack(limits, pwd, nvlout);
+
+ return (0);
+}
+
+static int
+pwd_getpwuid(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
+{
+ struct passwd *pwd;
+ uid_t uid;
+
+ if (!nvlist_exists_number(nvlin, "uid"))
+ return (EINVAL);
+
+ uid = (uid_t)nvlist_get_number(nvlin, "uid");
+
+ errno = 0;
+ pwd = getpwuid(uid);
+ if (errno != 0)
+ return (errno);
+
+ (void)pwd_pack(limits, pwd, nvlout);
+
+ return (0);
+}
+
+static int
+pwd_setpassent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
+{
+ int stayopen;
+
+ if (!nvlist_exists_bool(nvlin, "stayopen"))
+ return (EINVAL);
+
+ stayopen = nvlist_get_bool(nvlin, "stayopen") ? 1 : 0;
+
+ return (setpassent(stayopen) == 0 ? EFAULT : 0);
+}
+
+static int
+pwd_setpwent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
+{
+
+ setpwent();
+
+ return (0);
+}
+
+static int
+pwd_endpwent(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
+{
+
+ endpwent();
+
+ return (0);
+}
+
+static int
+pwd_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
+{
+ const nvlist_t *limits;
+ const char *name;
+ void *cookie;
+ int error, type;
+
+ if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "cmds") &&
+ !nvlist_exists_nvlist(newlimits, "cmds")) {
+ return (ENOTCAPABLE);
+ }
+ if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "fields") &&
+ !nvlist_exists_nvlist(newlimits, "fields")) {
+ return (ENOTCAPABLE);
+ }
+ if (oldlimits != NULL && nvlist_exists_nvlist(oldlimits, "users") &&
+ !nvlist_exists_nvlist(newlimits, "users")) {
+ return (ENOTCAPABLE);
+ }
+
+ cookie = NULL;
+ while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
+ if (type != NV_TYPE_NVLIST)
+ return (EINVAL);
+ limits = nvlist_get_nvlist(newlimits, name);
+ if (strcmp(name, "cmds") == 0)
+ error = pwd_allowed_cmds(oldlimits, limits);
+ else if (strcmp(name, "fields") == 0)
+ error = pwd_allowed_fields(oldlimits, limits);
+ else if (strcmp(name, "users") == 0)
+ error = pwd_allowed_users(oldlimits, limits);
+ else
+ error = EINVAL;
+ if (error != 0)
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+pwd_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
+ nvlist_t *nvlout)
+{
+ int error;
+
+ if (!pwd_allowed_cmd(limits, cmd))
+ return (ENOTCAPABLE);
+
+ if (strcmp(cmd, "getpwent") == 0 || strcmp(cmd, "getpwent_r") == 0)
+ error = pwd_getpwent(limits, nvlin, nvlout);
+ else if (strcmp(cmd, "getpwnam") == 0 || strcmp(cmd, "getpwnam_r") == 0)
+ error = pwd_getpwnam(limits, nvlin, nvlout);
+ else if (strcmp(cmd, "getpwuid") == 0 || strcmp(cmd, "getpwuid_r") == 0)
+ error = pwd_getpwuid(limits, nvlin, nvlout);
+ else if (strcmp(cmd, "setpassent") == 0)
+ error = pwd_setpassent(limits, nvlin, nvlout);
+ else if (strcmp(cmd, "setpwent") == 0)
+ error = pwd_setpwent(limits, nvlin, nvlout);
+ else if (strcmp(cmd, "endpwent") == 0)
+ error = pwd_endpwent(limits, nvlin, nvlout);
+ else
+ error = EINVAL;
+
+ return (error);
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ return (service_start("system.pwd", PARENT_FILENO, pwd_limit,
+ pwd_command, argc, argv));
+}
diff --git a/libexec/casper/random/Makefile b/libexec/casper/random/Makefile
new file mode 100644
index 0000000..46c9739
--- /dev/null
+++ b/libexec/casper/random/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR} ${.CURDIR}/../../../sbin/casper
+
+PROG= random
+
+SRCS= random.c
+
+DPADD= ${LIBCAPSICUM} ${LIBCASPER} ${LIBNV} ${LIBPJDLOG} ${LIBUTIL}
+LDADD= -lcapsicum -lcasper -lnv -lpjdlog -lutil
+
+BINDIR= /libexec/casper
+
+CFLAGS+=-I${.CURDIR}
+CFLAGS+=-I${.CURDIR}/../../../lib/libcapsicum
+CFLAGS+=-I${.CURDIR}/../../../lib/libcasper
+CFLAGS+=-I${.CURDIR}/../../../lib/libpjdlog
+CFLAGS+=-I${.CURDIR}/../../../sbin/casper
+
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/libexec/casper/random/random.c b/libexec/casper/random/random.c
new file mode 100644
index 0000000..56f1afb
--- /dev/null
+++ b/libexec/casper/random/random.c
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (c) 2012-2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Pawel Jakub Dawidek under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libcapsicum.h>
+#include <libcasper.h>
+#include <nv.h>
+#include <pjdlog.h>
+
+#define MAXSIZE (1024 * 1024)
+
+static int
+random_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
+ nvlist_t *nvlout)
+{
+ void *data;
+ size_t size;
+
+ if (strcmp(cmd, "generate") != 0)
+ return (EINVAL);
+ if (!nvlist_exists_number(nvlin, "size"))
+ return (EINVAL);
+
+ size = (size_t)nvlist_get_number(nvlin, "size");
+ if (size == 0 || size > MAXSIZE)
+ return (EINVAL);
+
+ data = malloc(size);
+ if (data == NULL)
+ return (ENOMEM);
+
+ arc4random_buf(data, size);
+
+ nvlist_move_binary(nvlout, "data", data, size);
+
+ return (0);
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ /*
+ * TODO: Sandbox this.
+ */
+
+ return (service_start("system.random", PARENT_FILENO, NULL,
+ random_command, argc, argv));
+}
diff --git a/libexec/casper/sysctl/Makefile b/libexec/casper/sysctl/Makefile
new file mode 100644
index 0000000..432008e
--- /dev/null
+++ b/libexec/casper/sysctl/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR} ${.CURDIR}/../../../sbin/casper
+
+PROG= sysctl
+
+SRCS= sysctl.c
+
+DPADD= ${LIBCAPSICUM} ${LIBCASPER} ${LIBNV} ${LIBPJDLOG} ${LIBUTIL}
+LDADD= -lcapsicum -lcasper -lnv -lpjdlog -lutil
+
+BINDIR= /libexec/casper
+
+CFLAGS+=-I${.CURDIR}
+CFLAGS+=-I${.CURDIR}/../../../lib/libcapsicum
+CFLAGS+=-I${.CURDIR}/../../../lib/libcasper
+CFLAGS+=-I${.CURDIR}/../../../lib/libpjdlog
+CFLAGS+=-I${.CURDIR}/../../../sbin/casper
+
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/libexec/casper/sysctl/sysctl.c b/libexec/casper/sysctl/sysctl.c
new file mode 100644
index 0000000..8bb43b6
--- /dev/null
+++ b/libexec/casper/sysctl/sysctl.c
@@ -0,0 +1,249 @@
+/*-
+ * Copyright (c) 2013 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Pawel Jakub Dawidek under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libcapsicum.h>
+#include <libcapsicum_sysctl.h>
+#include <libcasper.h>
+#include <nv.h>
+#include <pjdlog.h>
+
+static int
+sysctl_check_one(const nvlist_t *nvl, bool islimit)
+{
+ const char *name;
+ void *cookie;
+ int type;
+ unsigned int fields;
+
+ /* NULL nvl is of course invalid. */
+ if (nvl == NULL)
+ return (EINVAL);
+ if (nvlist_error(nvl) != 0)
+ return (nvlist_error(nvl));
+
+#define HAS_NAME 0x01
+#define HAS_OPERATION 0x02
+
+ fields = 0;
+ cookie = NULL;
+ while ((name = nvlist_next(nvl, &type, &cookie)) != NULL) {
+ /* We accept only one 'name' and one 'operation' in nvl. */
+ if (strcmp(name, "name") == 0) {
+ if (type != NV_TYPE_STRING)
+ return (EINVAL);
+ /* Only one 'name' can be present. */
+ if ((fields & HAS_NAME) != 0)
+ return (EINVAL);
+ fields |= HAS_NAME;
+ } else if (strcmp(name, "operation") == 0) {
+ uint64_t operation;
+
+ if (type != NV_TYPE_NUMBER)
+ return (EINVAL);
+ /*
+ * We accept only CAP_SYSCTL_READ and
+ * CAP_SYSCTL_WRITE flags.
+ */
+ operation = nvlist_get_number(nvl, name);
+ if ((operation & ~(CAP_SYSCTL_RDWR)) != 0)
+ return (EINVAL);
+ /* ...but there has to be at least one of them. */
+ if ((operation & (CAP_SYSCTL_RDWR)) == 0)
+ return (EINVAL);
+ /* Only one 'operation' can be present. */
+ if ((fields & HAS_OPERATION) != 0)
+ return (EINVAL);
+ fields |= HAS_OPERATION;
+ } else if (islimit) {
+ /* If this is limit, there can be no other fields. */
+ return (EINVAL);
+ }
+ }
+
+ /* Both fields has to be there. */
+ if (fields != (HAS_NAME | HAS_OPERATION))
+ return (EINVAL);
+
+#undef HAS_OPERATION
+#undef HAS_NAME
+
+ return (0);
+}
+
+static bool
+sysctl_allowed(const nvlist_t *limits, const char *chname, uint64_t choperation)
+{
+ uint64_t operation;
+ const char *name;
+ void *cookie;
+ int type;
+
+ if (limits == NULL)
+ return (true);
+
+ cookie = NULL;
+ while ((name = nvlist_next(limits, &type, &cookie)) != NULL) {
+ PJDLOG_ASSERT(type == NV_TYPE_NUMBER);
+
+ operation = nvlist_get_number(limits, name);
+ if ((operation & choperation) != choperation)
+ continue;
+
+ if ((operation & CAP_SYSCTL_RECURSIVE) == 0) {
+ if (strcmp(name, chname) != 0)
+ continue;
+ } else {
+ size_t namelen;
+
+ namelen = strlen(name);
+ if (strncmp(name, chname, namelen) != 0)
+ continue;
+ if (chname[namelen] != '.' && chname[namelen] != '\0')
+ continue;
+ }
+
+ return (true);
+ }
+
+ return (false);
+}
+
+static int
+sysctl_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
+{
+ const nvlist_t *nvl;
+ const char *name;
+ void *cookie;
+ uint64_t operation;
+ int error, type;
+
+ cookie = NULL;
+ while ((name = nvlist_next(newlimits, &type, &cookie)) != NULL) {
+ if (type != NV_TYPE_NUMBER)
+ return (EINVAL);
+ operation = nvlist_get_number(newlimits, name);
+ if ((operation & ~(CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE)) != 0)
+ return (EINVAL);
+ if ((operation & (CAP_SYSCTL_RDWR | CAP_SYSCTL_RECURSIVE)) == 0)
+ return (EINVAL);
+ if (!sysctl_allowed(oldlimits, name, operation))
+ return (ENOTCAPABLE);
+ }
+
+ return (0);
+}
+
+static int
+sysctl_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
+ nvlist_t *nvlout)
+{
+ const char *name;
+ const void *newp;
+ void *oldp;
+ uint64_t operation;
+ size_t oldlen, newlen;
+ size_t *oldlenp;
+ int error;
+
+ if (strcmp(cmd, "sysctl") != 0)
+ return (EINVAL);
+ error = sysctl_check_one(nvlin, false);
+ if (error != 0)
+ return (error);
+
+ name = nvlist_get_string(nvlin, "name");
+ operation = nvlist_get_number(nvlin, "operation");
+ if (!sysctl_allowed(limits, name, operation))
+ return (ENOTCAPABLE);
+
+ if ((operation & CAP_SYSCTL_WRITE) != 0) {
+ if (!nvlist_exists_binary(nvlin, "newp"))
+ return (EINVAL);
+ newp = nvlist_get_binary(nvlin, "newp", &newlen);
+ PJDLOG_ASSERT(newp != NULL && newlen > 0);
+ } else {
+ newp = NULL;
+ newlen = 0;
+ }
+
+ if ((operation & CAP_SYSCTL_READ) != 0) {
+ if (nvlist_exists_null(nvlin, "justsize")) {
+ oldp = NULL;
+ oldlen = 0;
+ oldlenp = &oldlen;
+ } else {
+ if (!nvlist_exists_number(nvlin, "oldlen"))
+ return (EINVAL);
+ oldlen = (size_t)nvlist_get_number(nvlin, "oldlen");
+ if (oldlen == 0)
+ return (EINVAL);
+ oldp = calloc(1, oldlen);
+ if (oldp == NULL)
+ return (ENOMEM);
+ oldlenp = &oldlen;
+ }
+ } else {
+ oldp = NULL;
+ oldlen = 0;
+ oldlenp = NULL;
+ }
+
+ if (sysctlbyname(name, oldp, oldlenp, newp, newlen) == -1) {
+ error = errno;
+ free(oldp);
+ return (error);
+ }
+
+ if ((operation & CAP_SYSCTL_READ) != 0) {
+ if (nvlist_exists_null(nvlin, "justsize"))
+ nvlist_add_number(nvlout, "oldlen", (uint64_t)oldlen);
+ else
+ nvlist_move_binary(nvlout, "oldp", oldp, oldlen);
+ }
+
+ return (0);
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ return (service_start("system.sysctl", PARENT_FILENO, sysctl_limit,
+ sysctl_command, argc, argv));
+}
diff --git a/libexec/comsat/Makefile b/libexec/comsat/Makefile
new file mode 100644
index 0000000..c501ccc
--- /dev/null
+++ b/libexec/comsat/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+# $FreeBSD$
+
+PROG= comsat
+MAN= comsat.8
+
+.include <bsd.prog.mk>
diff --git a/libexec/comsat/comsat.8 b/libexec/comsat/comsat.8
new file mode 100644
index 0000000..5a58ad0
--- /dev/null
+++ b/libexec/comsat/comsat.8
@@ -0,0 +1,111 @@
+.\" 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. 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
+.\" $FreeBSD$
+.\"
+.Dd January 21, 2010
+.Dt COMSAT 8
+.Os
+.Sh NAME
+.Nm comsat
+.Nd biff server
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility is the server process which receives reports of incoming mail
+and notifies users if they have requested this service.
+The
+.Nm
+utility 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
+.D1 Ar user Ns @ Ns Ar mailbox Ns - Ns Ar offset Ns Op : Ns Ar mailbox-name
+.Pp
+If the
+.Ar user
+specified is logged in to the system and the associated terminal has
+the owner execute bit turned on (by a
+.Dq Nm biff Cm y ) ,
+the
+.Ar 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 Li From ,
+.Dq Li \&To ,
+.Dq Li Date ,
+or
+.Dq Li Subject
+lines are not included in the displayed message.
+.Pp
+If the
+.Ar user
+specified is logged in to the system and the associated terminal has
+the group execute bit turned on (by a
+.Dq Nm biff Cm b ) ,
+two bell characters
+.Tn ( ASCII
+\\007) are printed on the user's terminal.
+.Pp
+If
+.Ar mailbox-name
+omitted, standard mailbox assumed.
+.Sh FILES
+.Bl -tag -width ".Pa /var/mail/user" -compact
+.It Pa /var/run/utx.active
+to find out who is logged on and on what terminals
+.It Pa /var/mail/user
+standard mailbox
+.El
+.Sh SEE ALSO
+.Xr biff 1 ,
+.Xr inetd 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
+.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.
diff --git a/libexec/comsat/comsat.c b/libexec/comsat/comsat.c
new file mode 100644
index 0000000..4420f00
--- /dev/null
+++ b/libexec/comsat/comsat.c
@@ -0,0 +1,284 @@
+/*
+ * 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. 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 const char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)comsat.c 8.1 (Berkeley) 6/4/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#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 <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <paths.h>
+#include <pwd.h>
+#include <termios.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <utmpx.h>
+
+static int debug = 0;
+#define dsyslog if (debug) syslog
+
+#define MAXIDLE 120
+
+static char hostname[MAXHOSTNAMELEN];
+
+static void jkfprintf(FILE *, char[], char[], off_t);
+static void mailfor(char *);
+static void notify(struct utmpx *, char[], off_t, int);
+static void reapchildren(int);
+
+int
+main(int argc __unused, char *argv[] __unused)
+{
+ struct sockaddr_in from;
+ socklen_t fromlen;
+ int cc;
+ char msgbuf[256];
+
+ /* verify proper invocation */
+ fromlen = sizeof(from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0)
+ err(1, "getsockname");
+ 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);
+ }
+ (void)gethostname(hostname, sizeof(hostname));
+ (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;
+ }
+ msgbuf[cc] = '\0';
+ mailfor(msgbuf);
+ sigsetmask(0L);
+ }
+}
+
+static void
+reapchildren(int signo __unused)
+{
+ while (wait3(NULL, WNOHANG, NULL) > 0);
+}
+
+static void
+mailfor(char *name)
+{
+ struct utmpx *utp;
+ char *cp;
+ char *file;
+ off_t offset;
+ int folder;
+ char buf[sizeof(_PATH_MAILDIR) + sizeof(utp->ut_user) + 1];
+ char buf2[sizeof(_PATH_MAILDIR) + sizeof(utp->ut_user) + 1];
+
+ if (!(cp = strchr(name, '@')))
+ return;
+ *cp = '\0';
+ offset = strtoll(cp + 1, NULL, 10);
+ if (!(cp = strchr(cp + 1, ':')))
+ file = name;
+ else
+ file = cp + 1;
+ sprintf(buf, "%s/%.*s", _PATH_MAILDIR, (int)sizeof(utp->ut_user),
+ name);
+ if (*file != '/') {
+ sprintf(buf2, "%s/%.*s", _PATH_MAILDIR,
+ (int)sizeof(utp->ut_user), file);
+ file = buf2;
+ }
+ folder = strcmp(buf, file);
+ setutxent();
+ while ((utp = getutxent()) != NULL)
+ if (utp->ut_type == USER_PROCESS && !strcmp(utp->ut_user, name))
+ notify(utp, file, offset, folder);
+ endutxent();
+}
+
+static const char *cr;
+
+static void
+notify(struct utmpx *utp, char file[], off_t offset, int folder)
+{
+ FILE *tp;
+ struct stat stb;
+ struct termios tio;
+ char tty[20];
+ const char *s = utp->ut_line;
+
+ if (strncmp(s, "pts/", 4) == 0)
+ s += 4;
+ if (strchr(s, '/')) {
+ /* A slash is an attempt to break security... */
+ syslog(LOG_AUTH | LOG_NOTICE, "Unexpected `/' in `%s'",
+ utp->ut_line);
+ return;
+ }
+ (void)snprintf(tty, sizeof(tty), "%s%.*s",
+ _PATH_DEV, (int)sizeof(utp->ut_line), utp->ut_line);
+ if (stat(tty, &stb) == -1 || !(stb.st_mode & (S_IXUSR | S_IXGRP))) {
+ dsyslog(LOG_DEBUG, "%s: wrong mode on %s", utp->ut_user, tty);
+ return;
+ }
+ dsyslog(LOG_DEBUG, "notify %s on %s", utp->ut_user, tty);
+ switch (fork()) {
+ case -1:
+ syslog(LOG_NOTICE, "fork failed (%m)");
+ return;
+ case 0:
+ break;
+ default:
+ return;
+ }
+ if ((tp = fopen(tty, "w")) == NULL) {
+ dsyslog(LOG_ERR, "%s: %s", tty, strerror(errno));
+ _exit(1);
+ }
+ (void)tcgetattr(fileno(tp), &tio);
+ cr = ((tio.c_oflag & (OPOST|ONLCR)) == (OPOST|ONLCR)) ? "\n" : "\n\r";
+ switch (stb.st_mode & (S_IXUSR | S_IXGRP)) {
+ case S_IXUSR:
+ case (S_IXUSR | S_IXGRP):
+ (void)fprintf(tp,
+ "%s\007New mail for %s@%.*s\007 has arrived%s%s%s:%s----%s",
+ cr, utp->ut_user, (int)sizeof(hostname), hostname,
+ folder ? cr : "", folder ? "to " : "", folder ? file : "",
+ cr, cr);
+ jkfprintf(tp, utp->ut_user, file, offset);
+ break;
+ case S_IXGRP:
+ (void)fprintf(tp, "\007");
+ (void)fflush(tp);
+ (void)sleep(1);
+ (void)fprintf(tp, "\007");
+ break;
+ default:
+ break;
+ }
+ (void)fclose(tp);
+ _exit(0);
+}
+
+static void
+jkfprintf(FILE *tp, char user[], char file[], off_t offset)
+{
+ unsigned char *cp, ch;
+ FILE *fi;
+ int linecnt, charcnt, inheader;
+ struct passwd *p;
+ unsigned char line[BUFSIZ];
+
+ /* Set effective uid to user in case mail drop is on nfs */
+ if ((p = getpwnam(user)) != NULL)
+ (void) setuid(p->pw_uid);
+
+ if ((fi = fopen(file, "r")) == NULL)
+ return;
+
+ (void)fseeko(fi, offset, SEEK_CUR);
+ /*
+ * 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) {
+ /* disable upper controls and enable all other
+ 8bit codes due to lack of locale knowledge
+ */
+ if (((ch & 0x80) && ch < 0xA0) ||
+ (!(ch & 0x80) && !isprint(ch) &&
+ !isspace(ch) && ch != '\a' && ch != '\b')
+ ) {
+ if (ch & 0x80) {
+ ch &= ~0x80;
+ (void)fputs("M-", tp);
+ }
+ if (iscntrl(ch)) {
+ 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/dma-mbox-create/Makefile b/libexec/dma-mbox-create/Makefile
new file mode 100644
index 0000000..f0a2341
--- /dev/null
+++ b/libexec/dma-mbox-create/Makefile
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/dma
+
+CFLAGS= -I${.CURDIR}/../../contrib/dma \
+ -DHAVE_REALLOCF -DHAVE_STRLCPY -DHAVE_GETPROGNAME \
+ -DCONF_PATH='"/etc/dma"' \
+ -DLIBEXEC_PATH='"/usr/libexec"' -DDMA_VERSION='"v0.9+"'
+
+MAN=
+
+WARNS= 2
+
+PROG= dma-mbox-create
+BINGRP= mail
+BINMODE= 4554
+
+.include <bsd.prog.mk>
diff --git a/libexec/dma/Makefile b/libexec/dma/Makefile
new file mode 100644
index 0000000..757faf4
--- /dev/null
+++ b/libexec/dma/Makefile
@@ -0,0 +1,40 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/dma
+
+CFLAGS= -I${.CURDIR}/../../contrib/dma \
+ -DHAVE_REALLOCF -DHAVE_STRLCPY -DHAVE_GETPROGNAME \
+ -DCONF_PATH='"/etc/dma"' \
+ -DLIBEXEC_PATH='"/usr/libexec"' -DDMA_VERSION='"v0.9+"' \
+ -DDMA_ROOT_USER='"mailnull"' \
+ -DDMA_GROUP='"mail"'
+DPADD= ${LIBSSL} ${LIBCRYPTO}
+LDADD= -lssl -lcrypto
+
+PROG= dma
+SRCS= aliases_parse.y \
+ aliases_scan.l \
+ base64.c \
+ conf.c \
+ crypto.c \
+ dma.c \
+ dns.c \
+ local.c \
+ mail.c \
+ net.c \
+ spool.c \
+ util.c
+MAN8= dma.8
+YFLAGS+= -i
+CLEANFILES= aliases_parse.i
+
+BINGRP= mail
+BINMODE= 2555
+
+.include <bsd.compiler.mk>
+
+.if ${COMPILER_TYPE} == gcc
+WARNS= 5
+.endif
+
+.include <bsd.prog.mk>
diff --git a/libexec/fingerd/Makefile b/libexec/fingerd/Makefile
new file mode 100644
index 0000000..b6382ad
--- /dev/null
+++ b/libexec/fingerd/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+# $FreeBSD$
+
+PROG= fingerd
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+MAN= fingerd.8
+
+WARNS?= 2
+WFORMAT=0
+
+.include <bsd.prog.mk>
diff --git a/libexec/fingerd/fingerd.8 b/libexec/fingerd/fingerd.8
new file mode 100644
index 0000000..304479f
--- /dev/null
+++ b/libexec/fingerd/fingerd.8
@@ -0,0 +1,177 @@
+.\" 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. 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
+.\" $FreeBSD$
+.\"
+.Dd April 1, 2010
+.Dt FINGERD 8
+.Os
+.Sh NAME
+.Nm fingerd
+.Nd remote user information server
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl k
+.Op Fl s
+.Op Fl l
+.Op Fl p Ar filename
+.Sh DESCRIPTION
+The
+.Nm
+utility uses a simple protocol based on
+.%T RFC1196
+that provides an interface to
+.Xr finger 1
+at several network sites.
+It 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" ,
+thus,
+.Nm
+can also be used to implement other protocols in conjunction with the
+.Fl p
+flag.
+.Pp
+The
+.Nm
+utility is started by
+.Xr inetd 8 ,
+which 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 .
+The
+.Nm
+utility 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 1
+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
+as server program arguments in
+.Pa /etc/inetd.conf :
+.Bl -tag -width indent
+.It Fl d
+Enable debugging mode.
+In debugging mode,
+.Nm
+will not attempt any network-related operations on
+.Va stdin ,
+and it will print the full
+.Nm finger
+command line
+to
+.Va stderr
+before executing it.
+.It Fl k
+Suppress login information.
+See the description of the
+.Fl k
+option in
+.Xr finger 1
+for details.
+.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
+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.
+If
+.Fl p
+is specified,
+.Nm
+will also set the environment variable
+.Ev FINGERD_REMOTE_HOST
+to the name of the host making the request.
+.El
+.Sh SEE ALSO
+.Xr finger 1 ,
+.Xr inetd 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 .
+.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.
+The
+.Nm
+utility 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.
diff --git a/libexec/fingerd/fingerd.c b/libexec/fingerd/fingerd.c
new file mode 100644
index 0000000..be344d4
--- /dev/null
+++ b/libexec/fingerd/fingerd.c
@@ -0,0 +1,231 @@
+/*
+ * 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. 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 const char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)fingerd.c 8.1 (Berkeley) 6/4/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/param.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 <libutil.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pathnames.h"
+
+void logerr(const char *, ...) __printflike(1, 2) __dead2;
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fp;
+ int ch;
+ char *lp;
+ struct sockaddr_storage ss;
+ socklen_t sval;
+ int p[2], debug, kflag, logging, pflag, secure;
+#define ENTRIES 50
+ char **ap, *av[ENTRIES + 1], **comp, line[1024], *prog;
+ char rhost[MAXHOSTNAMELEN];
+
+ prog = _PATH_FINGER;
+ debug = logging = kflag = pflag = secure = 0;
+ openlog("fingerd", LOG_PID | LOG_CONS, LOG_DAEMON);
+ opterr = 0;
+ while ((ch = getopt(argc, argv, "dklp:s")) != -1)
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ case 'k':
+ kflag = 1;
+ break;
+ case 'l':
+ logging = 1;
+ break;
+ case 'p':
+ prog = optarg;
+ pflag = 1;
+ break;
+ case 's':
+ secure = 1;
+ break;
+ case '?':
+ default:
+ logerr("illegal option -- %c", optopt);
+ }
+
+ /*
+ * Enable server-side Transaction TCP.
+ */
+ if (!debug) {
+ 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);
+
+ if (!debug && (logging || pflag)) {
+ sval = sizeof(ss);
+ if (getpeername(0, (struct sockaddr *)&ss, &sval) < 0)
+ logerr("getpeername: %s", strerror(errno));
+ realhostname_sa(rhost, sizeof rhost - 1,
+ (struct sockaddr *)&ss, sval);
+ rhost[sizeof(rhost) - 1] = '\0';
+ if (pflag)
+ setenv("FINGERD_REMOTE_HOST", rhost, 1);
+ }
+
+ if (logging) {
+ char *t;
+ char *end;
+
+ end = memchr(line, 0, sizeof(line));
+ if (end == NULL) {
+ if ((t = malloc(sizeof(line) + 1)) == NULL)
+ logerr("malloc: %s", strerror(errno));
+ memcpy(t, line, sizeof(line));
+ t[sizeof(line)] = 0;
+ } else {
+ if ((t = strdup(line)) == NULL)
+ logerr("strdup: %s", strerror(errno));
+ }
+ for (end = t; *end; end++)
+ if (*end == '\n' || *end == '\r')
+ *end = ' ';
+ syslog(LOG_NOTICE, "query from %s: `%s'", rhost, t);
+ }
+
+ comp = &av[2];
+ av[3] = "--";
+ if (kflag)
+ *comp-- = "-k";
+ for (lp = line, ap = &av[4];;) {
+ *ap = strtok(lp, " \t\r\n");
+ if (!*ap) {
+ if (secure && ap == &av[4]) {
+ 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')) {
+ *comp-- = "-l";
+ }
+ else if (++ap == av + ENTRIES) {
+ *ap = NULL;
+ break;
+ }
+ lp = NULL;
+ }
+
+ if ((lp = strrchr(prog, '/')) != NULL)
+ *comp = ++lp;
+ else
+ *comp = prog;
+ if (pipe(p) < 0)
+ logerr("pipe: %s", strerror(errno));
+
+ if (debug) {
+ fprintf(stderr, "%s", prog);
+ for (ap = comp; *ap != NULL; ++ap)
+ fprintf(stderr, " %s", *ap);
+ fprintf(stderr, "\n");
+ }
+
+ switch(vfork()) {
+ case 0:
+ (void)close(p[0]);
+ if (p[1] != STDOUT_FILENO) {
+ (void)dup2(p[1], STDOUT_FILENO);
+ (void)close(p[1]);
+ }
+ dup2(STDOUT_FILENO, STDERR_FILENO);
+
+ execv(prog, comp);
+ write(STDERR_FILENO, prog, strlen(prog));
+#define MSG ": cannot execute\n"
+ write(STDERR_FILENO, MSG, strlen(MSG));
+#undef MSG
+ _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);
+}
+
+#include <stdarg.h>
+
+void
+logerr(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ (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..0a91541
--- /dev/null
+++ b/libexec/fingerd/pathnames.h
@@ -0,0 +1,34 @@
+/*
+ * 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. 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
+ *
+ * $FreeBSD$
+ */
+
+#define _PATH_FINGER "/usr/bin/finger"
diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile
new file mode 100644
index 0000000..0c7c982
--- /dev/null
+++ b/libexec/ftpd/Makefile
@@ -0,0 +1,40 @@
+# @(#)Makefile 8.2 (Berkeley) 4/4/94
+# $FreeBSD$
+
+.include <src.opts.mk>
+
+PROG= ftpd
+MAN= ftpd.8 ftpchroot.5
+SRCS= ftpd.c ftpcmd.y logwtmp.c popen.c
+
+CFLAGS+=-DSETPROCTITLE -DLOGIN_CAP -DVIRTUAL_HOSTING
+CFLAGS+=-I${.CURDIR}
+YFLAGS=
+WARNS?= 2
+WFORMAT=0
+
+DPADD= ${LIBUTIL} ${LIBCRYPT}
+LDADD= -lutil -lcrypt
+
+# XXX Kluge! Conversation mechanism needs to be fixed.
+DPADD+= ${LIBOPIE} ${LIBMD}
+LDADD+= -lopie -lmd
+
+LSDIR= ../../bin/ls
+.PATH: ${.CURDIR}/${LSDIR}
+SRCS+= ls.c cmp.c print.c util.c
+CFLAGS+=-Dmain=ls_main -I${.CURDIR}/${LSDIR}
+DPADD+= ${LIBM}
+LDADD+= -lm
+
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+=-DINET6
+.endif
+
+.if ${MK_PAM_SUPPORT} != "no"
+CFLAGS+=-DUSE_PAM
+DPADD+= ${LIBPAM}
+LDADD+= ${MINUSLPAM}
+.endif
+
+.include <bsd.prog.mk>
diff --git a/libexec/ftpd/config.h b/libexec/ftpd/config.h
new file mode 100644
index 0000000..8e10bbb
--- /dev/null
+++ b/libexec/ftpd/config.h
@@ -0,0 +1,281 @@
+/* $FreeBSD$ */
+
+
+/* config.h. Generated automatically by configure. */
+/* config.h.in. Generated automatically from configure.in by autoheader. */
+/* $Id: config.h.in,v 1.15 2001/04/28 07:11:46 lukem Exp $ */
+
+
+/* Define if the closedir function returns void instead of int. */
+/* #undef CLOSEDIR_VOID */
+
+/* Define to empty if the keyword does not work. */
+/* #undef const */
+
+/* Define if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Define if your Fortran 77 compiler doesn't accept -c and -o together. */
+/* #undef F77_NO_MINUS_C_MINUS_O */
+
+/* Define to `long' if <sys/types.h> doesn't define. */
+/* #undef off_t */
+
+/* Define to the type of arg1 for select(). */
+/* #undef SELECT_TYPE_ARG1 */
+
+/* Define to the type of args 2, 3 and 4 for select(). */
+/* #undef SELECT_TYPE_ARG234 */
+
+/* Define to the type of arg5 for select(). */
+/* #undef SELECT_TYPE_ARG5 */
+
+/* Define if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define if you can safely include both <sys/time.h> and <time.h>. */
+#define TIME_WITH_SYS_TIME 1
+
+/* Define if the closedir function returns void instead of int. */
+/* #undef VOID_CLOSEDIR */
+
+/* The number of bytes in a off_t. */
+#define SIZEOF_OFF_T 0
+
+/* Define if you have the err function. */
+#define HAVE_ERR 1
+
+/* Define if you have the fgetln function. */
+#define HAVE_FGETLN 1
+
+/* Define if you have the flock function. */
+#define HAVE_FLOCK 1
+
+/* Define if you have the fparseln function. */
+#define HAVE_FPARSELN 1
+
+/* Define if you have the fts_open function. */
+#define HAVE_FTS_OPEN 1
+
+/* Define if you have the getaddrinfo function. */
+#define HAVE_GETADDRINFO 1
+
+/* Define if you have the getgrouplist function. */
+#define HAVE_GETGROUPLIST 1
+
+/* Define if you have the getnameinfo function. */
+#define HAVE_GETNAMEINFO 1
+
+/* Define if you have the getspnam function. */
+/* #undef HAVE_GETSPNAM */
+
+/* Define if you have the getusershell function. */
+#define HAVE_GETUSERSHELL 1
+
+/* Define if you have the inet_net_pton function. */
+#define HAVE_INET_NET_PTON 1
+
+/* Define if you have the inet_ntop function. */
+#define HAVE_INET_NTOP 1
+
+/* Define if you have the inet_pton function. */
+#define HAVE_INET_PTON 1
+
+/* Define if you have the lockf function. */
+#define HAVE_LOCKF 1
+
+/* Define if you have the mkstemp function. */
+#define HAVE_MKSTEMP 1
+
+/* Define if you have the setlogin function. */
+#define HAVE_SETLOGIN 1
+
+/* Define if you have the setproctitle function. */
+#define HAVE_SETPROCTITLE 1
+
+/* Define if you have the sl_init function. */
+#define HAVE_SL_INIT 1
+
+/* Define if you have the snprintf function. */
+#define HAVE_SNPRINTF 1
+
+/* Define if you have the strdup function. */
+#define HAVE_STRDUP 1
+
+/* Define if you have the strerror function. */
+#define HAVE_STRERROR 1
+
+/* Define if you have the strlcat function. */
+#define HAVE_STRLCAT 1
+
+/* Define if you have the strlcpy function. */
+#define HAVE_STRLCPY 1
+
+/* Define if you have the strmode function. */
+#define HAVE_STRMODE 1
+
+/* Define if you have the strsep function. */
+#define HAVE_STRSEP 1
+
+/* Define if you have the strtoll function. */
+#define HAVE_STRTOLL 1
+
+/* Define if you have the user_from_uid function. */
+#define HAVE_USER_FROM_UID 1
+
+/* Define if you have the usleep function. */
+#define HAVE_USLEEP 1
+
+/* Define if you have the vfork function. */
+#define HAVE_VFORK 1
+
+/* Define if you have the vsyslog function. */
+#define HAVE_VSYSLOG 1
+
+/* Define if you have the <arpa/nameser.h> header file. */
+#define HAVE_ARPA_NAMESER_H 1
+
+/* Define if you have the <dirent.h> header file. */
+#define HAVE_DIRENT_H 1
+
+/* Define if you have the <err.h> header file. */
+#define HAVE_ERR_H 1
+
+/* Define if you have the <fts.h> header file. */
+#define HAVE_FTS_H 1
+
+/* Define if you have the <libutil.h> header file. */
+#define HAVE_LIBUTIL_H 1
+
+/* Define if you have the <ndir.h> header file. */
+/* #undef HAVE_NDIR_H */
+
+/* Define if you have the <paths.h> header file. */
+#define HAVE_PATHS_H 1
+
+/* Define if you have the <sys/dir.h> header file. */
+#define HAVE_SYS_DIR_H 1
+
+/* Define if you have the <sys/ndir.h> header file. */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define if you have the <sys/sysmacros.h> header file. */
+/* #undef HAVE_SYS_SYSMACROS_H */
+
+/* Define if you have the <util.h> header file. */
+/* #undef HAVE_UTIL_H */
+
+/* Define if you have the crypt library (-lcrypt). */
+#define HAVE_LIBCRYPT 1
+
+/* Define if you have the nsl library (-lnsl). */
+/* #undef HAVE_LIBNSL */
+
+/* Define if you have the skey library (-lskey). */
+/* #undef HAVE_LIBSKEY */
+
+/* Define if you have the socket library (-lsocket). */
+/* #undef HAVE_LIBSOCKET */
+
+/* Define if you have the util library (-lutil). */
+#define HAVE_LIBUTIL 1
+
+/* Define if your compiler supports `long long' */
+#define HAVE_LONG_LONG 1
+
+/* Define if *printf() uses %qd to print `long long' (otherwise uses %lld) */
+#define HAVE_PRINTF_QD 1
+
+/* Define if in_port_t exists */
+#define HAVE_IN_PORT_T 1
+
+/* Define if struct sockaddr.sa_len exists (implies sockaddr_in.sin_len, etc) */
+#define HAVE_SOCKADDR_SA_LEN 1
+
+/* Define if socklen_t exists */
+#define HAVE_SOCKLEN_T 1
+
+/* Define if AF_INET6 exists in <sys/socket.h> */
+#define HAVE_AF_INET6 1
+
+/* Define if `struct sockaddr_in6' exists in <netinet/in.h> */
+#define HAVE_SOCKADDR_IN6 1
+
+/* Define if `struct addrinfo' exists in <netdb.h> */
+#define HAVE_ADDRINFO 1
+
+/*
+ * Define if <netdb.h> contains AI_NUMERICHOST et al.
+ * Systems which only implement RFC2133 will need this.
+ */
+#define HAVE_RFC2553_NETDB 1
+
+/* Define if `struct direct' has a d_namlen element */
+#define HAVE_D_NAMLEN 1
+
+/* Define if struct passwd.pw_expire exists. */
+#define HAVE_PW_EXPIRE 1
+
+/* Define if GLOB_BRACE, gl_path and gl_match exist in <glob.h> */
+#define HAVE_WORKING_GLOB 1
+
+/* Define if crypt() is declared in <unistd.h> */
+#define HAVE_CRYPT_D 1
+
+/* Define if fclose() is declared in <stdio.h> */
+#define HAVE_FCLOSE_D 1
+
+/* Define if optarg is declared in <stdlib.h> or <unistd.h> */
+#define HAVE_OPTARG_D 1
+
+/* Define if optind is declared in <stdlib.h> or <unistd.h> */
+#define HAVE_OPTIND_D 1
+
+/* Define if optreset exists */
+#define HAVE_OPTRESET 1
+
+/* Define if pclose() is declared in <stdio.h> */
+#define HAVE_PCLOSE_D 1
+
+/* Define if getusershell() is declared in <unistd.h> */
+#define HAVE_GETUSERSHELL_D 1
+
+/* Define if `long long' is supported and sizeof(off_t) >= 8 */
+#define HAVE_QUAD_SUPPORT 1
+
+/* Define if not using in-built /bin/ls code */
+/* #undef NO_INTERNAL_LS */
+
+/* Define if using S/Key */
+/* #undef SKEY */
+
+/*
+ * Define this if compiling with SOCKS (the firewall traversal library).
+ * Also, you must define connect, getsockname, bind, accept, listen, and
+ * select to their R-versions.
+ */
+/* #undef SOCKS */
+/* #undef SOCKS4 */
+/* #undef SOCKS5 */
+/* #undef connect */
+/* #undef getsockname */
+/* #undef bind */
+/* #undef accept */
+/* #undef listen */
+/* #undef select */
+/* #undef dup */
+/* #undef dup2 */
+/* #undef fclose */
+/* #undef gethostbyname */
+/* #undef getpeername */
+/* #undef read */
+/* #undef recv */
+/* #undef recvfrom */
+/* #undef rresvport */
+/* #undef send */
+/* #undef sendto */
+/* #undef shutdown */
+/* #undef write */
+
+/* Define if you have the <arpa/ftp.h> header file. */
+#define HAVE_FTP_NAMES 1
diff --git a/libexec/ftpd/extern.h b/libexec/ftpd/extern.h
new file mode 100644
index 0000000..4f16f7b
--- /dev/null
+++ b/libexec/ftpd/extern.h
@@ -0,0 +1,114 @@
+/*-
+ * 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. 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
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+void blkfree(char **);
+char **copyblk(char **);
+void cwd(char *);
+void delete(char *);
+void dologout(int);
+void fatalerror(char *);
+void ftpd_logwtmp(char *, char *, struct sockaddr *addr);
+int ftpd_pclose(FILE *);
+FILE *ftpd_popen(char *, char *);
+int getline(char *, int, FILE *);
+void lreply(int, const char *, ...) __printflike(2, 3);
+void makedir(char *);
+void nack(char *);
+void pass(char *);
+void passive(void);
+void long_passive(char *, int);
+void perror_reply(int, char *);
+void pwd(void);
+void removedir(char *);
+void renamecmd(char *, char *);
+char *renamefrom(char *);
+void reply(int, const char *, ...) __printflike(2, 3);
+void retrieve(char *, char *);
+void send_file_list(char *);
+#ifdef OLD_SETPROCTITLE
+void setproctitle(const char *, ...);
+#endif
+void statcmd(void);
+void statfilecmd(char *);
+void store(char *, char *, int);
+void upper(char *);
+void user(char *);
+void yyerror(char *);
+int yyparse(void);
+int ls_main(int, char **);
+
+extern int assumeutf8;
+extern char cbuf[];
+extern union sockunion data_dest;
+extern int epsvall;
+extern int form;
+extern int ftpdebug;
+extern int guest;
+extern union sockunion his_addr;
+extern char *homedir;
+extern int hostinfo;
+extern char *hostname;
+extern int maxtimeout;
+extern int logged_in;
+extern int logging;
+extern int noepsv;
+extern int noguestretr;
+extern int noretr;
+extern int paranoid;
+extern struct passwd *pw;
+extern int pdata;
+extern char proctitle[];
+extern int readonly;
+extern off_t restart_point;
+extern int timeout;
+extern char tmpline[];
+extern int type;
+extern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */
+extern int usedefault;
+
+struct sockaddr_in;
+struct sockaddr_in6;
+union sockunion {
+ struct sockinet {
+ u_char si_len;
+ u_char si_family;
+ u_short si_port;
+ } su_si;
+ struct sockaddr_in su_sin;
+ struct sockaddr_in6 su_sin6;
+};
+#define su_len su_si.si_len
+#define su_family su_si.si_family
+#define su_port su_si.si_port
diff --git a/libexec/ftpd/ftpchroot.5 b/libexec/ftpd/ftpchroot.5
new file mode 100644
index 0000000..216ed3d
--- /dev/null
+++ b/libexec/ftpd/ftpchroot.5
@@ -0,0 +1,120 @@
+.\" Copyright (c) 2003 FreeBSD Project
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 26, 2003
+.Dt FTPCHROOT 5
+.Os
+.Sh NAME
+.Nm ftpchroot
+.Nd "list users and groups subject to FTP access restrictions"
+.Sh DESCRIPTION
+The file
+.Nm
+is read by
+.Xr ftpd 8
+at the beginning of an FTP session, after having authenticated the user.
+Each line in
+.Nm
+corresponds to a user or group.
+If a line in
+.Nm
+matches the current user or a group he is a member of,
+access restrictions will be applied to this
+session by changing its root directory with
+.Xr chroot 2
+to that specified on the line or to the user's login directory.
+.Pp
+The order of records in
+.Nm
+is important because the first match will be used.
+Fields on each line are separated by tabs or spaces.
+.Pp
+The first field specifies a user or group name.
+If it is prefixed by an
+.Dq at
+sign,
+.Ql @ ,
+it specifies a group name;
+the line will match each user who is a member of this group.
+As a special case, a single
+.Ql @
+in this field will match any user.
+A username is specified otherwise.
+.Pp
+The optional second field describes the directory for the user
+or each member of the group to be locked up in using
+.Xr chroot 2 .
+Be it omitted, the user's login directory will be used.
+If it is not an absolute pathname, then it will be relative
+to the user's login directory.
+If it contains the
+.Pa /./
+separator,
+.Xr ftpd 8
+will treat its left-hand side as the name of the directory to do
+.Xr chroot 2
+to, and its right-hand side to change the current directory to afterwards.
+.Sh FILES
+.Bl -tag -width ".Pa /etc/ftpchroot" -compact
+.It Pa /etc/ftpchroot
+.El
+.Sh EXAMPLES
+These lines in
+.Nm
+will lock up the user
+.Dq Li webuser
+and each member of the group
+.Dq Li hostee
+in their respective login directories:
+.Bd -literal -offset indent
+webuser
+@hostee
+.Ed
+.Pp
+And this line will tell
+.Xr ftpd 8
+to lock up the user
+.Dq Li joe
+in
+.Pa /var/spool/ftp
+and then to change the current directory to
+.Pa /joe ,
+which is relative to the session's new root:
+.Pp
+.Dl "joe /var/spool/ftp/./joe"
+.Pp
+And finally the following line will lock up every user connecting
+through FTP in his respective
+.Pa ~/public_html ,
+thus lowering possible impact on the system
+from intrinsic insecurity of FTP:
+.Pp
+.Dl "@ public_html"
+.Sh SEE ALSO
+.Xr chroot 2 ,
+.Xr group 5 ,
+.Xr passwd 5 ,
+.Xr ftpd 8
diff --git a/libexec/ftpd/ftpcmd.y b/libexec/ftpd/ftpcmd.y
new file mode 100644
index 0000000..94475ec
--- /dev/null
+++ b/libexec/ftpd/ftpcmd.y
@@ -0,0 +1,1813 @@
+/*
+ * 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. 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
+#if 0
+static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#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 <libutil.h>
+#include <limits.h>
+#include <md5.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "extern.h"
+#include "pathnames.h"
+
+off_t restart_point;
+
+static int cmd_type;
+static int cmd_form;
+static int cmd_bytesz;
+static int state;
+char cbuf[512];
+char *fromname = NULL;
+
+%}
+
+%union {
+ struct {
+ off_t o;
+ int i;
+ } u;
+ char *s;
+}
+
+%token
+ A B C E F I
+ L N P R S T
+ ALL
+
+ 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
+ LPRT LPSV EPRT EPSV FEAT
+
+ UMASK IDLE CHMOD MDFIVE
+
+ LEXERR NOTIMPL
+
+%token <s> STRING
+%token <u> NUMBER
+
+%type <u.i> check_login octal_number byte_size
+%type <u.i> check_login_ro check_login_epsv
+%type <u.i> struct_code mode_code type_code form_code
+%type <s> pathstring pathname password username
+%type <s> ALL NOTIMPL
+
+%start cmd_list
+
+%%
+
+cmd_list
+ : /* empty */
+ | cmd_list cmd
+ {
+ if (fromname)
+ free(fromname);
+ fromname = NULL;
+ restart_point = 0;
+ }
+ | cmd_list rcmd
+ ;
+
+cmd
+ : USER SP username CRLF
+ {
+ user($3);
+ free($3);
+ }
+ | PASS SP password CRLF
+ {
+ pass($3);
+ free($3);
+ }
+ | PASS CRLF
+ {
+ pass("");
+ }
+ | PORT check_login SP host_port CRLF
+ {
+ if (epsvall) {
+ reply(501, "No PORT allowed after EPSV ALL.");
+ goto port_done;
+ }
+ if (!$2)
+ goto port_done;
+ if (port_check("PORT") == 1)
+ goto port_done;
+#ifdef INET6
+ if ((his_addr.su_family != AF_INET6 ||
+ !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
+ /* shoud never happen */
+ usedefault = 1;
+ reply(500, "Invalid address rejected.");
+ goto port_done;
+ }
+ port_check_v6("pcmd");
+#endif
+ port_done:
+ ;
+ }
+ | LPRT check_login SP host_long_port CRLF
+ {
+ if (epsvall) {
+ reply(501, "No LPRT allowed after EPSV ALL.");
+ goto lprt_done;
+ }
+ if (!$2)
+ goto lprt_done;
+ if (port_check("LPRT") == 1)
+ goto lprt_done;
+#ifdef INET6
+ if (his_addr.su_family != AF_INET6) {
+ usedefault = 1;
+ reply(500, "Invalid address rejected.");
+ goto lprt_done;
+ }
+ if (port_check_v6("LPRT") == 1)
+ goto lprt_done;
+#endif
+ lprt_done:
+ ;
+ }
+ | EPRT check_login SP STRING CRLF
+ {
+ char delim;
+ char *tmp = NULL;
+ char *p, *q;
+ char *result[3];
+ struct addrinfo hints;
+ struct addrinfo *res;
+ int i;
+
+ if (epsvall) {
+ reply(501, "No EPRT allowed after EPSV ALL.");
+ goto eprt_done;
+ }
+ if (!$2)
+ goto eprt_done;
+
+ memset(&data_dest, 0, sizeof(data_dest));
+ tmp = strdup($4);
+ if (ftpdebug)
+ syslog(LOG_DEBUG, "%s", tmp);
+ if (!tmp) {
+ fatalerror("not enough core");
+ /*NOTREACHED*/
+ }
+ p = tmp;
+ delim = p[0];
+ p++;
+ memset(result, 0, sizeof(result));
+ for (i = 0; i < 3; i++) {
+ q = strchr(p, delim);
+ if (!q || *q != delim) {
+ parsefail:
+ reply(500,
+ "Invalid argument, rejected.");
+ if (tmp)
+ free(tmp);
+ usedefault = 1;
+ goto eprt_done;
+ }
+ *q++ = '\0';
+ result[i] = p;
+ if (ftpdebug)
+ syslog(LOG_DEBUG, "%d: %s", i, p);
+ p = q;
+ }
+
+ /* some more sanity check */
+ p = result[0];
+ while (*p) {
+ if (!isdigit(*p))
+ goto parsefail;
+ p++;
+ }
+ p = result[2];
+ while (*p) {
+ if (!isdigit(*p))
+ goto parsefail;
+ p++;
+ }
+
+ /* grab address */
+ memset(&hints, 0, sizeof(hints));
+ if (atoi(result[0]) == 1)
+ hints.ai_family = PF_INET;
+#ifdef INET6
+ else if (atoi(result[0]) == 2)
+ hints.ai_family = PF_INET6;
+#endif
+ else
+ hints.ai_family = PF_UNSPEC; /*XXX*/
+ hints.ai_socktype = SOCK_STREAM;
+ i = getaddrinfo(result[1], result[2], &hints, &res);
+ if (i)
+ goto parsefail;
+ memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
+#ifdef INET6
+ if (his_addr.su_family == AF_INET6
+ && data_dest.su_family == AF_INET6) {
+ /* XXX more sanity checks! */
+ data_dest.su_sin6.sin6_scope_id =
+ his_addr.su_sin6.sin6_scope_id;
+ }
+#endif
+ free(tmp);
+ tmp = NULL;
+
+ if (port_check("EPRT") == 1)
+ goto eprt_done;
+#ifdef INET6
+ if (his_addr.su_family != AF_INET6) {
+ usedefault = 1;
+ reply(500, "Invalid address rejected.");
+ goto eprt_done;
+ }
+ if (port_check_v6("EPRT") == 1)
+ goto eprt_done;
+#endif
+ eprt_done:
+ free($4);
+ }
+ | PASV check_login CRLF
+ {
+ if (epsvall)
+ reply(501, "No PASV allowed after EPSV ALL.");
+ else if ($2)
+ passive();
+ }
+ | LPSV check_login CRLF
+ {
+ if (epsvall)
+ reply(501, "No LPSV allowed after EPSV ALL.");
+ else if ($2)
+ long_passive("LPSV", PF_UNSPEC);
+ }
+ | EPSV check_login_epsv SP NUMBER CRLF
+ {
+ if ($2) {
+ int pf;
+ switch ($4.i) {
+ case 1:
+ pf = PF_INET;
+ break;
+#ifdef INET6
+ case 2:
+ pf = PF_INET6;
+ break;
+#endif
+ default:
+ pf = -1; /*junk value*/
+ break;
+ }
+ long_passive("EPSV", pf);
+ }
+ }
+ | EPSV check_login_epsv SP ALL CRLF
+ {
+ if ($2) {
+ reply(200, "EPSV ALL command successful.");
+ epsvall++;
+ }
+ }
+ | EPSV check_login_epsv CRLF
+ {
+ if ($2)
+ long_passive("EPSV", PF_UNSPEC);
+ }
+ | TYPE check_login SP type_code CRLF
+ {
+ if ($2) {
+ 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 CHAR_BIT == 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 /* CHAR_BIT == 8 */
+ UNIMPLEMENTED for CHAR_BIT != 8
+#endif /* CHAR_BIT == 8 */
+ }
+ }
+ }
+ | STRU check_login SP struct_code CRLF
+ {
+ if ($2) {
+ switch ($4) {
+
+ case STRU_F:
+ reply(200, "STRU F accepted.");
+ break;
+
+ default:
+ reply(504, "Unimplemented STRU type.");
+ }
+ }
+ }
+ | MODE check_login SP mode_code CRLF
+ {
+ if ($2) {
+ switch ($4) {
+
+ case MODE_S:
+ reply(200, "MODE S accepted.");
+ break;
+
+ default:
+ reply(502, "Unimplemented MODE type.");
+ }
+ }
+ }
+ | ALLO check_login SP NUMBER CRLF
+ {
+ if ($2) {
+ reply(202, "ALLO command ignored.");
+ }
+ }
+ | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
+ {
+ if ($2) {
+ reply(202, "ALLO command ignored.");
+ }
+ }
+ | RETR check_login SP pathname CRLF
+ {
+ if (noretr || (guest && noguestretr))
+ reply(500, "RETR command disabled.");
+ else if ($2 && $4 != NULL)
+ retrieve(NULL, $4);
+
+ if ($4 != NULL)
+ free($4);
+ }
+ | STOR check_login_ro SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ store($4, "w", 0);
+ if ($4 != NULL)
+ free($4);
+ }
+ | APPE check_login_ro 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 pathstring CRLF
+ {
+ if ($2)
+ send_file_list($4);
+ free($4);
+ }
+ | LIST check_login CRLF
+ {
+ if ($2)
+ retrieve(_PATH_LS " -lgA", "");
+ }
+ | LIST check_login SP pathstring CRLF
+ {
+ if ($2)
+ retrieve(_PATH_LS " -lgA %s", $4);
+ free($4);
+ }
+ | STAT check_login SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ statfilecmd($4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | STAT check_login CRLF
+ {
+ if ($2) {
+ statcmd();
+ }
+ }
+ | DELE check_login_ro SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ delete($4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | RNTO check_login_ro SP pathname CRLF
+ {
+ if ($2 && $4 != NULL) {
+ if (fromname) {
+ renamecmd(fromname, $4);
+ free(fromname);
+ fromname = NULL;
+ } else {
+ reply(503, "Bad sequence of commands.");
+ }
+ }
+ if ($4 != NULL)
+ free($4);
+ }
+ | ABOR check_login CRLF
+ {
+ if ($2)
+ reply(225, "ABOR command successful.");
+ }
+ | CWD check_login CRLF
+ {
+ if ($2) {
+ cwd(homedir);
+ }
+ }
+ | CWD check_login SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ cwd($4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | HELP CRLF
+ {
+ help(cmdtab, NULL);
+ }
+ | 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, NULL);
+ } else
+ help(cmdtab, $3);
+ free($3);
+ }
+ | NOOP CRLF
+ {
+ reply(200, "NOOP command successful.");
+ }
+ | MKD check_login_ro SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ makedir($4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | RMD check_login_ro 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, NULL);
+ }
+ | SITE SP HELP SP STRING CRLF
+ {
+ help(sitetab, $5);
+ free($5);
+ }
+ | SITE SP MDFIVE check_login SP pathname CRLF
+ {
+ char p[64], *q;
+
+ if ($4 && $6) {
+ q = MD5File($6, p);
+ if (q != NULL)
+ reply(200, "MD5(%s) = %s", $6, p);
+ else
+ perror_reply(550, $6);
+ }
+ if ($6)
+ free($6);
+ }
+ | 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_ro SP octal_number SP pathname CRLF
+ {
+ if ($4 && ($8 != NULL)) {
+ if (($6 == -1 ) || ($6 > 0777))
+ reply(501, "Bad mode value.");
+ else if (chmod($8, $6) < 0)
+ perror_reply(550, $8);
+ else
+ reply(200, "CHMOD command successful.");
+ }
+ if ($8 != NULL)
+ free($8);
+ }
+ | SITE SP check_login IDLE CRLF
+ {
+ if ($3)
+ reply(200,
+ "Current IDLE time limit is %d seconds; max %d.",
+ timeout, maxtimeout);
+ }
+ | SITE SP check_login IDLE SP NUMBER CRLF
+ {
+ if ($3) {
+ if ($6.i < 30 || $6.i > maxtimeout) {
+ reply(501,
+ "Maximum IDLE time must be between 30 and %d seconds.",
+ maxtimeout);
+ } else {
+ timeout = $6.i;
+ (void) alarm(timeout);
+ reply(200,
+ "Maximum IDLE time set to %d seconds.",
+ timeout);
+ }
+ }
+ }
+ | STOU check_login_ro SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ store($4, "w", 1);
+ if ($4 != NULL)
+ free($4);
+ }
+ | FEAT CRLF
+ {
+ lreply(211, "Extensions supported:");
+#if 0
+ /* XXX these two keywords are non-standard */
+ printf(" EPRT\r\n");
+ if (!noepsv)
+ printf(" EPSV\r\n");
+#endif
+ printf(" MDTM\r\n");
+ printf(" REST STREAM\r\n");
+ printf(" SIZE\r\n");
+ if (assumeutf8) {
+ /* TVFS requires UTF8, see RFC 3659 */
+ printf(" TVFS\r\n");
+ printf(" UTF8\r\n");
+ }
+ reply(211, "End.");
+ }
+ | SYST check_login CRLF
+ {
+ if ($2) {
+ if (hostinfo)
+#ifdef BSD
+ reply(215, "UNIX Type: L%d Version: BSD-%d",
+ CHAR_BIT, BSD);
+#else /* BSD */
+ reply(215, "UNIX Type: L%d", CHAR_BIT);
+#endif /* BSD */
+ else
+ reply(215, "UNKNOWN Type: L%d", CHAR_BIT);
+ }
+ }
+
+ /*
+ * 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)
+ perror_reply(550, $4);
+ 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,
+ "%04d%02d%02d%02d%02d%02d",
+ 1900 + 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);
+ }
+ | NOTIMPL
+ {
+ nack($1);
+ }
+ | error
+ {
+ yyclearin; /* discard lookahead data */
+ yyerrok; /* clear error condition */
+ state = CMD; /* reset lexer state */
+ }
+ ;
+rcmd
+ : RNFR check_login_ro SP pathname CRLF
+ {
+ restart_point = 0;
+ if ($2 && $4) {
+ if (fromname)
+ free(fromname);
+ fromname = NULL;
+ if (renamefrom($4))
+ fromname = $4;
+ else
+ free($4);
+ } else if ($4) {
+ free($4);
+ }
+ }
+ | REST check_login SP NUMBER CRLF
+ {
+ if ($2) {
+ if (fromname)
+ free(fromname);
+ fromname = NULL;
+ restart_point = $4.o;
+ reply(350, "Restarting at %jd. %s",
+ (intmax_t)restart_point,
+ "Send STORE or RETRIEVE to initiate transfer.");
+ }
+ }
+ ;
+
+username
+ : STRING
+ ;
+
+password
+ : /* empty */
+ {
+ $$ = (char *)calloc(1, sizeof(char));
+ }
+ | STRING
+ ;
+
+byte_size
+ : NUMBER
+ {
+ $$ = $1.i;
+ }
+ ;
+
+host_port
+ : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER
+ {
+ char *a, *p;
+
+ data_dest.su_len = sizeof(struct sockaddr_in);
+ data_dest.su_family = AF_INET;
+ p = (char *)&data_dest.su_sin.sin_port;
+ p[0] = $9.i; p[1] = $11.i;
+ a = (char *)&data_dest.su_sin.sin_addr;
+ a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
+ }
+ ;
+
+host_long_port
+ : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER
+ {
+ char *a, *p;
+
+ memset(&data_dest, 0, sizeof(data_dest));
+ data_dest.su_len = sizeof(struct sockaddr_in6);
+ data_dest.su_family = AF_INET6;
+ p = (char *)&data_dest.su_port;
+ p[0] = $39.i; p[1] = $41.i;
+ a = (char *)&data_dest.su_sin6.sin6_addr;
+ a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
+ a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
+ a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
+ a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
+ if (his_addr.su_family == AF_INET6) {
+ /* XXX more sanity checks! */
+ data_dest.su_sin6.sin6_scope_id =
+ his_addr.su_sin6.sin6_scope_id;
+ }
+ if ($1.i != 6 || $3.i != 16 || $37.i != 2)
+ memset(&data_dest, 0, sizeof(data_dest));
+ }
+ | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER
+ {
+ char *a, *p;
+
+ memset(&data_dest, 0, sizeof(data_dest));
+ data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
+ data_dest.su_family = AF_INET;
+ p = (char *)&data_dest.su_port;
+ p[0] = $15.i; p[1] = $17.i;
+ a = (char *)&data_dest.su_sin.sin_addr;
+ a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
+ if ($1.i != 4 || $3.i != 4 || $13.i != 2)
+ memset(&data_dest, 0, sizeof(data_dest));
+ }
+ ;
+
+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 = CHAR_BIT;
+ }
+ | 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
+ {
+ if (logged_in && $1) {
+ char *p;
+
+ /*
+ * Expand ~user manually since glob(3)
+ * will return the unexpanded pathname
+ * if the corresponding file/directory
+ * doesn't exist yet. Using sole glob(3)
+ * would break natural commands like
+ * MKD ~user/newdir
+ * or
+ * RNTO ~/newfile
+ */
+ if ((p = exptilde($1)) != NULL) {
+ $$ = expglob(p);
+ free(p);
+ } else
+ $$ = NULL;
+ 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.i;
+ 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 */
+ {
+ $$ = check_login1();
+ }
+ ;
+
+check_login_epsv
+ : /* empty */
+ {
+ if (noepsv) {
+ reply(500, "EPSV command disabled.");
+ $$ = 0;
+ }
+ else
+ $$ = check_login1();
+ }
+ ;
+
+check_login_ro
+ : /* empty */
+ {
+ if (readonly) {
+ reply(550, "Permission denied.");
+ $$ = 0;
+ }
+ else
+ $$ = check_login1();
+ }
+ ;
+
+%%
+
+#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 /* optional SP then optional STRING */
+#define ZSTR2 6 /* optional STRING after SP */
+#define SITECMD 7 /* SITE command */
+#define NSTR 8 /* Number followed by a string */
+
+#define MAXGLOBARGS 1000
+
+#define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */
+
+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, b5" },
+ { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
+ { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
+ { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
+ { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
+ { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
+ { "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)" },
+ { "FEAT", FEAT, ARGS, 1, "(get extended features)" },
+ { "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[] = {
+ { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" },
+ { "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(char *);
+static char *expglob(char *);
+static char *exptilde(char *);
+static void help(struct tab *, char *);
+static struct tab *
+ lookup(struct tab *, char *);
+static int port_check(const char *);
+#ifdef INET6
+static int port_check_v6(const char *);
+#endif
+static void sizecmd(char *);
+static void toolong(int);
+#ifdef INET6
+static void v4map_data_dest(void);
+#endif
+static int yylex(void);
+
+static struct tab *
+lookup(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.
+ */
+int
+getline(char *s, int n, FILE *iop)
+{
+ int c;
+ register char *cs;
+ sigset_t sset, osset;
+
+ 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 (ftpdebug)
+ syslog(LOG_DEBUG, "command: %s", s);
+ tmpline[0] = '\0';
+ return(0);
+ }
+ if (c == 0)
+ tmpline[0] = '\0';
+ }
+ /* SIGURG would interrupt stdio if not blocked during the read loop */
+ sigemptyset(&sset);
+ sigaddset(&sset, SIGURG);
+ sigprocmask(SIG_BLOCK, &sset, &osset);
+ while ((c = getc(iop)) != EOF) {
+ c &= 0377;
+ if (c == IAC) {
+ if ((c = getc(iop)) == EOF)
+ goto got_eof;
+ c &= 0377;
+ switch (c) {
+ case WILL:
+ case WONT:
+ if ((c = getc(iop)) == EOF)
+ goto got_eof;
+ printf("%c%c%c", IAC, DONT, 0377&c);
+ (void) fflush(stdout);
+ continue;
+ case DO:
+ case DONT:
+ if ((c = getc(iop)) == EOF)
+ goto got_eof;
+ printf("%c%c%c", IAC, WONT, 0377&c);
+ (void) fflush(stdout);
+ continue;
+ case IAC:
+ break;
+ default:
+ continue; /* ignore command */
+ }
+ }
+ *cs++ = c;
+ if (--n <= 0) {
+ /*
+ * If command doesn't fit into buffer, discard the
+ * rest of the command and indicate truncation.
+ * This prevents the command to be split up into
+ * multiple commands.
+ */
+ while (c != '\n' && (c = getc(iop)) != EOF)
+ ;
+ return (-2);
+ }
+ if (c == '\n')
+ break;
+ }
+got_eof:
+ sigprocmask(SIG_SETMASK, &osset, NULL);
+ if (c == EOF && cs == s)
+ return (-1);
+ *cs++ = '\0';
+ if (ftpdebug) {
+ 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 (0);
+}
+
+static void
+toolong(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(void)
+{
+ static int cpos;
+ char *cp, *cp2;
+ struct tab *p;
+ int n;
+ char c;
+
+ for (;;) {
+ switch (state) {
+
+ case CMD:
+ (void) signal(SIGALRM, toolong);
+ (void) alarm(timeout);
+ n = getline(cbuf, sizeof(cbuf)-1, stdin);
+ if (n == -1) {
+ reply(221, "You could at least say goodbye.");
+ dologout(0);
+ } else if (n == -2) {
+ reply(500, "Command too long.");
+ (void) alarm(0);
+ continue;
+ }
+ (void) alarm(0);
+#ifdef SETPROCTITLE
+ if (strncasecmp(cbuf, "PASS", 4) != 0)
+ 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) {
+ yylval.s = p->name;
+ if (!p->implemented)
+ return (NOTIMPL); /* state remains CMD */
+ state = p->state;
+ 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) {
+ yylval.s = p->name;
+ if (!p->implemented) {
+ state = CMD;
+ return (NOTIMPL);
+ }
+ state = p->state;
+ return (p->token);
+ }
+ state = CMD;
+ break;
+
+ case ZSTR1:
+ case OSTR:
+ if (cbuf[cpos] == '\n') {
+ state = CMD;
+ return (CRLF);
+ }
+ /* FALLTHROUGH */
+
+ case STR1:
+ dostr1:
+ if (cbuf[cpos] == ' ') {
+ cpos++;
+ state = state == OSTR ? STR2 : state+1;
+ 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.u.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.u.i = atoi(cp);
+ yylval.u.o = strtoull(cp, NULL, 10);
+ cbuf[cpos] = c;
+ return (NUMBER);
+ }
+ if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
+ && !isalnum(cbuf[cpos + 3])) {
+ cpos += 3;
+ return ALL;
+ }
+ 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:
+ fatalerror("Unknown state in scanner.");
+ }
+ state = CMD;
+ return (LEXERR);
+ }
+}
+
+void
+upper(char *s)
+{
+ while (*s != '\0') {
+ if (islower(*s))
+ *s = toupper(*s);
+ s++;
+ }
+}
+
+static char *
+copy(char *s)
+{
+ char *p;
+
+ p = malloc(strlen(s) + 1);
+ if (p == NULL)
+ fatalerror("Ran out of memory.");
+ (void) strcpy(p, s);
+ return (p);
+}
+
+static void
+help(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);
+ if (hostinfo)
+ reply(214, "Direct comments to ftp-bugs@%s.", hostname);
+ else
+ reply(214, "End.");
+ return;
+ }
+ upper(s);
+ c = lookup(ctab, s);
+ if (c == NULL) {
+ 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(char *filename)
+{
+ switch (type) {
+ case TYPE_L:
+ case TYPE_I: {
+ struct stat stbuf;
+ if (stat(filename, &stbuf) < 0)
+ perror_reply(550, filename);
+ else if (!S_ISREG(stbuf.st_mode))
+ reply(550, "%s: not a plain file.", filename);
+ else
+ reply(213, "%jd", (intmax_t)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) {
+ perror_reply(550, filename);
+ (void) fclose(fin);
+ return;
+ } else if (!S_ISREG(stbuf.st_mode)) {
+ reply(550, "%s: not a plain file.", filename);
+ (void) fclose(fin);
+ return;
+ } else if (stbuf.st_size > MAXASIZE) {
+ reply(550, "%s: too large for type A SIZE.", 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, "%jd", (intmax_t)count);
+ break; }
+ default:
+ reply(504, "SIZE not implemented for type %s.",
+ typenames[type]);
+ }
+}
+
+/* Return 1, if port check is done. Return 0, if not yet. */
+static int
+port_check(const char *pcmd)
+{
+ if (his_addr.su_family == AF_INET) {
+ if (data_dest.su_family != AF_INET) {
+ usedefault = 1;
+ reply(500, "Invalid address rejected.");
+ return 1;
+ }
+ if (paranoid &&
+ ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
+ memcmp(&data_dest.su_sin.sin_addr,
+ &his_addr.su_sin.sin_addr,
+ sizeof(data_dest.su_sin.sin_addr)))) {
+ usedefault = 1;
+ reply(500, "Illegal PORT range rejected.");
+ } else {
+ usedefault = 0;
+ if (pdata >= 0) {
+ (void) close(pdata);
+ pdata = -1;
+ }
+ reply(200, "%s command successful.", pcmd);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static int
+check_login1(void)
+{
+ if (logged_in)
+ return 1;
+ else {
+ reply(530, "Please login with USER and PASS.");
+ return 0;
+ }
+}
+
+/*
+ * Replace leading "~user" in a pathname by the user's login directory.
+ * Returned string will be in a freshly malloced buffer unless it's NULL.
+ */
+static char *
+exptilde(char *s)
+{
+ char *p, *q;
+ char *path, *user;
+ struct passwd *ppw;
+
+ if ((p = strdup(s)) == NULL)
+ return (NULL);
+ if (*p != '~')
+ return (p);
+
+ user = p + 1; /* skip tilde */
+ if ((path = strchr(p, '/')) != NULL)
+ *(path++) = '\0'; /* separate ~user from the rest of path */
+ if (*user == '\0') /* no user specified, use the current user */
+ user = pw->pw_name;
+ /* read passwd even for the current user since we may be chrooted */
+ if ((ppw = getpwnam(user)) != NULL) {
+ /* user found, substitute login directory for ~user */
+ if (path)
+ asprintf(&q, "%s/%s", ppw->pw_dir, path);
+ else
+ q = strdup(ppw->pw_dir);
+ free(p);
+ p = q;
+ } else {
+ /* user not found, undo the damage */
+ if (path)
+ path[-1] = '/';
+ }
+ return (p);
+}
+
+/*
+ * Expand glob(3) patterns possibly present in a pathname.
+ * Avoid expanding to a pathname including '\r' or '\n' in order to
+ * not disrupt the FTP protocol.
+ * The expansion found must be unique.
+ * Return the result as a malloced string, or NULL if an error occurred.
+ *
+ * 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.
+ */
+static char *
+expglob(char *s)
+{
+ char *p, **pp, *rval;
+ int flags = GLOB_BRACE | GLOB_NOCHECK;
+ int n;
+ glob_t gl;
+
+ memset(&gl, 0, sizeof(gl));
+ flags |= GLOB_LIMIT;
+ gl.gl_matchc = MAXGLOBARGS;
+ if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) {
+ for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++)
+ if (*(*pp + strcspn(*pp, "\r\n")) == '\0') {
+ p = *pp;
+ n++;
+ }
+ if (n == 0)
+ rval = strdup(s);
+ else if (n == 1)
+ rval = strdup(p);
+ else {
+ reply(550, "Wildcard is ambiguous.");
+ rval = NULL;
+ }
+ } else {
+ reply(550, "Wildcard expansion error.");
+ rval = NULL;
+ }
+ globfree(&gl);
+ return (rval);
+}
+
+#ifdef INET6
+/* Return 1, if port check is done. Return 0, if not yet. */
+static int
+port_check_v6(const char *pcmd)
+{
+ if (his_addr.su_family == AF_INET6) {
+ if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
+ /* Convert data_dest into v4 mapped sockaddr.*/
+ v4map_data_dest();
+ if (data_dest.su_family != AF_INET6) {
+ usedefault = 1;
+ reply(500, "Invalid address rejected.");
+ return 1;
+ }
+ if (paranoid &&
+ ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
+ memcmp(&data_dest.su_sin6.sin6_addr,
+ &his_addr.su_sin6.sin6_addr,
+ sizeof(data_dest.su_sin6.sin6_addr)))) {
+ usedefault = 1;
+ reply(500, "Illegal PORT range rejected.");
+ } else {
+ usedefault = 0;
+ if (pdata >= 0) {
+ (void) close(pdata);
+ pdata = -1;
+ }
+ reply(200, "%s command successful.", pcmd);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static void
+v4map_data_dest(void)
+{
+ struct in_addr savedaddr;
+ int savedport;
+
+ if (data_dest.su_family != AF_INET) {
+ usedefault = 1;
+ reply(500, "Invalid address rejected.");
+ return;
+ }
+
+ savedaddr = data_dest.su_sin.sin_addr;
+ savedport = data_dest.su_port;
+
+ memset(&data_dest, 0, sizeof(data_dest));
+ data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
+ data_dest.su_sin6.sin6_family = AF_INET6;
+ data_dest.su_sin6.sin6_port = savedport;
+ memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
+ memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
+ (caddr_t)&savedaddr, sizeof(savedaddr));
+}
+#endif
diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8
new file mode 100644
index 0000000..50565e9
--- /dev/null
+++ b/libexec/ftpd/ftpd.8
@@ -0,0 +1,574 @@
+.\" 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. 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
+.\" $FreeBSD$
+.\"
+.Dd January 21, 2010
+.Dt FTPD 8
+.Os
+.Sh NAME
+.Nm ftpd
+.Nd Internet File Transfer Protocol server
+.Sh SYNOPSIS
+.Nm
+.Op Fl 468ADdEhMmOoRrSUvW
+.Op Fl l Op Fl l
+.Op Fl a Ar address
+.Op Fl P Ar port
+.Op Fl p Ar file
+.Op Fl T Ar maxtimeout
+.Op Fl t Ar timeout
+.Op Fl u Ar umask
+.Sh DESCRIPTION
+The
+.Nm
+utility is the
+Internet File Transfer Protocol
+server process.
+The server uses the
+.Tn TCP
+protocol
+and listens at the port specified with the
+.Fl P
+option or in the
+.Dq ftp
+service specification; see
+.Xr services 5 .
+.Pp
+Available options:
+.Bl -tag -width indent
+.It Fl 4
+When
+.Fl D
+is specified, accept connections via
+.Dv AF_INET
+socket.
+.It Fl 6
+When
+.Fl D
+is specified, accept connections via
+.Dv AF_INET6
+socket.
+.It Fl 8
+Enable transparent UTF-8 mode.
+RFC\ 2640 compliant clients will be told that the character encoding
+used by the server is UTF-8, which is the only effect of the option.
+.Pp
+This option does not enable any encoding conversion for server file names;
+it implies instead that the names of files on the server are encoded
+in UTF-8.
+As for files uploaded via FTP, it is the duty of the RFC\ 2640 compliant
+client to convert their names from the client's local encoding to UTF-8.
+FTP command names and own
+.Nm
+messages are always encoded in ASCII, which is a subset of UTF-8.
+Hence no need for server-side conversion at all.
+.It Fl A
+Allow only anonymous ftp access.
+.It Fl a
+When
+.Fl D
+is specified, accept connections only on the specified
+.Ar address .
+.It Fl D
+With this option set,
+.Nm
+will detach and become a daemon, accepting connections on the FTP port and
+forking children processes to handle them.
+This is lower overhead than starting
+.Nm
+from
+.Xr inetd 8
+and is thus useful on busy servers to reduce load.
+.It Fl d
+Debugging information is written to the syslog using
+.Dv LOG_FTP .
+.It Fl E
+Disable the EPSV command.
+This is useful for servers behind older firewalls.
+.It Fl h
+Disable printing host-specific information, such as the
+server software version or hostname, in server messages.
+.It Fl l
+Each successful and failed
+.Xr ftp 1
+session is logged using syslog with a facility of
+.Dv 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.
+By default,
+.Xr syslogd 8
+logs these to
+.Pa /var/log/xferlog .
+.It Fl M
+Prevent anonymous users from creating directories.
+.It Fl m
+Permit anonymous users to overwrite or modify
+existing files if allowed by file system permissions.
+By default, anonymous users cannot modify existing files;
+in particular, files to upload will be created under a unique name.
+.It Fl O
+Put server in write-only mode for anonymous users only.
+RETR is disabled for anonymous users, preventing anonymous downloads.
+This has no effect if
+.Fl o
+is also specified.
+.It Fl o
+Put server in write-only mode.
+RETR is disabled, preventing downloads.
+.It Fl P
+When
+.Fl D
+is specified, accept connections at
+.Ar port ,
+specified as a numeric value or service name, instead of at the default
+.Dq ftp
+port.
+.It Fl p
+When
+.Fl D
+is specified, write the daemon's process ID to
+.Ar file
+instead of the default pid file,
+.Pa /var/run/ftpd.pid .
+.It Fl R
+With this option set,
+.Nm
+will revert to historical behavior with regard to security checks on
+user operations and restrictions on PORT requests.
+Currently,
+.Nm
+will only honor PORT commands directed to unprivileged ports on the
+remote user's host (which violates the FTP protocol specification but
+closes some security holes).
+.It Fl r
+Put server in read-only mode.
+All commands which may modify the local file system are disabled.
+.It Fl S
+With this option set,
+.Nm
+logs all anonymous file downloads to the file
+.Pa /var/log/ftpd
+when this file exists.
+.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).
+.It Fl U
+This option instructs ftpd to use data ports in the range of
+.Dv IP_PORTRANGE_DEFAULT
+instead of in the range of
+.Dv IP_PORTRANGE_HIGH .
+Such a change may be useful for some specific firewall configurations;
+see
+.Xr ip 4
+for more information.
+.Pp
+Note that option is a virtual no-op in
+.Fx 5.0
+and above; both port
+ranges are identical by default.
+.It Fl u
+The default file creation mode mask is set to
+.Ar umask ,
+which is expected to be an octal numeric value.
+Refer to
+.Xr umask 2
+for details.
+This option may be overridden by
+.Xr login.conf 5 .
+.It Fl v
+A synonym for
+.Fl d .
+.It Fl W
+Do not log FTP sessions to the user accounting database.
+.El
+.Pp
+The file
+.Pa /var/run/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.
+Note the motd file used is the one
+relative to the login environment.
+This means the one in
+.Pa ~ftp/etc
+in the anonymous user's case.
+.Pp
+The ftp server currently supports the following ftp requests.
+The case of the requests is ignored.
+Requests marked [RW] are
+disabled if
+.Fl r
+is specified.
+.Bl -column "Request" -offset indent
+.It Sy Request Ta Sy "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 [RW]"
+.It CDUP Ta "change to parent of current working directory"
+.It CWD Ta "change working directory"
+.It DELE Ta "delete a file [RW]"
+.It EPRT Ta "specify data connection port, multiprotocol"
+.It EPSV Ta "prepare for server-to-server transfer, multiprotocol"
+.It FEAT Ta "give information on extended features of server"
+.It HELP Ta "give help information"
+.It LIST Ta "give list files in a directory" Pq Dq Li "ls -lgA"
+.It LPRT Ta "specify data connection port, multiprotocol"
+.It LPSV Ta "prepare for server-to-server transfer, multiprotocol"
+.It MDTM Ta "show last modification time of file"
+.It MKD Ta "make a directory [RW]"
+.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 [RW]"
+.It RNFR Ta "specify rename-from file name [RW]"
+.It RNTO Ta "specify rename-to file name [RW]"
+.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 [RW]"
+.It STOU Ta "store a file with a unique name [RW]"
+.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) [RW]"
+.It XPWD Ta "print the current working directory (deprecated)"
+.It XRMD Ta "remove a directory (deprecated) [RW]"
+.El
+.Pp
+The following non-standard or
+.Ux
+specific commands are supported
+by the
+SITE request.
+.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 [RW], e.g. ``SITE CHMOD 755 filename''"
+.It MD5 Ta "report the files MD5 checksum, e.g. ``SITE MD5 filename''"
+.It HELP Ta give help information
+.El
+.Pp
+Note: SITE requests are disabled in case of anonymous logins.
+.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.
+To avoid possible denial-of-service attacks, SIZE requests against
+files larger than 10240 bytes will be denied if the current transfer
+type is ASCII.
+.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
+The
+.Nm
+utility interprets file names according to the
+.Dq globbing
+conventions used by
+.Xr csh 1 .
+This allows users to utilize the metacharacters
+.Dq Li \&*?[]{}~ .
+.Pp
+The
+.Nm
+utility authenticates users according to six rules.
+.Bl -enum -offset indent
+.It
+The login name must be in the password data base
+and not have a null password.
+In this case a password must be provided by the client before any
+file operations may be performed.
+If the user has an OPIE key, the response from a successful USER
+command will include an OPIE challenge.
+The client may choose to respond with a PASS command giving either
+a standard password or an OPIE one-time password.
+The server will automatically determine which type of
+password it has been given and attempt to authenticate accordingly.
+See
+.Xr opie 4
+for more information on OPIE authentication.
+.It
+The login name must not appear in the file
+.Pa /etc/ftpusers .
+.It
+The login name must not be a member of a group specified in the file
+.Pa /etc/ftpusers .
+Entries in this file interpreted as group names are prefixed by an "at"
+.Ql \&@
+sign.
+.It
+The user must have a standard shell returned by
+.Xr getusershell 3 .
+.It
+If the user name appears in the file
+.Pa /etc/ftpchroot ,
+or the user is a member of a group with a group entry in this file,
+i.e., one prefixed with
+.Ql \&@ ,
+the session's root will be changed to the directory specified
+in this file or to the user's login directory by
+.Xr chroot 2
+as for an
+.Dq anonymous
+or
+.Dq ftp
+account (see next item).
+See
+.Xr ftpchroot 5
+for a detailed description of the format of this file.
+This facility may also be triggered by enabling the boolean "ftp-chroot"
+capability in
+.Xr login.conf 5 .
+However, the user must still supply a password.
+This feature is intended as a compromise between a fully anonymous
+account and a fully privileged account.
+The account should also be set up as for an anonymous account.
+.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
+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.
+As a special case if the
+.Dq ftp
+user's home directory pathname contains the
+.Pa /./
+separator,
+.Nm
+uses its left-hand side as the name of the directory to do
+.Xr chroot 2
+to, and its right-hand side to change the current directory to afterwards.
+A typical example for this case would be
+.Pa /usr/local/ftp/./pub .
+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/etc
+Make this directory owned by
+.Dq root
+and unwritable by anyone (mode 555).
+The files pwd.db (see
+.Xr passwd 5 )
+and
+.Xr group 5
+must be present for the
+.Xr ls 1
+command to be able to produce owner names rather than numbers.
+The password field in
+.Xr passwd 5
+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
+This directory and the subdirectories beneath it should be owned
+by the users and groups responsible for placing files in them,
+and be writable only by them (mode 755 or 775).
+They should
+.Em not
+be owned or writable by
+.Dq ftp
+or its group, otherwise guest users
+can fill the drive with unwanted files.
+.El
+.Pp
+If the system has multiple IP addresses,
+.Nm
+supports the idea of virtual hosts, which provides the ability to
+define multiple anonymous ftp areas, each one allocated to a different
+internet address.
+The file
+.Pa /etc/ftphosts
+contains information pertaining to each of the virtual hosts.
+Each host is defined on its own line which contains a number of
+fields separated by whitespace:
+.Bl -tag -offset indent -width hostname
+.It hostname
+Contains the hostname or IP address of the virtual host.
+.It user
+Contains a user record in the system password file.
+As with normal anonymous ftp, this user's access uid, gid and group
+memberships determine file access to the anonymous ftp area.
+The anonymous ftp area (to which any user is chrooted on login)
+is determined by the home directory defined for the account.
+User id and group for any ftp account may be the same as for the
+standard ftp user.
+.It statfile
+File to which all file transfers are logged, which
+defaults to
+.Pa /var/log/ftpd .
+.It welcome
+This file is the welcome message displayed before the server ready
+prompt.
+It defaults to
+.Pa /etc/ftpwelcome .
+.It motd
+This file is displayed after the user logs in.
+It defaults to
+.Pa /etc/ftpmotd .
+.El
+.Pp
+Lines beginning with a '#' are ignored and can be used to include
+comments.
+.Pp
+Defining a virtual host for the primary IP address or hostname
+changes the default for ftp logins to that address.
+The 'user', 'statfile', 'welcome' and 'motd' fields may be left
+blank, or a single hyphen '-' used to indicate that the default
+value is to be used.
+.Pp
+As with any anonymous login configuration, due care must be given
+to setup and maintenance to guard against security related problems.
+.Pp
+The
+.Nm
+utility has internal support for handling remote requests to list
+files, and will not execute
+.Pa /bin/ls
+in either a chrooted or non-chrooted environment.
+The
+.Pa ~/bin/ls
+executable need not be placed into the chrooted tree, nor need the
+.Pa ~/bin
+directory exist.
+.Sh FILES
+.Bl -tag -width ".Pa /var/run/ftpd.pid" -compact
+.It Pa /etc/ftpusers
+List of unwelcome/restricted users.
+.It Pa /etc/ftpchroot
+List of normal users who should be chroot'd.
+.It Pa /etc/ftphosts
+Virtual hosting configuration file.
+.It Pa /etc/ftpwelcome
+Welcome notice.
+.It Pa /etc/ftpmotd
+Welcome notice after login.
+.It Pa /var/run/ftpd.pid
+Default pid file for daemon mode.
+.It Pa /var/run/nologin
+Displayed and access refused.
+.It Pa /var/log/ftpd
+Log file for anonymous transfers.
+.It Pa /var/log/xferlog
+Default place for session logs.
+.El
+.Sh SEE ALSO
+.Xr ftp 1 ,
+.Xr umask 2 ,
+.Xr getusershell 3 ,
+.Xr opie 4 ,
+.Xr ftpchroot 5 ,
+.Xr login.conf 5 ,
+.Xr inetd 8 ,
+.Xr syslogd 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
+IPv6 support was added in WIDE Hydrangea IPv6 stack kit.
+.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.
diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c
new file mode 100644
index 0000000..b3c78f6
--- /dev/null
+++ b/libexec/ftpd/ftpd.c
@@ -0,0 +1,3490 @@
+/*
+ * 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. 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 0
+#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 */
+#endif
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)ftpd.c 8.4 (Berkeley) 4/16/94";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * FTP server.
+ */
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.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 <grp.h>
+#include <opie.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#include <libutil.h>
+#ifdef LOGIN_CAP
+#include <login_cap.h>
+#endif
+
+#ifdef USE_PAM
+#include <security/pam_appl.h>
+#endif
+
+#include "pathnames.h"
+#include "extern.h"
+
+#include <stdarg.h>
+
+static char version[] = "Version 6.00LS";
+#undef main
+
+union sockunion ctrl_addr;
+union sockunion data_source;
+union sockunion data_dest;
+union sockunion his_addr;
+union sockunion pasv_addr;
+
+int daemon_mode;
+int data;
+int dataport;
+int hostinfo = 1; /* print host-specific info in messages */
+int logged_in;
+struct passwd *pw;
+char *homedir;
+int ftpdebug;
+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 paranoid = 1; /* be extra careful about security */
+int anon_only = 0; /* Only anonymous ftp allowed */
+int assumeutf8 = 0; /* Assume that server file names are in UTF-8 */
+int guest;
+int dochroot;
+char *chrootdir;
+int dowtmp = 1;
+int stats;
+int statfd = -1;
+int type;
+int form;
+int stru; /* avoid C keyword */
+int mode;
+int usedefault = 1; /* for data transfers */
+int pdata = -1; /* for passive mode */
+int readonly = 0; /* Server is in readonly mode. */
+int noepsv = 0; /* EPSV command is disabled. */
+int noretr = 0; /* RETR command is disabled. */
+int noguestretr = 0; /* RETR command is disabled for anon users. */
+int noguestmkd = 0; /* MKD command is disabled for anon users. */
+int noguestmod = 1; /* anon users may not modify existing files. */
+
+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;
+int epsvall = 0;
+
+#ifdef VIRTUAL_HOSTING
+char *ftpuser;
+
+static struct ftphost {
+ struct ftphost *next;
+ struct addrinfo *hostinfo;
+ char *hostname;
+ char *anonuser;
+ char *statfile;
+ char *welcome;
+ char *loginmsg;
+} *thishost, *firsthost;
+
+#endif
+char remotehost[NI_MAXHOST];
+char *ident = NULL;
+
+static char wtmpid[20];
+
+#ifdef USE_PAM
+static int auth_pam(struct passwd**, const char*);
+pam_handle_t *pamh = NULL;
+#endif
+
+static struct opie opiedata;
+static char opieprompt[OPIE_CHALLENGE_MAX+1];
+static int pwok;
+
+char *pid_file = NULL; /* means default location to pidfile(3) */
+
+/*
+ * Limit number of pathnames that glob can return.
+ * A limit of 0 indicates the number of pathnames is unlimited.
+ */
+#define MAXGLOBARGS 16384
+#
+
+/*
+ * Timeout intervals for retrying connections
+ * to hosts that don't accept PORT cmds. This
+ * is a kludge, but given the problems with TCP...
+ */
+#define SWAITMAX 90 /* wait at most 90 seconds */
+#define SWAITINT 5 /* interval between retries */
+
+int swaitmax = SWAITMAX;
+int swaitint = SWAITINT;
+
+#ifdef SETPROCTITLE
+#ifdef OLD_SETPROCTITLE
+char **Argv = NULL; /* pointer to argument vector */
+char *LastArgv = NULL; /* end of argv */
+#endif /* OLD_SETPROCTITLE */
+char proctitle[LINE_MAX]; /* initial part of title */
+#endif /* SETPROCTITLE */
+
+#define LOGCMD(cmd, file) logcmd((cmd), (file), NULL, -1)
+#define LOGCMD2(cmd, file1, file2) logcmd((cmd), (file1), (file2), -1)
+#define LOGBYTES(cmd, file, cnt) logcmd((cmd), (file), NULL, (cnt))
+
+static volatile sig_atomic_t recvurg;
+static int transflag; /* NB: for debugging only */
+
+#define STARTXFER flagxfer(1)
+#define ENDXFER flagxfer(0)
+
+#define START_UNSAFE maskurg(1)
+#define END_UNSAFE maskurg(0)
+
+/* It's OK to put an `else' clause after this macro. */
+#define CHECKOOB(action) \
+ if (recvurg) { \
+ recvurg = 0; \
+ if (myoob()) { \
+ ENDXFER; \
+ action; \
+ } \
+ }
+
+#ifdef VIRTUAL_HOSTING
+static void inithosts(int);
+static void selecthost(union sockunion *);
+#endif
+static void ack(char *);
+static void sigurg(int);
+static void maskurg(int);
+static void flagxfer(int);
+static int myoob(void);
+static int checkuser(char *, char *, int, char **, int *);
+static FILE *dataconn(char *, off_t, char *);
+static void dolog(struct sockaddr *);
+static void end_login(void);
+static FILE *getdatasock(char *);
+static int guniquefd(char *, char **);
+static void lostconn(int);
+static void sigquit(int);
+static int receive_data(FILE *, FILE *);
+static int send_data(FILE *, FILE *, size_t, off_t, int);
+static struct passwd *
+ sgetpwnam(char *);
+static char *sgetsave(char *);
+static void reapchild(int);
+static void appendf(char **, char *, ...) __printflike(2, 3);
+static void logcmd(char *, char *, char *, off_t);
+static void logxfer(char *, off_t, time_t);
+static char *doublequote(char *);
+static int *socksetup(int, char *, const char *);
+
+int
+main(int argc, char *argv[], char **envp)
+{
+ socklen_t addrlen;
+ int ch, on = 1, tos;
+ char *cp, line[LINE_MAX];
+ FILE *fd;
+ char *bindname = NULL;
+ const char *bindport = "ftp";
+ int family = AF_UNSPEC;
+ struct sigaction sa;
+
+ tzset(); /* in case no timezone database in ~ftp */
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+
+#ifdef OLD_SETPROCTITLE
+ /*
+ * Save start and extent of argv for setproctitle.
+ */
+ Argv = argv;
+ while (*envp)
+ envp++;
+ LastArgv = envp[-1] + strlen(envp[-1]);
+#endif /* OLD_SETPROCTITLE */
+
+ /*
+ * Prevent diagnostic messages from appearing on stderr.
+ * We run as a daemon or from inetd; in both cases, there's
+ * more reason in logging to syslog.
+ */
+ (void) freopen(_PATH_DEVNULL, "w", stderr);
+ opterr = 0;
+
+ /*
+ * 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);
+
+ while ((ch = getopt(argc, argv,
+ "468a:AdDEhlmMoOp:P:rRSt:T:u:UvW")) != -1) {
+ switch (ch) {
+ case '4':
+ family = (family == AF_INET6) ? AF_UNSPEC : AF_INET;
+ break;
+
+ case '6':
+ family = (family == AF_INET) ? AF_UNSPEC : AF_INET6;
+ break;
+
+ case '8':
+ assumeutf8 = 1;
+ break;
+
+ case 'a':
+ bindname = optarg;
+ break;
+
+ case 'A':
+ anon_only = 1;
+ break;
+
+ case 'd':
+ ftpdebug++;
+ break;
+
+ case 'D':
+ daemon_mode++;
+ break;
+
+ case 'E':
+ noepsv = 1;
+ break;
+
+ case 'h':
+ hostinfo = 0;
+ break;
+
+ case 'l':
+ logging++; /* > 1 == extra logging */
+ break;
+
+ case 'm':
+ noguestmod = 0;
+ break;
+
+ case 'M':
+ noguestmkd = 1;
+ break;
+
+ case 'o':
+ noretr = 1;
+ break;
+
+ case 'O':
+ noguestretr = 1;
+ break;
+
+ case 'p':
+ pid_file = optarg;
+ break;
+
+ case 'P':
+ bindport = optarg;
+ break;
+
+ case 'r':
+ readonly = 1;
+ break;
+
+ case 'R':
+ paranoid = 0;
+ break;
+
+ case 'S':
+ stats++;
+ break;
+
+ case 't':
+ timeout = atoi(optarg);
+ if (maxtimeout < timeout)
+ maxtimeout = timeout;
+ break;
+
+ 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)
+ syslog(LOG_WARNING, "bad value for -u");
+ else
+ defumask = val;
+ break;
+ }
+ case 'U':
+ restricted_data_ports = 0;
+ break;
+
+ case 'v':
+ ftpdebug++;
+ break;
+
+ case 'W':
+ dowtmp = 0;
+ break;
+
+ default:
+ syslog(LOG_WARNING, "unknown flag -%c ignored", optopt);
+ break;
+ }
+ }
+
+ if (daemon_mode) {
+ int *ctl_sock, fd, maxfd = -1, nfds, i;
+ fd_set defreadfds, readfds;
+ pid_t pid;
+ struct pidfh *pfh;
+
+ if ((pfh = pidfile_open(pid_file, 0600, &pid)) == NULL) {
+ if (errno == EEXIST) {
+ syslog(LOG_ERR, "%s already running, pid %d",
+ getprogname(), (int)pid);
+ exit(1);
+ }
+ syslog(LOG_WARNING, "pidfile_open: %m");
+ }
+
+ /*
+ * Detach from parent.
+ */
+ if (daemon(1, 1) < 0) {
+ syslog(LOG_ERR, "failed to become a daemon");
+ exit(1);
+ }
+
+ if (pfh != NULL && pidfile_write(pfh) == -1)
+ syslog(LOG_WARNING, "pidfile_write: %m");
+
+ sa.sa_handler = reapchild;
+ (void)sigaction(SIGCHLD, &sa, NULL);
+
+#ifdef VIRTUAL_HOSTING
+ inithosts(family);
+#endif
+
+ /*
+ * Open a socket, bind it to the FTP port, and start
+ * listening.
+ */
+ ctl_sock = socksetup(family, bindname, bindport);
+ if (ctl_sock == NULL)
+ exit(1);
+
+ FD_ZERO(&defreadfds);
+ for (i = 1; i <= *ctl_sock; i++) {
+ FD_SET(ctl_sock[i], &defreadfds);
+ if (listen(ctl_sock[i], 32) < 0) {
+ syslog(LOG_ERR, "control listen: %m");
+ exit(1);
+ }
+ if (maxfd < ctl_sock[i])
+ maxfd = ctl_sock[i];
+ }
+
+ /*
+ * Loop forever accepting connection requests and forking off
+ * children to handle them.
+ */
+ while (1) {
+ FD_COPY(&defreadfds, &readfds);
+ nfds = select(maxfd + 1, &readfds, NULL, NULL, 0);
+ if (nfds <= 0) {
+ if (nfds < 0 && errno != EINTR)
+ syslog(LOG_WARNING, "select: %m");
+ continue;
+ }
+
+ pid = -1;
+ for (i = 1; i <= *ctl_sock; i++)
+ if (FD_ISSET(ctl_sock[i], &readfds)) {
+ addrlen = sizeof(his_addr);
+ fd = accept(ctl_sock[i],
+ (struct sockaddr *)&his_addr,
+ &addrlen);
+ if (fd == -1) {
+ syslog(LOG_WARNING,
+ "accept: %m");
+ continue;
+ }
+ switch (pid = fork()) {
+ case 0:
+ /* child */
+ (void) dup2(fd, 0);
+ (void) dup2(fd, 1);
+ (void) close(fd);
+ for (i = 1; i <= *ctl_sock; i++)
+ close(ctl_sock[i]);
+ if (pfh != NULL)
+ pidfile_close(pfh);
+ goto gotchild;
+ case -1:
+ syslog(LOG_WARNING, "fork: %m");
+ /* FALLTHROUGH */
+ default:
+ close(fd);
+ }
+ }
+ }
+ } else {
+ addrlen = sizeof(his_addr);
+ if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
+ syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
+ exit(1);
+ }
+
+#ifdef VIRTUAL_HOSTING
+ if (his_addr.su_family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
+ family = AF_INET;
+ else
+ family = his_addr.su_family;
+ inithosts(family);
+#endif
+ }
+
+gotchild:
+ sa.sa_handler = SIG_DFL;
+ (void)sigaction(SIGCHLD, &sa, NULL);
+
+ sa.sa_handler = sigurg;
+ sa.sa_flags = 0; /* don't restart syscalls for SIGURG */
+ (void)sigaction(SIGURG, &sa, NULL);
+
+ sigfillset(&sa.sa_mask); /* block all signals in handler */
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = sigquit;
+ (void)sigaction(SIGHUP, &sa, NULL);
+ (void)sigaction(SIGINT, &sa, NULL);
+ (void)sigaction(SIGQUIT, &sa, NULL);
+ (void)sigaction(SIGTERM, &sa, NULL);
+
+ sa.sa_handler = lostconn;
+ (void)sigaction(SIGPIPE, &sa, NULL);
+
+ addrlen = sizeof(ctrl_addr);
+ if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
+ syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
+ exit(1);
+ }
+ dataport = ntohs(ctrl_addr.su_port) - 1; /* as per RFC 959 */
+#ifdef VIRTUAL_HOSTING
+ /* select our identity from virtual host table */
+ selecthost(&ctrl_addr);
+#endif
+#ifdef IP_TOS
+ if (ctrl_addr.su_family == AF_INET)
+ {
+ tos = IPTOS_LOWDELAY;
+ if (setsockopt(0, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0)
+ syslog(LOG_WARNING, "control setsockopt (IP_TOS): %m");
+ }
+#endif
+ /*
+ * Disable Nagle on the control channel so that we don't have to wait
+ * for peer's ACK before issuing our next reply.
+ */
+ if (setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
+ syslog(LOG_WARNING, "control setsockopt (TCP_NODELAY): %m");
+
+ data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1);
+
+ (void)snprintf(wtmpid, sizeof(wtmpid), "%xftpd", getpid());
+
+ /* Try to handle urgent data inline */
+#ifdef SO_OOBINLINE
+ if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)) < 0)
+ syslog(LOG_WARNING, "control setsockopt (SO_OOBINLINE): %m");
+#endif
+
+#ifdef F_SETOWN
+ if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
+ syslog(LOG_ERR, "fcntl F_SETOWN: %m");
+#endif
+ dolog((struct sockaddr *)&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);
+ }
+#ifdef VIRTUAL_HOSTING
+ fd = fopen(thishost->welcome, "r");
+#else
+ fd = fopen(_PATH_FTPWELCOME, "r");
+#endif
+ if (fd != 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 */
+ }
+#ifndef VIRTUAL_HOSTING
+ if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL)
+ fatalerror("Ran out of memory.");
+ if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0)
+ hostname[0] = '\0';
+ hostname[MAXHOSTNAMELEN - 1] = '\0';
+#endif
+ if (hostinfo)
+ reply(220, "%s FTP server (%s) ready.", hostname, version);
+ else
+ reply(220, "FTP server ready.");
+ for (;;)
+ (void) yyparse();
+ /* NOTREACHED */
+}
+
+static void
+lostconn(int signo)
+{
+
+ if (ftpdebug)
+ syslog(LOG_DEBUG, "lost connection");
+ dologout(1);
+}
+
+static void
+sigquit(int signo)
+{
+
+ syslog(LOG_ERR, "got signal %d", signo);
+ dologout(1);
+}
+
+#ifdef VIRTUAL_HOSTING
+/*
+ * read in virtual host tables (if they exist)
+ */
+
+static void
+inithosts(int family)
+{
+ int insert;
+ size_t len;
+ FILE *fp;
+ char *cp, *mp, *line;
+ char *hostname;
+ char *vhost, *anonuser, *statfile, *welcome, *loginmsg;
+ struct ftphost *hrp, *lhrp;
+ struct addrinfo hints, *res, *ai;
+
+ /*
+ * Fill in the default host information
+ */
+ if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL)
+ fatalerror("Ran out of memory.");
+ if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0)
+ hostname[0] = '\0';
+ hostname[MAXHOSTNAMELEN - 1] = '\0';
+ if ((hrp = malloc(sizeof(struct ftphost))) == NULL)
+ fatalerror("Ran out of memory.");
+ hrp->hostname = hostname;
+ hrp->hostinfo = NULL;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+ if (getaddrinfo(hrp->hostname, NULL, &hints, &res) == 0)
+ hrp->hostinfo = res;
+ hrp->statfile = _PATH_FTPDSTATFILE;
+ hrp->welcome = _PATH_FTPWELCOME;
+ hrp->loginmsg = _PATH_FTPLOGINMESG;
+ hrp->anonuser = "ftp";
+ hrp->next = NULL;
+ thishost = firsthost = lhrp = hrp;
+ if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) {
+ int addrsize, gothost;
+ void *addr;
+ struct hostent *hp;
+
+ while ((line = fgetln(fp, &len)) != NULL) {
+ int i, hp_error;
+
+ /* skip comments */
+ if (line[0] == '#')
+ continue;
+ if (line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ mp = NULL;
+ } else {
+ if ((mp = malloc(len + 1)) == NULL)
+ fatalerror("Ran out of memory.");
+ memcpy(mp, line, len);
+ mp[len] = '\0';
+ line = mp;
+ }
+ cp = strtok(line, " \t");
+ /* skip empty lines */
+ if (cp == NULL)
+ goto nextline;
+ vhost = cp;
+
+ /* set defaults */
+ anonuser = "ftp";
+ statfile = _PATH_FTPDSTATFILE;
+ welcome = _PATH_FTPWELCOME;
+ loginmsg = _PATH_FTPLOGINMESG;
+
+ /*
+ * Preparse the line so we can use its info
+ * for all the addresses associated with
+ * the virtual host name.
+ * Field 0, the virtual host name, is special:
+ * it's already parsed off and will be strdup'ed
+ * later, after we know its canonical form.
+ */
+ for (i = 1; i < 5 && (cp = strtok(NULL, " \t")); i++)
+ if (*cp != '-' && (cp = strdup(cp)))
+ switch (i) {
+ case 1: /* anon user permissions */
+ anonuser = cp;
+ break;
+ case 2: /* statistics file */
+ statfile = cp;
+ break;
+ case 3: /* welcome message */
+ welcome = cp;
+ break;
+ case 4: /* login message */
+ loginmsg = cp;
+ break;
+ default: /* programming error */
+ abort();
+ /* NOTREACHED */
+ }
+
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+ if (getaddrinfo(vhost, NULL, &hints, &res) != 0)
+ goto nextline;
+ for (ai = res; ai != NULL && ai->ai_addr != NULL;
+ ai = ai->ai_next) {
+
+ gothost = 0;
+ for (hrp = firsthost; hrp != NULL; hrp = hrp->next) {
+ struct addrinfo *hi;
+
+ for (hi = hrp->hostinfo; hi != NULL;
+ hi = hi->ai_next)
+ if (hi->ai_addrlen == ai->ai_addrlen &&
+ memcmp(hi->ai_addr,
+ ai->ai_addr,
+ ai->ai_addr->sa_len) == 0) {
+ gothost++;
+ break;
+ }
+ if (gothost)
+ break;
+ }
+ if (hrp == NULL) {
+ if ((hrp = malloc(sizeof(struct ftphost))) == NULL)
+ goto nextline;
+ hrp->hostname = NULL;
+ insert = 1;
+ } else {
+ if (hrp->hostinfo && hrp->hostinfo != res)
+ freeaddrinfo(hrp->hostinfo);
+ insert = 0; /* host already in the chain */
+ }
+ hrp->hostinfo = res;
+
+ /*
+ * determine hostname to use.
+ * force defined name if there is a valid alias
+ * otherwise fallback to primary hostname
+ */
+ /* XXX: getaddrinfo() can't do alias check */
+ switch(hrp->hostinfo->ai_family) {
+ case AF_INET:
+ addr = &((struct sockaddr_in *)hrp->hostinfo->ai_addr)->sin_addr;
+ addrsize = sizeof(struct in_addr);
+ break;
+ case AF_INET6:
+ addr = &((struct sockaddr_in6 *)hrp->hostinfo->ai_addr)->sin6_addr;
+ addrsize = sizeof(struct in6_addr);
+ break;
+ default:
+ /* should not reach here */
+ freeaddrinfo(hrp->hostinfo);
+ if (insert)
+ free(hrp); /*not in chain, can free*/
+ else
+ hrp->hostinfo = NULL; /*mark as blank*/
+ goto nextline;
+ /* NOTREACHED */
+ }
+ if ((hp = getipnodebyaddr(addr, addrsize,
+ hrp->hostinfo->ai_family,
+ &hp_error)) != NULL) {
+ if (strcmp(vhost, hp->h_name) != 0) {
+ if (hp->h_aliases == NULL)
+ vhost = hp->h_name;
+ else {
+ i = 0;
+ while (hp->h_aliases[i] &&
+ strcmp(vhost, hp->h_aliases[i]) != 0)
+ ++i;
+ if (hp->h_aliases[i] == NULL)
+ vhost = hp->h_name;
+ }
+ }
+ }
+ if (hrp->hostname &&
+ strcmp(hrp->hostname, vhost) != 0) {
+ free(hrp->hostname);
+ hrp->hostname = NULL;
+ }
+ if (hrp->hostname == NULL &&
+ (hrp->hostname = strdup(vhost)) == NULL) {
+ freeaddrinfo(hrp->hostinfo);
+ hrp->hostinfo = NULL; /* mark as blank */
+ if (hp)
+ freehostent(hp);
+ goto nextline;
+ }
+ hrp->anonuser = anonuser;
+ hrp->statfile = statfile;
+ hrp->welcome = welcome;
+ hrp->loginmsg = loginmsg;
+ if (insert) {
+ hrp->next = NULL;
+ lhrp->next = hrp;
+ lhrp = hrp;
+ }
+ if (hp)
+ freehostent(hp);
+ }
+nextline:
+ if (mp)
+ free(mp);
+ }
+ (void) fclose(fp);
+ }
+}
+
+static void
+selecthost(union sockunion *su)
+{
+ struct ftphost *hrp;
+ u_int16_t port;
+#ifdef INET6
+ struct in6_addr *mapped_in6 = NULL;
+#endif
+ struct addrinfo *hi;
+
+#ifdef INET6
+ /*
+ * XXX IPv4 mapped IPv6 addr consideraton,
+ * specified in rfc2373.
+ */
+ if (su->su_family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr))
+ mapped_in6 = &su->su_sin6.sin6_addr;
+#endif
+
+ hrp = thishost = firsthost; /* default */
+ port = su->su_port;
+ su->su_port = 0;
+ while (hrp != NULL) {
+ for (hi = hrp->hostinfo; hi != NULL; hi = hi->ai_next) {
+ if (memcmp(su, hi->ai_addr, hi->ai_addrlen) == 0) {
+ thishost = hrp;
+ goto found;
+ }
+#ifdef INET6
+ /* XXX IPv4 mapped IPv6 addr consideraton */
+ if (hi->ai_addr->sa_family == AF_INET && mapped_in6 != NULL &&
+ (memcmp(&mapped_in6->s6_addr[12],
+ &((struct sockaddr_in *)hi->ai_addr)->sin_addr,
+ sizeof(struct in_addr)) == 0)) {
+ thishost = hrp;
+ goto found;
+ }
+#endif
+ }
+ hrp = hrp->next;
+ }
+found:
+ su->su_port = port;
+ /* setup static variables as appropriate */
+ hostname = thishost->hostname;
+ ftpuser = thishost->anonuser;
+}
+#endif
+
+/*
+ * Helper function for sgetpwnam().
+ */
+static char *
+sgetsave(char *s)
+{
+ char *new = malloc(strlen(s) + 1);
+
+ if (new == NULL) {
+ reply(421, "Ran out of memory.");
+ 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).
+ * NB: The data returned by sgetpwnam() will remain valid until
+ * the next call to this function. Its difference from getpwnam()
+ * is that sgetpwnam() is known to be called from ftpd code only.
+ */
+static struct passwd *
+sgetpwnam(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_class);
+ 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_class = sgetsave(p->pw_class);
+ 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[MAXLOGNAME]; /* 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(char *name)
+{
+ int ecode;
+ char *cp, *shell;
+
+ if (logged_in) {
+ if (guest) {
+ reply(530, "Can't change user from guest login.");
+ return;
+ } else if (dochroot) {
+ reply(530, "Can't change user from chroot user.");
+ return;
+ }
+ end_login();
+ }
+
+ guest = 0;
+#ifdef VIRTUAL_HOSTING
+ pw = sgetpwnam(thishost->anonuser);
+#else
+ pw = sgetpwnam("ftp");
+#endif
+ if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
+ if (checkuser(_PATH_FTPUSERS, "ftp", 0, NULL, &ecode) ||
+ (ecode != 0 && ecode != ENOENT))
+ reply(530, "User %s access denied.", name);
+ else if (checkuser(_PATH_FTPUSERS, "anonymous", 0, NULL, &ecode) ||
+ (ecode != 0 && ecode != ENOENT))
+ reply(530, "User %s access denied.", name);
+ else if (pw != 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 (anon_only != 0) {
+ reply(530, "Sorry, only anonymous ftp allowed.");
+ return;
+ }
+
+ if ((pw = sgetpwnam(name))) {
+ if ((shell = pw->pw_shell) == NULL || *shell == 0)
+ shell = _PATH_BSHELL;
+ setusershell();
+ while ((cp = getusershell()) != NULL)
+ if (strcmp(cp, shell) == 0)
+ break;
+ endusershell();
+
+ if (cp == NULL ||
+ (checkuser(_PATH_FTPUSERS, name, 1, NULL, &ecode) ||
+ (ecode != 0 && ecode != ENOENT))) {
+ reply(530, "User %s access denied.", name);
+ if (logging)
+ syslog(LOG_NOTICE,
+ "FTP LOGIN REFUSED FROM %s, %s",
+ remotehost, name);
+ pw = NULL;
+ return;
+ }
+ }
+ if (logging)
+ strncpy(curname, name, sizeof(curname)-1);
+
+ pwok = 0;
+#ifdef USE_PAM
+ /* XXX Kluge! The conversation mechanism needs to be fixed. */
+#endif
+ if (opiechallenge(&opiedata, name, opieprompt) == 0) {
+ pwok = (pw != NULL) &&
+ opieaccessfile(remotehost) &&
+ opiealways(pw->pw_dir);
+ reply(331, "Response to %s %s for %s.",
+ opieprompt, pwok ? "requested" : "required", name);
+ } else {
+ pwok = 1;
+ reply(331, "Password required for %s.", name);
+ }
+ askpasswd = 1;
+ /*
+ * Delay before reading passwd after first failed
+ * attempt to slow down passwd-guessing programs.
+ */
+ if (login_attempts)
+ sleep(login_attempts);
+}
+
+/*
+ * Check if a user is in the file "fname",
+ * return a pointer to a malloc'd string with the rest
+ * of the matching line in "residue" if not NULL.
+ */
+static int
+checkuser(char *fname, char *name, int pwset, char **residue, int *ecode)
+{
+ FILE *fd;
+ int found = 0;
+ size_t len;
+ char *line, *mp, *p;
+
+ if (ecode != NULL)
+ *ecode = 0;
+ if ((fd = fopen(fname, "r")) != NULL) {
+ while (!found && (line = fgetln(fd, &len)) != NULL) {
+ /* skip comments */
+ if (line[0] == '#')
+ continue;
+ if (line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ mp = NULL;
+ } else {
+ if ((mp = malloc(len + 1)) == NULL)
+ fatalerror("Ran out of memory.");
+ memcpy(mp, line, len);
+ mp[len] = '\0';
+ line = mp;
+ }
+ /* avoid possible leading and trailing whitespace */
+ p = strtok(line, " \t");
+ /* skip empty lines */
+ if (p == NULL)
+ goto nextline;
+ /*
+ * if first chr is '@', check group membership
+ */
+ if (p[0] == '@') {
+ int i = 0;
+ struct group *grp;
+
+ if (p[1] == '\0') /* single @ matches anyone */
+ found = 1;
+ else {
+ if ((grp = getgrnam(p+1)) == NULL)
+ goto nextline;
+ /*
+ * Check user's default group
+ */
+ if (pwset && grp->gr_gid == pw->pw_gid)
+ found = 1;
+ /*
+ * Check supplementary groups
+ */
+ while (!found && grp->gr_mem[i])
+ found = strcmp(name,
+ grp->gr_mem[i++])
+ == 0;
+ }
+ }
+ /*
+ * Otherwise, just check for username match
+ */
+ else
+ found = strcmp(p, name) == 0;
+ /*
+ * Save the rest of line to "residue" if matched
+ */
+ if (found && residue) {
+ if ((p = strtok(NULL, "")) != NULL)
+ p += strspn(p, " \t");
+ if (p && *p) {
+ if ((*residue = strdup(p)) == NULL)
+ fatalerror("Ran out of memory.");
+ } else
+ *residue = NULL;
+ }
+nextline:
+ if (mp)
+ free(mp);
+ }
+ (void) fclose(fd);
+ } else if (ecode != NULL)
+ *ecode = errno;
+ 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)
+{
+#ifdef USE_PAM
+ int e;
+#endif
+
+ (void) seteuid(0);
+ if (logged_in && dowtmp)
+ ftpd_logwtmp(wtmpid, NULL, NULL);
+ pw = NULL;
+#ifdef LOGIN_CAP
+ setusercontext(NULL, getpwuid(0), 0, LOGIN_SETALL & ~(LOGIN_SETLOGIN |
+ LOGIN_SETUSER | LOGIN_SETGROUP | LOGIN_SETPATH |
+ LOGIN_SETENV));
+#endif
+#ifdef USE_PAM
+ if (pamh) {
+ if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS)
+ syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e));
+ if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS)
+ syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e));
+ if ((e = pam_end(pamh, e)) != PAM_SUCCESS)
+ syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
+ pamh = NULL;
+ }
+#endif
+ logged_in = 0;
+ guest = 0;
+ dochroot = 0;
+}
+
+#ifdef USE_PAM
+
+/*
+ * the following code is stolen from imap-uw PAM authentication module and
+ * login.c
+ */
+#define COPY_STRING(s) (s ? strdup(s) : NULL)
+
+struct cred_t {
+ const char *uname; /* user name */
+ const char *pass; /* password */
+};
+typedef struct cred_t cred_t;
+
+static int
+auth_conv(int num_msg, const struct pam_message **msg,
+ struct pam_response **resp, void *appdata)
+{
+ int i;
+ cred_t *cred = (cred_t *) appdata;
+ struct pam_response *reply;
+
+ reply = calloc(num_msg, sizeof *reply);
+ if (reply == NULL)
+ return PAM_BUF_ERR;
+
+ for (i = 0; i < num_msg; i++) {
+ switch (msg[i]->msg_style) {
+ case PAM_PROMPT_ECHO_ON: /* assume want user name */
+ reply[i].resp_retcode = PAM_SUCCESS;
+ reply[i].resp = COPY_STRING(cred->uname);
+ /* PAM frees resp. */
+ break;
+ case PAM_PROMPT_ECHO_OFF: /* assume want password */
+ reply[i].resp_retcode = PAM_SUCCESS;
+ reply[i].resp = COPY_STRING(cred->pass);
+ /* PAM frees resp. */
+ break;
+ case PAM_TEXT_INFO:
+ case PAM_ERROR_MSG:
+ reply[i].resp_retcode = PAM_SUCCESS;
+ reply[i].resp = NULL;
+ break;
+ default: /* unknown message style */
+ free(reply);
+ return PAM_CONV_ERR;
+ }
+ }
+
+ *resp = reply;
+ return PAM_SUCCESS;
+}
+
+/*
+ * Attempt to authenticate the user using PAM. Returns 0 if the user is
+ * authenticated, or 1 if not authenticated. If some sort of PAM system
+ * error occurs (e.g., the "/etc/pam.conf" file is missing) then this
+ * function returns -1. This can be used as an indication that we should
+ * fall back to a different authentication mechanism.
+ */
+static int
+auth_pam(struct passwd **ppw, const char *pass)
+{
+ const char *tmpl_user;
+ const void *item;
+ int rval;
+ int e;
+ cred_t auth_cred = { (*ppw)->pw_name, pass };
+ struct pam_conv conv = { &auth_conv, &auth_cred };
+
+ e = pam_start("ftpd", (*ppw)->pw_name, &conv, &pamh);
+ if (e != PAM_SUCCESS) {
+ /*
+ * In OpenPAM, it's OK to pass NULL to pam_strerror()
+ * if context creation has failed in the first place.
+ */
+ syslog(LOG_ERR, "pam_start: %s", pam_strerror(NULL, e));
+ return -1;
+ }
+
+ e = pam_set_item(pamh, PAM_RHOST, remotehost);
+ if (e != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s",
+ pam_strerror(pamh, e));
+ if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
+ }
+ pamh = NULL;
+ return -1;
+ }
+
+ e = pam_authenticate(pamh, 0);
+ switch (e) {
+ case PAM_SUCCESS:
+ /*
+ * With PAM we support the concept of a "template"
+ * user. The user enters a login name which is
+ * authenticated by PAM, usually via a remote service
+ * such as RADIUS or TACACS+. If authentication
+ * succeeds, a different but related "template" name
+ * is used for setting the credentials, shell, and
+ * home directory. The name the user enters need only
+ * exist on the remote authentication server, but the
+ * template name must be present in the local password
+ * database.
+ *
+ * This is supported by two various mechanisms in the
+ * individual modules. However, from the application's
+ * point of view, the template user is always passed
+ * back as a changed value of the PAM_USER item.
+ */
+ if ((e = pam_get_item(pamh, PAM_USER, &item)) ==
+ PAM_SUCCESS) {
+ tmpl_user = (const char *) item;
+ if (strcmp((*ppw)->pw_name, tmpl_user) != 0)
+ *ppw = getpwnam(tmpl_user);
+ } else
+ syslog(LOG_ERR, "Couldn't get PAM_USER: %s",
+ pam_strerror(pamh, e));
+ rval = 0;
+ break;
+
+ case PAM_AUTH_ERR:
+ case PAM_USER_UNKNOWN:
+ case PAM_MAXTRIES:
+ rval = 1;
+ break;
+
+ default:
+ syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e));
+ rval = -1;
+ break;
+ }
+
+ if (rval == 0) {
+ e = pam_acct_mgmt(pamh, 0);
+ if (e != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_acct_mgmt: %s",
+ pam_strerror(pamh, e));
+ rval = 1;
+ }
+ }
+
+ if (rval != 0) {
+ if ((e = pam_end(pamh, e)) != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e));
+ }
+ pamh = NULL;
+ }
+ return rval;
+}
+
+#endif /* USE_PAM */
+
+void
+pass(char *passwd)
+{
+ int rval, ecode;
+ FILE *fd;
+#ifdef LOGIN_CAP
+ login_cap_t *lc = NULL;
+#endif
+#ifdef USE_PAM
+ int e;
+#endif
+ char *residue = NULL;
+ char *xpasswd;
+
+ 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) {
+ rval = 1; /* failure below */
+ goto skip;
+ }
+#ifdef USE_PAM
+ rval = auth_pam(&pw, passwd);
+ if (rval >= 0) {
+ opieunlock();
+ goto skip;
+ }
+#endif
+ if (opieverify(&opiedata, passwd) == 0)
+ xpasswd = pw->pw_passwd;
+ else if (pwok) {
+ xpasswd = crypt(passwd, pw->pw_passwd);
+ if (passwd[0] == '\0' && pw->pw_passwd[0] != '\0')
+ xpasswd = ":";
+ } else {
+ rval = 1;
+ goto skip;
+ }
+ rval = strcmp(pw->pw_passwd, xpasswd);
+ if (pw->pw_expire && time(NULL) >= pw->pw_expire)
+ rval = 1; /* failure */
+skip:
+ /*
+ * If rval == 1, the user failed the authentication check
+ * above. If rval == 0, either PAM or local authentication
+ * succeeded.
+ */
+ if (rval) {
+ reply(530, "Login incorrect.");
+ if (logging) {
+ syslog(LOG_NOTICE,
+ "FTP LOGIN FAILED FROM %s",
+ remotehost);
+ syslog(LOG_AUTHPRIV | 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(pw->pw_gid) < 0) {
+ reply(550, "Can't set gid.");
+ return;
+ }
+ /* May be overridden by login.conf */
+ (void) umask(defumask);
+#ifdef LOGIN_CAP
+ if ((lc = login_getpwclass(pw)) != NULL) {
+ char remote_ip[NI_MAXHOST];
+
+ if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
+ remote_ip, sizeof(remote_ip) - 1, NULL, 0,
+ NI_NUMERICHOST))
+ *remote_ip = 0;
+ remote_ip[sizeof(remote_ip) - 1] = 0;
+ if (!auth_hostok(lc, remotehost, remote_ip)) {
+ syslog(LOG_INFO|LOG_AUTH,
+ "FTP LOGIN FAILED (HOST) as %s: permission denied.",
+ pw->pw_name);
+ reply(530, "Permission denied.");
+ pw = NULL;
+ return;
+ }
+ if (!auth_timeok(lc, time(NULL))) {
+ reply(530, "Login not available right now.");
+ pw = NULL;
+ return;
+ }
+ }
+ setusercontext(lc, pw, 0, LOGIN_SETALL &
+ ~(LOGIN_SETUSER | LOGIN_SETPATH | LOGIN_SETENV));
+#else
+ setlogin(pw->pw_name);
+ (void) initgroups(pw->pw_name, pw->pw_gid);
+#endif
+
+#ifdef USE_PAM
+ if (pamh) {
+ if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e));
+ } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e));
+ }
+ }
+#endif
+
+ dochroot =
+ checkuser(_PATH_FTPCHROOT, pw->pw_name, 1, &residue, &ecode)
+#ifdef LOGIN_CAP /* Allow login.conf configuration as well */
+ || login_getcapbool(lc, "ftp-chroot", 0)
+#endif
+ ;
+ /*
+ * It is possible that checkuser() failed to open the chroot file.
+ * If this is the case, report that logins are un-available, since we
+ * have no way of checking whether or not the user should be chrooted.
+ * We ignore ENOENT since it is not required that this file be present.
+ */
+ if (ecode != 0 && ecode != ENOENT) {
+ reply(530, "Login not available right now.");
+ return;
+ }
+ chrootdir = NULL;
+
+ /* Disable wtmp logging when chrooting. */
+ if (dochroot || guest)
+ dowtmp = 0;
+ if (dowtmp)
+ ftpd_logwtmp(wtmpid, pw->pw_name,
+ (struct sockaddr *)&his_addr);
+ logged_in = 1;
+
+ if (guest && stats && statfd < 0)
+#ifdef VIRTUAL_HOSTING
+ statfd = open(thishost->statfile, O_WRONLY|O_APPEND);
+#else
+ statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND);
+#endif
+ if (statfd < 0)
+ stats = 0;
+
+ /*
+ * For a chrooted local user,
+ * a) see whether ftpchroot(5) specifies a chroot directory,
+ * b) extract the directory pathname from the line,
+ * c) expand it to the absolute pathname if necessary.
+ */
+ if (dochroot && residue &&
+ (chrootdir = strtok(residue, " \t")) != NULL) {
+ if (chrootdir[0] != '/')
+ asprintf(&chrootdir, "%s/%s", pw->pw_dir, chrootdir);
+ else
+ chrootdir = strdup(chrootdir); /* make it permanent */
+ if (chrootdir == NULL)
+ fatalerror("Ran out of memory.");
+ }
+ if (guest || dochroot) {
+ /*
+ * If no chroot directory set yet, use the login directory.
+ * Copy it so it can be modified while pw->pw_dir stays intact.
+ */
+ if (chrootdir == NULL &&
+ (chrootdir = strdup(pw->pw_dir)) == NULL)
+ fatalerror("Ran out of memory.");
+ /*
+ * Check for the "/chroot/./home" syntax,
+ * separate the chroot and home directory pathnames.
+ */
+ if ((homedir = strstr(chrootdir, "/./")) != NULL) {
+ *(homedir++) = '\0'; /* wipe '/' */
+ homedir++; /* skip '.' */
+ } else {
+ /*
+ * We MUST do a chdir() after the chroot. Otherwise
+ * the old current directory will be accessible as "."
+ * outside the new root!
+ */
+ homedir = "/";
+ }
+ /*
+ * Finally, do chroot()
+ */
+ if (chroot(chrootdir) < 0) {
+ reply(550, "Can't change root.");
+ goto bad;
+ }
+ __FreeBSD_libc_enter_restricted_mode();
+ } else /* real user w/o chroot */
+ homedir = pw->pw_dir;
+ /*
+ * Set euid *before* doing chdir() so
+ * a) the user won't be carried to a directory that he couldn't reach
+ * on his own due to no permission to upper path components,
+ * b) NFS mounted homedirs w/restrictive permissions will be accessible
+ * (uid 0 has no root power over NFS if not mapped explicitly.)
+ */
+ if (seteuid(pw->pw_uid) < 0) {
+ reply(550, "Can't set uid.");
+ goto bad;
+ }
+ if (chdir(homedir) < 0) {
+ if (guest || dochroot) {
+ reply(550, "Can't change to base directory.");
+ goto bad;
+ } else {
+ if (chdir("/") < 0) {
+ reply(550, "Root is inaccessible.");
+ goto bad;
+ }
+ lreply(230, "No directory! Logging in with home=/.");
+ }
+ }
+
+ /*
+ * Display a login message, if it exists.
+ * N.B. reply(230,) must follow the message.
+ */
+#ifdef VIRTUAL_HOSTING
+ fd = fopen(thishost->loginmsg, "r");
+#else
+ fd = fopen(_PATH_FTPLOGINMESG, "r");
+#endif
+ if (fd != 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) {
+ if (ident != NULL)
+ free(ident);
+ ident = strdup(passwd);
+ if (ident == NULL)
+ fatalerror("Ran out of memory.");
+
+ reply(230, "Guest login ok, access restrictions apply.");
+#ifdef SETPROCTITLE
+#ifdef VIRTUAL_HOSTING
+ if (thishost != firsthost)
+ snprintf(proctitle, sizeof(proctitle),
+ "%s: anonymous(%s)/%s", remotehost, hostname,
+ passwd);
+ else
+#endif
+ snprintf(proctitle, sizeof(proctitle),
+ "%s: anonymous/%s", remotehost, passwd);
+ setproctitle("%s", proctitle);
+#endif /* SETPROCTITLE */
+ if (logging)
+ syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
+ remotehost, passwd);
+ } else {
+ if (dochroot)
+ reply(230, "User %s logged in, "
+ "access restrictions apply.", pw->pw_name);
+ else
+ reply(230, "User %s logged in.", pw->pw_name);
+
+#ifdef SETPROCTITLE
+ snprintf(proctitle, sizeof(proctitle),
+ "%s: user/%s", remotehost, pw->pw_name);
+ setproctitle("%s", proctitle);
+#endif /* SETPROCTITLE */
+ if (logging)
+ syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
+ remotehost, pw->pw_name);
+ }
+ if (logging && (guest || dochroot))
+ syslog(LOG_INFO, "session root changed to %s", chrootdir);
+#ifdef LOGIN_CAP
+ login_close(lc);
+#endif
+ if (residue)
+ free(residue);
+ return;
+bad:
+ /* Forget all about it... */
+#ifdef LOGIN_CAP
+ login_close(lc);
+#endif
+ if (residue)
+ free(residue);
+ end_login();
+}
+
+void
+retrieve(char *cmd, char *name)
+{
+ FILE *fin, *dout;
+ struct stat st;
+ int (*closefunc)(FILE *);
+ time_t start;
+
+ if (cmd == 0) {
+ fin = fopen(name, "r"), closefunc = fclose;
+ st.st_size = 0;
+ } else {
+ char line[BUFSIZ];
+
+ (void) snprintf(line, sizeof(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) {
+ if (fstat(fileno(fin), &st) < 0) {
+ perror_reply(550, name);
+ goto done;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ /*
+ * Never sending a raw directory is a workaround
+ * for buggy clients that will attempt to RETR
+ * a directory before listing it, e.g., Mozilla.
+ * Preventing a guest from getting irregular files
+ * is a simple security measure.
+ */
+ if (S_ISDIR(st.st_mode) || guest) {
+ reply(550, "%s: not a plain file.", name);
+ goto done;
+ }
+ st.st_size = -1;
+ /* st.st_blksize is set for all descriptor types */
+ }
+ }
+ 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;
+ time(&start);
+ send_data(fin, dout, st.st_blksize, st.st_size,
+ restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode));
+ if (cmd == 0 && guest && stats && byte_count > 0)
+ logxfer(name, byte_count, start);
+ (void) fclose(dout);
+ data = -1;
+ pdata = -1;
+done:
+ if (cmd == 0)
+ LOGBYTES("get", name, byte_count);
+ (*closefunc)(fin);
+}
+
+void
+store(char *name, char *mode, int unique)
+{
+ int fd;
+ FILE *fout, *din;
+ int (*closefunc)(FILE *);
+
+ if (*mode == 'a') { /* APPE */
+ if (unique) {
+ /* Programming error */
+ syslog(LOG_ERR, "Internal: unique flag to APPE");
+ unique = 0;
+ }
+ if (guest && noguestmod) {
+ reply(550, "Appending to existing file denied.");
+ goto err;
+ }
+ restart_point = 0; /* not affected by preceding REST */
+ }
+ if (unique) /* STOU overrides REST */
+ restart_point = 0;
+ if (guest && noguestmod) {
+ if (restart_point) { /* guest STOR w/REST */
+ reply(550, "Modifying existing file denied.");
+ goto err;
+ } else /* treat guest STOR as STOU */
+ unique = 1;
+ }
+
+ if (restart_point)
+ mode = "r+"; /* so ASCII manual seek can work */
+ if (unique) {
+ if ((fd = guniquefd(name, &name)) < 0)
+ goto err;
+ fout = fdopen(fd, mode);
+ } else
+ fout = fopen(name, mode);
+ closefunc = fclose;
+ if (fout == NULL) {
+ perror_reply(553, name);
+ goto err;
+ }
+ 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 (fseeko(fout, 0, SEEK_CUR) < 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, -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 == 'a' ? "append" : "put", name, byte_count);
+ (*closefunc)(fout);
+ return;
+err:
+ LOGCMD(*mode == 'a' ? "append" : "put" , name);
+ return;
+}
+
+static FILE *
+getdatasock(char *mode)
+{
+ int on = 1, s, t, tries;
+
+ if (data >= 0)
+ return (fdopen(data, mode));
+
+ s = socket(data_dest.su_family, SOCK_STREAM, 0);
+ if (s < 0)
+ goto bad;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
+ syslog(LOG_WARNING, "data setsockopt (SO_REUSEADDR): %m");
+ /* anchor socket to avoid multi-homing problems */
+ data_source = ctrl_addr;
+ data_source.su_port = htons(dataport);
+ (void) seteuid(0);
+ for (tries = 1; ; tries++) {
+ /*
+ * We should loop here since it's possible that
+ * another ftpd instance has passed this point and is
+ * trying to open a data connection in active mode now.
+ * Until the other connection is opened, we'll be getting
+ * EADDRINUSE because no SOCK_STREAM sockets in the system
+ * can share both local and remote addresses, localIP:20
+ * and *:* in this case.
+ */
+ if (bind(s, (struct sockaddr *)&data_source,
+ data_source.su_len) >= 0)
+ break;
+ if (errno != EADDRINUSE || tries > 10)
+ goto bad;
+ sleep(tries);
+ }
+ (void) seteuid(pw->pw_uid);
+#ifdef IP_TOS
+ if (data_source.su_family == AF_INET)
+ {
+ on = IPTOS_THROUGHPUT;
+ if (setsockopt(s, IPPROTO_IP, IP_TOS, &on, sizeof(int)) < 0)
+ syslog(LOG_WARNING, "data 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().
+ */
+ on = 1;
+ if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, &on, sizeof on) < 0)
+ syslog(LOG_WARNING, "data setsockopt (TCP_NOPUSH): %m");
+#endif
+ return (fdopen(s, mode));
+bad:
+ /* Return the real value of errno (close may change it) */
+ t = errno;
+ (void) seteuid(pw->pw_uid);
+ (void) close(s);
+ errno = t;
+ return (NULL);
+}
+
+static FILE *
+dataconn(char *name, off_t size, char *mode)
+{
+ char sizebuf[32];
+ FILE *file;
+ int retry = 0, tos, conerrno;
+
+ file_size = size;
+ byte_count = 0;
+ if (size != -1)
+ (void) snprintf(sizebuf, sizeof(sizebuf),
+ " (%jd bytes)", (intmax_t)size);
+ else
+ *sizebuf = '\0';
+ if (pdata >= 0) {
+ union sockunion from;
+ socklen_t fromlen = ctrl_addr.su_len;
+ int flags, s;
+ struct timeval timeout;
+ fd_set set;
+
+ FD_ZERO(&set);
+ FD_SET(pdata, &set);
+
+ timeout.tv_usec = 0;
+ timeout.tv_sec = 120;
+
+ /*
+ * Granted a socket is in the blocking I/O mode,
+ * accept() will block after a successful select()
+ * if the selected connection dies in between.
+ * Therefore set the non-blocking I/O flag here.
+ */
+ if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 ||
+ fcntl(pdata, F_SETFL, flags | O_NONBLOCK) == -1)
+ goto pdata_err;
+ if (select(pdata+1, &set, NULL, NULL, &timeout) <= 0 ||
+ (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0)
+ goto pdata_err;
+ (void) close(pdata);
+ pdata = s;
+ /*
+ * Unset the inherited non-blocking I/O flag
+ * on the child socket so stdio can work on it.
+ */
+ if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 ||
+ fcntl(pdata, F_SETFL, flags & ~O_NONBLOCK) == -1)
+ goto pdata_err;
+#ifdef IP_TOS
+ if (from.su_family == AF_INET)
+ {
+ tos = IPTOS_THROUGHPUT;
+ if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0)
+ syslog(LOG_WARNING, "pdata setsockopt (IP_TOS): %m");
+ }
+#endif
+ reply(150, "Opening %s mode data connection for '%s'%s.",
+ type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
+ return (fdopen(pdata, mode));
+pdata_err:
+ reply(425, "Can't open data connection.");
+ (void) close(pdata);
+ pdata = -1;
+ return (NULL);
+ }
+ 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;
+ do {
+ file = getdatasock(mode);
+ if (file == NULL) {
+ char hostbuf[NI_MAXHOST], portbuf[NI_MAXSERV];
+
+ if (getnameinfo((struct sockaddr *)&data_source,
+ data_source.su_len,
+ hostbuf, sizeof(hostbuf) - 1,
+ portbuf, sizeof(portbuf) - 1,
+ NI_NUMERICHOST|NI_NUMERICSERV))
+ *hostbuf = *portbuf = 0;
+ hostbuf[sizeof(hostbuf) - 1] = 0;
+ portbuf[sizeof(portbuf) - 1] = 0;
+ reply(425, "Can't create data socket (%s,%s): %s.",
+ hostbuf, portbuf, strerror(errno));
+ return (NULL);
+ }
+ data = fileno(file);
+ conerrno = 0;
+ if (connect(data, (struct sockaddr *)&data_dest,
+ data_dest.su_len) == 0)
+ break;
+ conerrno = errno;
+ (void) fclose(file);
+ data = -1;
+ if (conerrno == EADDRINUSE) {
+ sleep(swaitint);
+ retry += swaitint;
+ } else {
+ break;
+ }
+ } while (retry <= swaitmax);
+ if (conerrno != 0) {
+ reply(425, "Can't build data connection: %s.",
+ strerror(conerrno));
+ return (NULL);
+ }
+ reply(150, "Opening %s mode data connection for '%s'%s.",
+ type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
+ return (file);
+}
+
+/*
+ * A helper macro to avoid code duplication
+ * in send_data() and receive_data().
+ *
+ * XXX We have to block SIGURG during putc() because BSD stdio
+ * is unable to restart interrupted write operations and hence
+ * the entire buffer contents will be lost as soon as a write()
+ * call indicates EINTR to stdio.
+ */
+#define FTPD_PUTC(ch, file, label) \
+ do { \
+ int ret; \
+ \
+ do { \
+ START_UNSAFE; \
+ ret = putc((ch), (file)); \
+ END_UNSAFE; \
+ CHECKOOB(return (-1)) \
+ else if (ferror(file)) \
+ goto label; \
+ clearerr(file); \
+ } while (ret == EOF); \
+ } while (0)
+
+/*
+ * 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 int
+send_data(FILE *instr, FILE *outstr, size_t blksize, off_t filesize, int isreg)
+{
+ int c, cp, filefd, netfd;
+ char *buf;
+
+ STARTXFER;
+
+ switch (type) {
+
+ case TYPE_A:
+ cp = EOF;
+ for (;;) {
+ c = getc(instr);
+ CHECKOOB(return (-1))
+ else if (c == EOF && ferror(instr))
+ goto file_err;
+ if (c == EOF) {
+ if (ferror(instr)) { /* resume after OOB */
+ clearerr(instr);
+ continue;
+ }
+ if (feof(instr)) /* EOF */
+ break;
+ syslog(LOG_ERR, "Internal: impossible condition"
+ " on file after getc()");
+ goto file_err;
+ }
+ if (c == '\n' && cp != '\r') {
+ FTPD_PUTC('\r', outstr, data_err);
+ byte_count++;
+ }
+ FTPD_PUTC(c, outstr, data_err);
+ byte_count++;
+ cp = c;
+ }
+#ifdef notyet /* BSD stdio isn't ready for that */
+ while (fflush(outstr) == EOF) {
+ CHECKOOB(return (-1))
+ else
+ goto data_err;
+ clearerr(outstr);
+ }
+ ENDXFER;
+#else
+ ENDXFER;
+ if (fflush(outstr) == EOF)
+ goto data_err;
+#endif
+ reply(226, "Transfer complete.");
+ return (0);
+
+ 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) {
+ char *msg = "Transfer complete.";
+ off_t cnt, offset;
+ int err;
+
+ cnt = offset = 0;
+
+ while (filesize > 0) {
+ err = sendfile(filefd, netfd, offset, 0,
+ NULL, &cnt, 0);
+ /*
+ * Calculate byte_count before OOB processing.
+ * It can be used in myoob() later.
+ */
+ byte_count += cnt;
+ offset += cnt;
+ filesize -= cnt;
+ CHECKOOB(return (-1))
+ else if (err == -1) {
+ if (errno != EINTR &&
+ cnt == 0 && offset == 0)
+ goto oldway;
+ goto data_err;
+ }
+ if (err == -1) /* resume after OOB */
+ continue;
+ /*
+ * We hit the EOF prematurely.
+ * Perhaps the file was externally truncated.
+ */
+ if (cnt == 0) {
+ msg = "Transfer finished due to "
+ "premature end of file.";
+ break;
+ }
+ }
+ ENDXFER;
+ reply(226, "%s", msg);
+ return (0);
+ }
+
+oldway:
+ if ((buf = malloc(blksize)) == NULL) {
+ ENDXFER;
+ reply(451, "Ran out of memory.");
+ return (-1);
+ }
+
+ for (;;) {
+ int cnt, len;
+ char *bp;
+
+ cnt = read(filefd, buf, blksize);
+ CHECKOOB(free(buf); return (-1))
+ else if (cnt < 0) {
+ free(buf);
+ goto file_err;
+ }
+ if (cnt < 0) /* resume after OOB */
+ continue;
+ if (cnt == 0) /* EOF */
+ break;
+ for (len = cnt, bp = buf; len > 0;) {
+ cnt = write(netfd, bp, len);
+ CHECKOOB(free(buf); return (-1))
+ else if (cnt < 0) {
+ free(buf);
+ goto data_err;
+ }
+ if (cnt <= 0)
+ continue;
+ len -= cnt;
+ bp += cnt;
+ byte_count += cnt;
+ }
+ }
+ ENDXFER;
+ free(buf);
+ reply(226, "Transfer complete.");
+ return (0);
+ default:
+ ENDXFER;
+ reply(550, "Unimplemented TYPE %d in send_data.", type);
+ return (-1);
+ }
+
+data_err:
+ ENDXFER;
+ perror_reply(426, "Data connection");
+ return (-1);
+
+file_err:
+ ENDXFER;
+ perror_reply(551, "Error on input file");
+ return (-1);
+}
+
+/*
+ * 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(FILE *instr, FILE *outstr)
+{
+ int c, cp;
+ int bare_lfs = 0;
+
+ STARTXFER;
+
+ switch (type) {
+
+ case TYPE_I:
+ case TYPE_L:
+ for (;;) {
+ int cnt, len;
+ char *bp;
+ char buf[BUFSIZ];
+
+ cnt = read(fileno(instr), buf, sizeof(buf));
+ CHECKOOB(return (-1))
+ else if (cnt < 0)
+ goto data_err;
+ if (cnt < 0) /* resume after OOB */
+ continue;
+ if (cnt == 0) /* EOF */
+ break;
+ for (len = cnt, bp = buf; len > 0;) {
+ cnt = write(fileno(outstr), bp, len);
+ CHECKOOB(return (-1))
+ else if (cnt < 0)
+ goto file_err;
+ if (cnt <= 0)
+ continue;
+ len -= cnt;
+ bp += cnt;
+ byte_count += cnt;
+ }
+ }
+ ENDXFER;
+ return (0);
+
+ case TYPE_E:
+ ENDXFER;
+ reply(553, "TYPE E not implemented.");
+ return (-1);
+
+ case TYPE_A:
+ cp = EOF;
+ for (;;) {
+ c = getc(instr);
+ CHECKOOB(return (-1))
+ else if (c == EOF && ferror(instr))
+ goto data_err;
+ if (c == EOF && ferror(instr)) { /* resume after OOB */
+ clearerr(instr);
+ continue;
+ }
+
+ if (cp == '\r') {
+ if (c != '\n')
+ FTPD_PUTC('\r', outstr, file_err);
+ } else
+ if (c == '\n')
+ bare_lfs++;
+ if (c == '\r') {
+ byte_count++;
+ cp = c;
+ continue;
+ }
+
+ /* Check for EOF here in order not to lose last \r. */
+ if (c == EOF) {
+ if (feof(instr)) /* EOF */
+ break;
+ syslog(LOG_ERR, "Internal: impossible condition"
+ " on data stream after getc()");
+ goto data_err;
+ }
+
+ byte_count++;
+ FTPD_PUTC(c, outstr, file_err);
+ cp = c;
+ }
+#ifdef notyet /* BSD stdio isn't ready for that */
+ while (fflush(outstr) == EOF) {
+ CHECKOOB(return (-1))
+ else
+ goto file_err;
+ clearerr(outstr);
+ }
+ ENDXFER;
+#else
+ ENDXFER;
+ if (fflush(outstr) == EOF)
+ goto file_err;
+#endif
+ 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:
+ ENDXFER;
+ reply(550, "Unimplemented TYPE %d in receive_data.", type);
+ return (-1);
+ }
+
+data_err:
+ ENDXFER;
+ perror_reply(426, "Data connection");
+ return (-1);
+
+file_err:
+ ENDXFER;
+ perror_reply(452, "Error writing to file");
+ return (-1);
+}
+
+void
+statfilecmd(char *filename)
+{
+ FILE *fin;
+ int atstart;
+ int c, code;
+ char line[LINE_MAX];
+ struct stat st;
+
+ code = lstat(filename, &st) == 0 && S_ISDIR(st.st_mode) ? 212 : 213;
+ (void)snprintf(line, sizeof(line), _PATH_LS " -lgA %s", filename);
+ fin = ftpd_popen(line, "r");
+ if (fin == NULL) {
+ perror_reply(551, filename);
+ return;
+ }
+ lreply(code, "Status of %s:", filename);
+ atstart = 1;
+ 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);
+ }
+ /*
+ * RFC 959 says neutral text should be prepended before
+ * a leading 3-digit number followed by whitespace, but
+ * many ftp clients can be confused by any leading digits,
+ * as a matter of fact.
+ */
+ if (atstart && isdigit(c))
+ (void) putc(' ', stdout);
+ (void) putc(c, stdout);
+ atstart = (c == '\n');
+ }
+ (void) ftpd_pclose(fin);
+ reply(code, "End of status.");
+}
+
+void
+statcmd(void)
+{
+ union sockunion *su;
+ u_char *a, *p;
+ char hname[NI_MAXHOST];
+ int ispassive;
+
+ if (hostinfo) {
+ lreply(211, "%s FTP server status:", hostname);
+ printf(" %s\r\n", version);
+ } else
+ lreply(211, "FTP server status:");
+ printf(" Connected to %s", remotehost);
+ if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
+ hname, sizeof(hname) - 1, NULL, 0, NI_NUMERICHOST)) {
+ hname[sizeof(hname) - 1] = 0;
+ if (strcmp(hname, remotehost) != 0)
+ printf(" (%s)", hname);
+ }
+ 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 CHAR_BIT == 8
+ printf(" %d", CHAR_BIT);
+#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) {
+ ispassive = 1;
+ su = &pasv_addr;
+ goto printaddr;
+ } else if (usedefault == 0) {
+ ispassive = 0;
+ su = &data_dest;
+printaddr:
+#define UC(b) (((int) b) & 0xff)
+ if (epsvall) {
+ printf(" EPSV only mode (EPSV ALL)\r\n");
+ goto epsvonly;
+ }
+
+ /* PORT/PASV */
+ if (su->su_family == AF_INET) {
+ a = (u_char *) &su->su_sin.sin_addr;
+ p = (u_char *) &su->su_sin.sin_port;
+ printf(" %s (%d,%d,%d,%d,%d,%d)\r\n",
+ ispassive ? "PASV" : "PORT",
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(p[0]), UC(p[1]));
+ }
+
+ /* LPRT/LPSV */
+ {
+ int alen, af, i;
+
+ switch (su->su_family) {
+ case AF_INET:
+ a = (u_char *) &su->su_sin.sin_addr;
+ p = (u_char *) &su->su_sin.sin_port;
+ alen = sizeof(su->su_sin.sin_addr);
+ af = 4;
+ break;
+ case AF_INET6:
+ a = (u_char *) &su->su_sin6.sin6_addr;
+ p = (u_char *) &su->su_sin6.sin6_port;
+ alen = sizeof(su->su_sin6.sin6_addr);
+ af = 6;
+ break;
+ default:
+ af = 0;
+ break;
+ }
+ if (af) {
+ printf(" %s (%d,%d,", ispassive ? "LPSV" : "LPRT",
+ af, alen);
+ for (i = 0; i < alen; i++)
+ printf("%d,", UC(a[i]));
+ printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1]));
+ }
+ }
+
+epsvonly:;
+ /* EPRT/EPSV */
+ {
+ int af;
+
+ switch (su->su_family) {
+ case AF_INET:
+ af = 1;
+ break;
+ case AF_INET6:
+ af = 2;
+ break;
+ default:
+ af = 0;
+ break;
+ }
+ if (af) {
+ union sockunion tmp;
+
+ tmp = *su;
+ if (tmp.su_family == AF_INET6)
+ tmp.su_sin6.sin6_scope_id = 0;
+ if (!getnameinfo((struct sockaddr *)&tmp, tmp.su_len,
+ hname, sizeof(hname) - 1, NULL, 0,
+ NI_NUMERICHOST)) {
+ hname[sizeof(hname) - 1] = 0;
+ printf(" %s |%d|%s|%d|\r\n",
+ ispassive ? "EPSV" : "EPRT",
+ af, hname, htons(tmp.su_port));
+ }
+ }
+ }
+#undef UC
+ } else
+ printf(" No data connection\r\n");
+ reply(211, "End of status.");
+}
+
+void
+fatalerror(char *s)
+{
+
+ reply(451, "Error in server: %s", s);
+ reply(221, "Closing connection due to server error.");
+ dologout(0);
+ /* NOTREACHED */
+}
+
+void
+reply(int n, const char *fmt, ...)
+{
+ va_list ap;
+
+ (void)printf("%d ", n);
+ va_start(ap, fmt);
+ (void)vprintf(fmt, ap);
+ va_end(ap);
+ (void)printf("\r\n");
+ (void)fflush(stdout);
+ if (ftpdebug) {
+ syslog(LOG_DEBUG, "<--- %d ", n);
+ va_start(ap, fmt);
+ vsyslog(LOG_DEBUG, fmt, ap);
+ va_end(ap);
+ }
+}
+
+void
+lreply(int n, const char *fmt, ...)
+{
+ va_list ap;
+
+ (void)printf("%d- ", n);
+ va_start(ap, fmt);
+ (void)vprintf(fmt, ap);
+ va_end(ap);
+ (void)printf("\r\n");
+ (void)fflush(stdout);
+ if (ftpdebug) {
+ syslog(LOG_DEBUG, "<--- %d- ", n);
+ va_start(ap, fmt);
+ vsyslog(LOG_DEBUG, fmt, ap);
+ va_end(ap);
+ }
+}
+
+static void
+ack(char *s)
+{
+
+ reply(250, "%s command successful.", s);
+}
+
+void
+nack(char *s)
+{
+
+ reply(502, "%s command not implemented.", s);
+}
+
+/* ARGSUSED */
+void
+yyerror(char *s)
+{
+ char *cp;
+
+ if ((cp = strchr(cbuf,'\n')))
+ *cp = '\0';
+ reply(500, "%s: command not understood.", cbuf);
+}
+
+void
+delete(char *name)
+{
+ struct stat st;
+
+ LOGCMD("delete", name);
+ if (lstat(name, &st) < 0) {
+ perror_reply(550, name);
+ return;
+ }
+ if (S_ISDIR(st.st_mode)) {
+ if (rmdir(name) < 0) {
+ perror_reply(550, name);
+ return;
+ }
+ goto done;
+ }
+ if (guest && noguestmod) {
+ reply(550, "Operation not permitted.");
+ return;
+ }
+ if (unlink(name) < 0) {
+ perror_reply(550, name);
+ return;
+ }
+done:
+ ack("DELE");
+}
+
+void
+cwd(char *path)
+{
+
+ if (chdir(path) < 0)
+ perror_reply(550, path);
+ else
+ ack("CWD");
+}
+
+void
+makedir(char *name)
+{
+ char *s;
+
+ LOGCMD("mkdir", name);
+ if (guest && noguestmkd)
+ reply(550, "Operation not permitted.");
+ else if (mkdir(name, 0777) < 0)
+ perror_reply(550, name);
+ else {
+ if ((s = doublequote(name)) == NULL)
+ fatalerror("Ran out of memory.");
+ reply(257, "\"%s\" directory created.", s);
+ free(s);
+ }
+}
+
+void
+removedir(char *name)
+{
+
+ LOGCMD("rmdir", name);
+ if (rmdir(name) < 0)
+ perror_reply(550, name);
+ else
+ ack("RMD");
+}
+
+void
+pwd(void)
+{
+ char *s, path[MAXPATHLEN + 1];
+
+ if (getcwd(path, sizeof(path)) == NULL)
+ perror_reply(550, "Get current directory");
+ else {
+ if ((s = doublequote(path)) == NULL)
+ fatalerror("Ran out of memory.");
+ reply(257, "\"%s\" is current directory.", s);
+ free(s);
+ }
+}
+
+char *
+renamefrom(char *name)
+{
+ struct stat st;
+
+ if (guest && noguestmod) {
+ reply(550, "Operation not permitted.");
+ return (NULL);
+ }
+ if (lstat(name, &st) < 0) {
+ perror_reply(550, name);
+ return (NULL);
+ }
+ reply(350, "File exists, ready for destination name.");
+ return (name);
+}
+
+void
+renamecmd(char *from, char *to)
+{
+ struct stat st;
+
+ LOGCMD2("rename", from, to);
+
+ if (guest && (stat(to, &st) == 0)) {
+ reply(550, "%s: permission denied.", to);
+ return;
+ }
+
+ if (rename(from, to) < 0)
+ perror_reply(550, "rename");
+ else
+ ack("RNTO");
+}
+
+static void
+dolog(struct sockaddr *who)
+{
+ char who_name[NI_MAXHOST];
+
+ realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len);
+ remotehost[sizeof(remotehost) - 1] = 0;
+ if (getnameinfo(who, who->sa_len,
+ who_name, sizeof(who_name) - 1, NULL, 0, NI_NUMERICHOST))
+ *who_name = 0;
+ who_name[sizeof(who_name) - 1] = 0;
+
+#ifdef SETPROCTITLE
+#ifdef VIRTUAL_HOSTING
+ if (thishost != firsthost)
+ snprintf(proctitle, sizeof(proctitle), "%s: connected (to %s)",
+ remotehost, hostname);
+ else
+#endif
+ snprintf(proctitle, sizeof(proctitle), "%s: connected",
+ remotehost);
+ setproctitle("%s", proctitle);
+#endif /* SETPROCTITLE */
+
+ if (logging) {
+#ifdef VIRTUAL_HOSTING
+ if (thishost != firsthost)
+ syslog(LOG_INFO, "connection from %s (%s) to %s",
+ remotehost, who_name, hostname);
+ else
+#endif
+ syslog(LOG_INFO, "connection from %s (%s)",
+ remotehost, who_name);
+ }
+}
+
+/*
+ * Record logout in wtmp file
+ * and exit with supplied status.
+ */
+void
+dologout(int status)
+{
+
+ if (logged_in && dowtmp) {
+ (void) seteuid(0);
+ ftpd_logwtmp(wtmpid, NULL, NULL);
+ }
+ /* beware of flushing buffers after a SIGPIPE */
+ _exit(status);
+}
+
+static void
+sigurg(int signo)
+{
+
+ recvurg = 1;
+}
+
+static void
+maskurg(int flag)
+{
+ int oerrno;
+ sigset_t sset;
+
+ if (!transflag) {
+ syslog(LOG_ERR, "Internal: maskurg() while no transfer");
+ return;
+ }
+ oerrno = errno;
+ sigemptyset(&sset);
+ sigaddset(&sset, SIGURG);
+ sigprocmask(flag ? SIG_BLOCK : SIG_UNBLOCK, &sset, NULL);
+ errno = oerrno;
+}
+
+static void
+flagxfer(int flag)
+{
+
+ if (flag) {
+ if (transflag)
+ syslog(LOG_ERR, "Internal: flagxfer(1): "
+ "transfer already under way");
+ transflag = 1;
+ maskurg(0);
+ recvurg = 0;
+ } else {
+ if (!transflag)
+ syslog(LOG_ERR, "Internal: flagxfer(0): "
+ "no active transfer");
+ maskurg(1);
+ transflag = 0;
+ }
+}
+
+/*
+ * Returns 0 if OK to resume or -1 if abort requested.
+ */
+static int
+myoob(void)
+{
+ char *cp;
+ int ret;
+
+ if (!transflag) {
+ syslog(LOG_ERR, "Internal: myoob() while no transfer");
+ return (0);
+ }
+ cp = tmpline;
+ ret = getline(cp, 7, stdin);
+ if (ret == -1) {
+ reply(221, "You could at least say goodbye.");
+ dologout(0);
+ } else if (ret == -2) {
+ /* Ignore truncated command. */
+ return (0);
+ }
+ upper(cp);
+ if (strcmp(cp, "ABOR\r\n") == 0) {
+ tmpline[0] = '\0';
+ reply(426, "Transfer aborted. Data connection closed.");
+ reply(226, "Abort successful.");
+ return (-1);
+ }
+ if (strcmp(cp, "STAT\r\n") == 0) {
+ tmpline[0] = '\0';
+ if (file_size != -1)
+ reply(213, "Status: %jd of %jd bytes transferred.",
+ (intmax_t)byte_count, (intmax_t)file_size);
+ else
+ reply(213, "Status: %jd bytes transferred.",
+ (intmax_t)byte_count);
+ }
+ return (0);
+}
+
+/*
+ * 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(void)
+{
+ socklen_t len;
+ int on;
+ char *p, *a;
+
+ if (pdata >= 0) /* close old port if one set */
+ close(pdata);
+
+ pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
+ if (pdata < 0) {
+ perror_reply(425, "Can't open passive connection");
+ return;
+ }
+ on = 1;
+ if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
+ syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m");
+
+ (void) seteuid(0);
+
+#ifdef IP_PORTRANGE
+ if (ctrl_addr.su_family == AF_INET) {
+ on = restricted_data_ports ? IP_PORTRANGE_HIGH
+ : IP_PORTRANGE_DEFAULT;
+
+ if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE,
+ &on, sizeof(on)) < 0)
+ goto pasv_error;
+ }
+#endif
+#ifdef IPV6_PORTRANGE
+ if (ctrl_addr.su_family == AF_INET6) {
+ on = restricted_data_ports ? IPV6_PORTRANGE_HIGH
+ : IPV6_PORTRANGE_DEFAULT;
+
+ if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE,
+ &on, sizeof(on)) < 0)
+ goto pasv_error;
+ }
+#endif
+
+ pasv_addr = ctrl_addr;
+ pasv_addr.su_port = 0;
+ if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0)
+ goto pasv_error;
+
+ (void) seteuid(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;
+ if (pasv_addr.su_family == AF_INET)
+ a = (char *) &pasv_addr.su_sin.sin_addr;
+ else if (pasv_addr.su_family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr))
+ a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12];
+ else
+ goto pasv_error;
+
+ p = (char *) &pasv_addr.su_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) seteuid(pw->pw_uid);
+ (void) close(pdata);
+ pdata = -1;
+ perror_reply(425, "Can't open passive connection");
+ return;
+}
+
+/*
+ * Long Passive defined in RFC 1639.
+ * 228 Entering Long Passive Mode
+ * (af, hal, h1, h2, h3,..., pal, p1, p2...)
+ */
+
+void
+long_passive(char *cmd, int pf)
+{
+ socklen_t len;
+ int on;
+ char *p, *a;
+
+ if (pdata >= 0) /* close old port if one set */
+ close(pdata);
+
+ if (pf != PF_UNSPEC) {
+ if (ctrl_addr.su_family != pf) {
+ switch (ctrl_addr.su_family) {
+ case AF_INET:
+ pf = 1;
+ break;
+ case AF_INET6:
+ pf = 2;
+ break;
+ default:
+ pf = 0;
+ break;
+ }
+ /*
+ * XXX
+ * only EPRT/EPSV ready clients will understand this
+ */
+ if (strcmp(cmd, "EPSV") == 0 && pf) {
+ reply(522, "Network protocol mismatch, "
+ "use (%d)", pf);
+ } else
+ reply(501, "Network protocol mismatch."); /*XXX*/
+
+ return;
+ }
+ }
+
+ pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
+ if (pdata < 0) {
+ perror_reply(425, "Can't open passive connection");
+ return;
+ }
+ on = 1;
+ if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
+ syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m");
+
+ (void) seteuid(0);
+
+ pasv_addr = ctrl_addr;
+ pasv_addr.su_port = 0;
+ len = pasv_addr.su_len;
+
+#ifdef IP_PORTRANGE
+ if (ctrl_addr.su_family == AF_INET) {
+ on = restricted_data_ports ? IP_PORTRANGE_HIGH
+ : IP_PORTRANGE_DEFAULT;
+
+ if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE,
+ &on, sizeof(on)) < 0)
+ goto pasv_error;
+ }
+#endif
+#ifdef IPV6_PORTRANGE
+ if (ctrl_addr.su_family == AF_INET6) {
+ on = restricted_data_ports ? IPV6_PORTRANGE_HIGH
+ : IPV6_PORTRANGE_DEFAULT;
+
+ if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE,
+ &on, sizeof(on)) < 0)
+ goto pasv_error;
+ }
+#endif
+
+ if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0)
+ goto pasv_error;
+
+ (void) seteuid(pw->pw_uid);
+
+ if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
+ goto pasv_error;
+ if (listen(pdata, 1) < 0)
+ goto pasv_error;
+
+#define UC(b) (((int) b) & 0xff)
+
+ if (strcmp(cmd, "LPSV") == 0) {
+ p = (char *)&pasv_addr.su_port;
+ switch (pasv_addr.su_family) {
+ case AF_INET:
+ a = (char *) &pasv_addr.su_sin.sin_addr;
+ v4_reply:
+ reply(228,
+"Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)",
+ 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ 2, UC(p[0]), UC(p[1]));
+ return;
+ case AF_INET6:
+ if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) {
+ a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12];
+ goto v4_reply;
+ }
+ a = (char *) &pasv_addr.su_sin6.sin6_addr;
+ reply(228,
+"Entering Long Passive Mode "
+"(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)",
+ 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
+ UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
+ UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
+ 2, UC(p[0]), UC(p[1]));
+ return;
+ }
+ } else if (strcmp(cmd, "EPSV") == 0) {
+ switch (pasv_addr.su_family) {
+ case AF_INET:
+ case AF_INET6:
+ reply(229, "Entering Extended Passive Mode (|||%d|)",
+ ntohs(pasv_addr.su_port));
+ return;
+ }
+ } else {
+ /* more proper error code? */
+ }
+
+pasv_error:
+ (void) seteuid(pw->pw_uid);
+ (void) close(pdata);
+ pdata = -1;
+ perror_reply(425, "Can't open passive connection");
+ return;
+}
+
+/*
+ * Generate unique name for file with basename "local"
+ * and open the file in order to avoid possible races.
+ * Try "local" first, then "local.1", "local.2" etc, up to "local.99".
+ * Return descriptor to the file, set "name" to its name.
+ *
+ * Generates failure reply on error.
+ */
+static int
+guniquefd(char *local, char **name)
+{
+ static char new[MAXPATHLEN];
+ struct stat st;
+ char *cp;
+ int count;
+ int fd;
+
+ cp = strrchr(local, '/');
+ if (cp)
+ *cp = '\0';
+ if (stat(cp ? local : ".", &st) < 0) {
+ perror_reply(553, cp ? local : ".");
+ return (-1);
+ }
+ if (cp) {
+ /*
+ * Let not overwrite dirname with counter suffix.
+ * -4 is for /nn\0
+ * In this extreme case dot won't be put in front of suffix.
+ */
+ if (strlen(local) > sizeof(new) - 4) {
+ reply(553, "Pathname too long.");
+ return (-1);
+ }
+ *cp = '/';
+ }
+ /* -4 is for the .nn<null> we put on the end below */
+ (void) snprintf(new, sizeof(new) - 4, "%s", local);
+ cp = new + strlen(new);
+ /*
+ * Don't generate dotfile unless requested explicitly.
+ * This covers the case when basename gets truncated off
+ * by buffer size.
+ */
+ if (cp > new && cp[-1] != '/')
+ *cp++ = '.';
+ for (count = 0; count < 100; count++) {
+ /* At count 0 try unmodified name */
+ if (count)
+ (void)sprintf(cp, "%d", count);
+ if ((fd = open(count ? new : local,
+ O_RDWR | O_CREAT | O_EXCL, 0666)) >= 0) {
+ *name = count ? new : local;
+ return (fd);
+ }
+ if (errno != EEXIST) {
+ perror_reply(553, count ? new : local);
+ return (-1);
+ }
+ }
+ reply(452, "Unique file name cannot be created.");
+ return (-1);
+}
+
+/*
+ * Format and send reply containing system error number.
+ */
+void
+perror_reply(int code, char *string)
+{
+
+ reply(code, "%s: %s.", string, strerror(errno));
+}
+
+static char *onefile[] = {
+ "",
+ 0
+};
+
+void
+send_file_list(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_TILDE;
+
+ memset(&gl, 0, sizeof(gl));
+ gl.gl_matchc = MAXGLOBARGS;
+ flags |= GLOB_LIMIT;
+ freeglob = 1;
+ if (glob(whichf, flags, 0, &gl)) {
+ reply(550, "No matching files 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;
+ }
+
+ 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 &&
+ dout == NULL)
+ retrieve(_PATH_LS " %s", dirname);
+ else
+ perror_reply(550, whichf);
+ goto out;
+ }
+
+ if (S_ISREG(st.st_mode)) {
+ if (dout == NULL) {
+ dout = dataconn("file list", -1, "w");
+ if (dout == NULL)
+ goto out;
+ STARTXFER;
+ }
+ START_UNSAFE;
+ fprintf(dout, "%s%s\n", dirname,
+ type == TYPE_A ? "\r" : "");
+ END_UNSAFE;
+ if (ferror(dout))
+ goto data_err;
+ byte_count += strlen(dirname) +
+ (type == TYPE_A ? 2 : 1);
+ CHECKOOB(goto abrt);
+ continue;
+ } else if (!S_ISDIR(st.st_mode))
+ continue;
+
+ if ((dirp = opendir(dirname)) == NULL)
+ continue;
+
+ while ((dir = readdir(dirp)) != NULL) {
+ char nbuf[MAXPATHLEN];
+
+ CHECKOOB(goto abrt);
+
+ if (dir->d_name[0] == '.' && dir->d_namlen == 1)
+ continue;
+ if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
+ dir->d_namlen == 2)
+ continue;
+
+ snprintf(nbuf, sizeof(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", -1, "w");
+ if (dout == NULL)
+ goto out;
+ STARTXFER;
+ }
+ START_UNSAFE;
+ 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" : "");
+ END_UNSAFE;
+ if (ferror(dout))
+ goto data_err;
+ byte_count += strlen(nbuf) +
+ (type == TYPE_A ? 2 : 1);
+ CHECKOOB(goto abrt);
+ }
+ }
+ (void) closedir(dirp);
+ dirp = NULL;
+ }
+
+ if (dout == NULL)
+ reply(550, "No files found.");
+ else if (ferror(dout))
+data_err: perror_reply(550, "Data connection");
+ else
+ reply(226, "Transfer complete.");
+out:
+ if (dout) {
+ ENDXFER;
+abrt:
+ (void) fclose(dout);
+ data = -1;
+ pdata = -1;
+ }
+ if (dirp)
+ (void) closedir(dirp);
+ if (freeglob) {
+ freeglob = 0;
+ globfree(&gl);
+ }
+}
+
+void
+reapchild(int signo)
+{
+ while (waitpid(-1, NULL, WNOHANG) > 0);
+}
+
+#ifdef OLD_SETPROCTITLE
+/*
+ * Clobber argv so ps will show what we're doing. (Stolen from sendmail.)
+ * Warning, since this is usually started from inetd.conf, it often doesn't
+ * have much of an environment or arglist to overwrite.
+ */
+void
+setproctitle(const char *fmt, ...)
+{
+ int i;
+ va_list ap;
+ char *p, *bp, ch;
+ char buf[LINE_MAX];
+
+ va_start(ap, fmt);
+ (void)vsnprintf(buf, sizeof(buf), fmt, ap);
+
+ /* make ps print our process name */
+ p = Argv[0];
+ *p++ = '-';
+
+ i = strlen(buf);
+ if (i > LastArgv - p - 2) {
+ i = LastArgv - p - 2;
+ buf[i] = '\0';
+ }
+ bp = buf;
+ while (ch = *bp++)
+ if (ch != '\n' && ch != '\r')
+ *p++ = ch;
+ while (p < LastArgv)
+ *p++ = ' ';
+}
+#endif /* OLD_SETPROCTITLE */
+
+static void
+appendf(char **strp, char *fmt, ...)
+{
+ va_list ap;
+ char *ostr, *p;
+
+ va_start(ap, fmt);
+ vasprintf(&p, fmt, ap);
+ va_end(ap);
+ if (p == NULL)
+ fatalerror("Ran out of memory.");
+ if (*strp == NULL)
+ *strp = p;
+ else {
+ ostr = *strp;
+ asprintf(strp, "%s%s", ostr, p);
+ if (*strp == NULL)
+ fatalerror("Ran out of memory.");
+ free(ostr);
+ }
+}
+
+static void
+logcmd(char *cmd, char *file1, char *file2, off_t cnt)
+{
+ char *msg = NULL;
+ char wd[MAXPATHLEN + 1];
+
+ if (logging <= 1)
+ return;
+
+ if (getcwd(wd, sizeof(wd) - 1) == NULL)
+ strcpy(wd, strerror(errno));
+
+ appendf(&msg, "%s", cmd);
+ if (file1)
+ appendf(&msg, " %s", file1);
+ if (file2)
+ appendf(&msg, " %s", file2);
+ if (cnt >= 0)
+ appendf(&msg, " = %jd bytes", (intmax_t)cnt);
+ appendf(&msg, " (wd: %s", wd);
+ if (guest || dochroot)
+ appendf(&msg, "; chrooted");
+ appendf(&msg, ")");
+ syslog(LOG_INFO, "%s", msg);
+ free(msg);
+}
+
+static void
+logxfer(char *name, off_t size, time_t start)
+{
+ char buf[MAXPATHLEN + 1024];
+ char path[MAXPATHLEN + 1];
+ time_t now;
+
+ if (statfd >= 0) {
+ time(&now);
+ if (realpath(name, path) == NULL) {
+ syslog(LOG_NOTICE, "realpath failed on %s: %m", path);
+ return;
+ }
+ snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s!%jd!%ld\n",
+ ctime(&now)+4, ident, remotehost,
+ path, (intmax_t)size,
+ (long)(now - start + (now == start)));
+ write(statfd, buf, strlen(buf));
+ }
+}
+
+static char *
+doublequote(char *s)
+{
+ int n;
+ char *p, *s2;
+
+ for (p = s, n = 0; *p; p++)
+ if (*p == '"')
+ n++;
+
+ if ((s2 = malloc(p - s + n + 1)) == NULL)
+ return (NULL);
+
+ for (p = s2; *s; s++, p++) {
+ if ((*p = *s) == '"')
+ *(++p) = '"';
+ }
+ *p = '\0';
+
+ return (s2);
+}
+
+/* setup server socket for specified address family */
+/* if af is PF_UNSPEC more than one socket may be returned */
+/* the returned list is dynamically allocated, so caller needs to free it */
+static int *
+socksetup(int af, char *bindname, const char *bindport)
+{
+ struct addrinfo hints, *res, *r;
+ int error, maxs, *s, *socks;
+ const int on = 1;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = af;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(bindname, bindport, &hints, &res);
+ if (error) {
+ syslog(LOG_ERR, "%s", gai_strerror(error));
+ if (error == EAI_SYSTEM)
+ syslog(LOG_ERR, "%s", strerror(errno));
+ return NULL;
+ }
+
+ /* Count max number of sockets we may open */
+ for (maxs = 0, r = res; r; r = r->ai_next, maxs++)
+ ;
+ socks = malloc((maxs + 1) * sizeof(int));
+ if (!socks) {
+ freeaddrinfo(res);
+ syslog(LOG_ERR, "couldn't allocate memory for sockets");
+ return NULL;
+ }
+
+ *socks = 0; /* num of sockets counter at start of array */
+ s = socks + 1;
+ for (r = res; r; r = r->ai_next) {
+ *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
+ if (*s < 0) {
+ syslog(LOG_DEBUG, "control socket: %m");
+ continue;
+ }
+ if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR,
+ &on, sizeof(on)) < 0)
+ syslog(LOG_WARNING,
+ "control setsockopt (SO_REUSEADDR): %m");
+ if (r->ai_family == AF_INET6) {
+ if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY,
+ &on, sizeof(on)) < 0)
+ syslog(LOG_WARNING,
+ "control setsockopt (IPV6_V6ONLY): %m");
+ }
+ if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) {
+ syslog(LOG_DEBUG, "control bind: %m");
+ close(*s);
+ continue;
+ }
+ (*socks)++;
+ s++;
+ }
+
+ if (res)
+ freeaddrinfo(res);
+
+ if (*socks == 0) {
+ syslog(LOG_ERR, "control socket: Couldn't bind to any socket");
+ free(socks);
+ return NULL;
+ }
+ return(socks);
+}
diff --git a/libexec/ftpd/logwtmp.c b/libexec/ftpd/logwtmp.c
new file mode 100644
index 0000000..9beade3
--- /dev/null
+++ b/libexec/ftpd/logwtmp.c
@@ -0,0 +1,77 @@
+/*
+ * 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. 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
+#if 0
+static char sccsid[] = "@(#)logwtmp.c 8.1 (Berkeley) 6/4/93";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <libutil.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <utmpx.h>
+#include "extern.h"
+
+void
+ftpd_logwtmp(char *id, char *user, struct sockaddr *addr)
+{
+ struct utmpx ut;
+
+ memset(&ut, 0, sizeof(ut));
+
+ if (user != NULL) {
+ /* Log in. */
+ ut.ut_type = USER_PROCESS;
+ (void)strncpy(ut.ut_user, user, sizeof(ut.ut_user));
+ if (addr != NULL)
+ realhostname_sa(ut.ut_host, sizeof(ut.ut_host),
+ addr, addr->sa_len);
+ } else {
+ /* Log out. */
+ ut.ut_type = DEAD_PROCESS;
+ }
+
+ ut.ut_pid = getpid();
+ gettimeofday(&ut.ut_tv, NULL);
+ (void)strncpy(ut.ut_id, id, sizeof(ut.ut_id));
+ (void)strncpy(ut.ut_line, "ftpd", sizeof(ut.ut_line));
+
+ pututxline(&ut);
+}
diff --git a/libexec/ftpd/pathnames.h b/libexec/ftpd/pathnames.h
new file mode 100644
index 0000000..2083034
--- /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. 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
+ * $FreeBSD$
+ */
+
+#include <paths.h>
+
+#define _PATH_FTPCHROOT "/etc/ftpchroot"
+#define _PATH_FTPWELCOME "/etc/ftpwelcome"
+#define _PATH_FTPLOGINMESG "/etc/ftpmotd"
+#define _PATH_FTPHOSTS "/etc/ftphosts"
+#define _PATH_FTPDSTATFILE "/var/log/ftpd"
+#define _PATH_LS "/bin/ls"
diff --git a/libexec/ftpd/popen.c b/libexec/ftpd/popen.c
new file mode 100644
index 0000000..03b8f7a
--- /dev/null
+++ b/libexec/ftpd/popen.c
@@ -0,0 +1,201 @@
+/*
+ * 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. 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
+#if 0
+static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 4/6/94";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <netinet/in.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"
+#include "pathnames.h"
+#include <syslog.h>
+#include <time.h>
+
+#define MAXUSRARGS 100
+#define MAXGLOBARGS 1000
+
+/*
+ * 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(char *program, char *type)
+{
+ char *cp;
+ FILE *iop;
+ int argc, gargc, pdes[2], pid;
+ char **pop, *argv[MAXUSRARGS], *gargv[MAXGLOBARGS];
+
+ if (((*type != 'r') && (*type != 'w')) || type[1])
+ return (NULL);
+
+ if (!pids) {
+ if ((fds = getdtablesize()) <= 0)
+ return (NULL);
+ if ((pids = malloc(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; argc < MAXUSRARGS; cp = NULL) {
+ if (!(argv[argc++] = strtok(cp, " \t\n")))
+ break;
+ }
+ argv[argc - 1] = NULL;
+
+ /* glob each piece */
+ gargv[0] = argv[0];
+ for (gargc = argc = 1; argv[argc] && gargc < (MAXGLOBARGS-1); argc++) {
+ glob_t gl;
+ int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
+
+ memset(&gl, 0, sizeof(gl));
+ gl.gl_matchc = MAXGLOBARGS;
+ flags |= GLOB_LIMIT;
+ if (glob(argv[argc], flags, NULL, &gl))
+ gargv[gargc++] = strdup(argv[argc]);
+ else if (gl.gl_pathc > 0) {
+ for (pop = gl.gl_pathv; *pop && gargc < (MAXGLOBARGS-1);
+ pop++)
+ gargv[gargc++] = strdup(*pop);
+ }
+ globfree(&gl);
+ }
+ gargv[gargc] = NULL;
+
+ iop = NULL;
+ fflush(NULL);
+ pid = (strcmp(gargv[0], _PATH_LS) == 0) ? fork() : vfork();
+ switch(pid) {
+ 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]);
+ }
+ /* Drop privileges before proceeding */
+ if (getuid() != geteuid() && setuid(geteuid()) < 0)
+ _exit(1);
+ if (strcmp(gargv[0], _PATH_LS) == 0) {
+ /* Reset getopt for ls_main() */
+ optreset = optind = optopt = 1;
+ /* Close syslogging to remove pwd.db missing msgs */
+ closelog();
+ /* Trigger to sense new /etc/localtime after chroot */
+ if (getenv("TZ") == NULL) {
+ setenv("TZ", "", 0);
+ tzset();
+ unsetenv("TZ");
+ tzset();
+ }
+ exit(ls_main(gargc, gargv));
+ }
+ 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(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/getty/Makefile b/libexec/getty/Makefile
new file mode 100644
index 0000000..f967958
--- /dev/null
+++ b/libexec/getty/Makefile
@@ -0,0 +1,14 @@
+# from: @(#)Makefile 8.1 (Berkeley) 6/4/93
+# $FreeBSD$
+
+PROG= getty
+SRCS= main.c init.c subr.c chat.c
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+MAN= gettytab.5 ttys.5 getty.8
+
+WARNS?= 1
+WFORMAT=0
+
+.include <bsd.prog.mk>
+
diff --git a/libexec/getty/chat.c b/libexec/getty/chat.c
new file mode 100644
index 0000000..4817c97
--- /dev/null
+++ b/libexec/getty/chat.c
@@ -0,0 +1,488 @@
+/*-
+ * Copyright (c) 1997
+ * David L Nugent <davidn@blaze.net.au>.
+ * All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, is permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * 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. This work was done expressly for inclusion into FreeBSD. Other use
+ * is permitted provided this notation is included.
+ * 4. Absolutely no warranty of function or purpose is made by the authors.
+ * 5. Modifications may be freely made to this file providing the above
+ * conditions are met.
+ *
+ * Modem chat module - send/expect style functions for getty
+ * For semi-intelligent modem handling.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+
+#include <ctype.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "gettytab.h"
+#include "extern.h"
+
+#define PAUSE_CH (unsigned char)'\xff' /* pause kludge */
+
+#define CHATDEBUG_RECEIVE 0x01
+#define CHATDEBUG_SEND 0x02
+#define CHATDEBUG_EXPECT 0x04
+#define CHATDEBUG_MISC 0x08
+
+#define CHATDEBUG_DEFAULT 0
+#define CHAT_DEFAULT_TIMEOUT 10
+
+
+static int chat_debug = CHATDEBUG_DEFAULT;
+static int chat_alarm = CHAT_DEFAULT_TIMEOUT; /* Default */
+
+static volatile int alarmed = 0;
+
+
+static void chat_alrm(int);
+static int chat_unalarm(void);
+static int getdigit(unsigned char **, int, int);
+static char **read_chat(char **);
+static char *cleanchr(char **, unsigned char);
+static char *cleanstr(const unsigned char *, int);
+static const char *result(int);
+static int chat_expect(const char *);
+static int chat_send(char const *);
+
+
+/*
+ * alarm signal handler
+ * handle timeouts in read/write
+ * change stdin to non-blocking mode to prevent
+ * possible hang in read().
+ */
+
+static void
+chat_alrm(int signo __unused)
+{
+ int on = 1;
+
+ alarm(1);
+ alarmed = 1;
+ signal(SIGALRM, chat_alrm);
+ ioctl(STDIN_FILENO, FIONBIO, &on);
+}
+
+
+/*
+ * Turn back on blocking mode reset by chat_alrm()
+ */
+
+static int
+chat_unalarm(void)
+{
+ int off = 0;
+ return ioctl(STDIN_FILENO, FIONBIO, &off);
+}
+
+
+/*
+ * convert a string of a given base (octal/hex) to binary
+ */
+
+static int
+getdigit(unsigned char **ptr, int base, int max)
+{
+ int i, val = 0;
+ char * q;
+
+ static const char xdigits[] = "0123456789abcdef";
+
+ for (i = 0, q = *ptr; i++ < max; ++q) {
+ int sval;
+ const char * s = strchr(xdigits, tolower(*q));
+
+ if (s == NULL || (sval = s - xdigits) >= base)
+ break;
+ val = (val * base) + sval;
+ }
+ *ptr = q;
+ return val;
+}
+
+
+/*
+ * read_chat()
+ * Convert a whitespace delimtied string into an array
+ * of strings, being expect/send pairs
+ */
+
+static char **
+read_chat(char **chatstr)
+{
+ char *str = *chatstr;
+ char **res = NULL;
+
+ if (str != NULL) {
+ char *tmp = NULL;
+ int l;
+
+ if ((l=strlen(str)) > 0 && (tmp=malloc(l + 1)) != NULL &&
+ (res=malloc((l / 2 + 1) * sizeof(char *))) != NULL) {
+ static char ws[] = " \t";
+ char * p;
+
+ for (l = 0, p = strtok(strcpy(tmp, str), ws);
+ p != NULL;
+ p = strtok(NULL, ws))
+ {
+ unsigned char *q, *r;
+
+ /* Read escapes */
+ for (q = r = (unsigned char *)p; *r; ++q)
+ {
+ if (*q == '\\')
+ {
+ /* handle special escapes */
+ switch (*++q)
+ {
+ case 'a': /* bell */
+ *r++ = '\a';
+ break;
+ case 'r': /* cr */
+ *r++ = '\r';
+ break;
+ case 'n': /* nl */
+ *r++ = '\n';
+ break;
+ case 'f': /* ff */
+ *r++ = '\f';
+ break;
+ case 'b': /* bs */
+ *r++ = '\b';
+ break;
+ case 'e': /* esc */
+ *r++ = 27;
+ break;
+ case 't': /* tab */
+ *r++ = '\t';
+ break;
+ case 'p': /* pause */
+ *r++ = PAUSE_CH;
+ break;
+ case 's':
+ case 'S': /* space */
+ *r++ = ' ';
+ break;
+ case 'x': /* hexdigit */
+ ++q;
+ *r++ = getdigit(&q, 16, 2);
+ --q;
+ break;
+ case '0': /* octal */
+ ++q;
+ *r++ = getdigit(&q, 8, 3);
+ --q;
+ break;
+ default: /* literal */
+ *r++ = *q;
+ break;
+ case 0: /* not past eos */
+ --q;
+ break;
+ }
+ } else {
+ /* copy standard character */
+ *r++ = *q;
+ }
+ }
+
+ /* Remove surrounding quotes, if any
+ */
+ if (*p == '"' || *p == '\'') {
+ q = strrchr(p+1, *p);
+ if (q != NULL && *q == *p && q[1] == '\0') {
+ *q = '\0';
+ strcpy(p, p+1);
+ }
+ }
+
+ res[l++] = p;
+ }
+ res[l] = NULL;
+ *chatstr = tmp;
+ return res;
+ }
+ free(tmp);
+ }
+ return res;
+}
+
+
+/*
+ * clean a character for display (ctrl/meta character)
+ */
+
+static char *
+cleanchr(char **buf, unsigned char ch)
+{
+ int l;
+ static char tmpbuf[5];
+ char * tmp = buf ? *buf : tmpbuf;
+
+ if (ch & 0x80) {
+ strcpy(tmp, "M-");
+ l = 2;
+ ch &= 0x7f;
+ } else
+ l = 0;
+
+ if (ch < 32) {
+ tmp[l++] = '^';
+ tmp[l++] = ch + '@';
+ } else if (ch == 127) {
+ tmp[l++] = '^';
+ tmp[l++] = '?';
+ } else
+ tmp[l++] = ch;
+ tmp[l] = '\0';
+
+ if (buf)
+ *buf = tmp + l;
+ return tmp;
+}
+
+
+/*
+ * clean a string for display (ctrl/meta characters)
+ */
+
+static char *
+cleanstr(const unsigned char *s, int l)
+{
+ static unsigned char * tmp = NULL;
+ static int tmplen = 0;
+
+ if (tmplen < l * 4 + 1)
+ tmp = realloc(tmp, tmplen = l * 4 + 1);
+
+ if (tmp == NULL) {
+ tmplen = 0;
+ return (char *)"(mem alloc error)";
+ } else {
+ int i = 0;
+ char * p = tmp;
+
+ while (i < l)
+ cleanchr(&p, s[i++]);
+ *p = '\0';
+ }
+
+ return tmp;
+}
+
+
+/*
+ * return result as a pseudo-english word
+ */
+
+static const char *
+result(int r)
+{
+ static const char * results[] = {
+ "OK", "MEMERROR", "IOERROR", "TIMEOUT"
+ };
+ return results[r & 3];
+}
+
+
+/*
+ * chat_expect()
+ * scan input for an expected string
+ */
+
+static int
+chat_expect(const char *str)
+{
+ int len, r = 0;
+
+ if (chat_debug & CHATDEBUG_EXPECT)
+ syslog(LOG_DEBUG, "chat_expect '%s'", cleanstr(str, strlen(str)));
+
+ if ((len = strlen(str)) > 0) {
+ int i = 0;
+ char * got;
+
+ if ((got = malloc(len + 1)) == NULL)
+ r = 1;
+ else {
+
+ memset(got, 0, len+1);
+ alarm(chat_alarm);
+ alarmed = 0;
+
+ while (r == 0 && i < len) {
+ if (alarmed)
+ r = 3;
+ else {
+ unsigned char ch;
+
+ if (read(STDIN_FILENO, &ch, 1) == 1) {
+
+ if (chat_debug & CHATDEBUG_RECEIVE)
+ syslog(LOG_DEBUG, "chat_recv '%s' m=%d",
+ cleanchr(NULL, ch), i);
+
+ if (ch == str[i])
+ got[i++] = ch;
+ else if (i > 0) {
+ int j = 1;
+
+ /* See if we can resync on a
+ * partial match in our buffer
+ */
+ while (j < i && memcmp(got + j, str, i - j) != 0)
+ j++;
+ if (j < i)
+ memcpy(got, got + j, i - j);
+ i -= j;
+ }
+ } else
+ r = alarmed ? 3 : 2;
+ }
+ }
+ alarm(0);
+ chat_unalarm();
+ alarmed = 0;
+ free(got);
+ }
+ }
+
+ if (chat_debug & CHATDEBUG_EXPECT)
+ syslog(LOG_DEBUG, "chat_expect %s", result(r));
+
+ return r;
+}
+
+
+/*
+ * chat_send()
+ * send a chat string
+ */
+
+static int
+chat_send(char const *str)
+{
+ int r = 0;
+
+ if (chat_debug & CHATDEBUG_SEND)
+ syslog(LOG_DEBUG, "chat_send '%s'", cleanstr(str, strlen(str)));
+
+ if (*str) {
+ alarm(chat_alarm);
+ alarmed = 0;
+ while (r == 0 && *str)
+ {
+ unsigned char ch = (unsigned char)*str++;
+
+ if (alarmed)
+ r = 3;
+ else if (ch == PAUSE_CH)
+ usleep(500000); /* 1/2 second */
+ else {
+ usleep(10000); /* be kind to modem */
+ if (write(STDOUT_FILENO, &ch, 1) != 1)
+ r = alarmed ? 3 : 2;
+ }
+ }
+ alarm(0);
+ chat_unalarm();
+ alarmed = 0;
+ }
+
+ if (chat_debug & CHATDEBUG_SEND)
+ syslog(LOG_DEBUG, "chat_send %s", result(r));
+
+ return r;
+}
+
+
+/*
+ * getty_chat()
+ *
+ * Termination codes:
+ * -1 - no script supplied
+ * 0 - script terminated correctly
+ * 1 - invalid argument, expect string too large, etc.
+ * 2 - error on an I/O operation or fatal error condition
+ * 3 - timeout waiting for a simple string
+ *
+ * Parameters:
+ * char *scrstr - unparsed chat script
+ * timeout - seconds timeout
+ * debug - debug value (bitmask)
+ */
+
+int
+getty_chat(char *scrstr, int timeout, int debug)
+{
+ int r = -1;
+
+ chat_alarm = timeout ? timeout : CHAT_DEFAULT_TIMEOUT;
+ chat_debug = debug;
+
+ if (scrstr != NULL) {
+ char **script;
+
+ if (chat_debug & CHATDEBUG_MISC)
+ syslog(LOG_DEBUG, "getty_chat script='%s'", scrstr);
+
+ if ((script = read_chat(&scrstr)) != NULL) {
+ int i = r = 0;
+ int off = 0;
+ sig_t old_alarm;
+
+ /*
+ * We need to be in raw mode for all this
+ * Rely on caller...
+ */
+
+ old_alarm = signal(SIGALRM, chat_alrm);
+ chat_unalarm(); /* Force blocking mode at start */
+
+ /*
+ * This is the send/expect loop
+ */
+ while (r == 0 && script[i] != NULL)
+ if ((r = chat_expect(script[i++])) == 0 && script[i] != NULL)
+ r = chat_send(script[i++]);
+
+ signal(SIGALRM, old_alarm);
+ free(script);
+ free(scrstr);
+
+ /*
+ * Ensure stdin is in blocking mode
+ */
+ ioctl(STDIN_FILENO, FIONBIO, &off);
+ }
+
+ if (chat_debug & CHATDEBUG_MISC)
+ syslog(LOG_DEBUG, "getty_chat %s", result(r));
+
+ }
+ return r;
+}
diff --git a/libexec/getty/extern.h b/libexec/getty/extern.h
new file mode 100644
index 0000000..c7b73a8
--- /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. 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: @(#)extern.h 8.1 (Berkeley) 6/4/93
+ * $FreeBSD$
+ */
+
+struct delayval;
+struct termios;
+
+extern char hostname[];
+extern int hopcount;
+extern struct termios tmode, omode;
+extern struct gettyflags gettyflags[];
+extern struct gettynums gettynums[];
+extern struct gettystrs gettystrs[];
+
+int adelay(int, struct delayval *);
+const char *autobaud(void);
+int delaybits(void);
+void edithost(const char *);
+void gendefaults(void);
+void gettable(const char *, char *);
+void makeenv(char *[]);
+const char *portselector(void);
+void set_ttydefaults(int);
+void setchars(void);
+void setdefaults(void);
+void set_flags(int);
+int speed(int);
+int getty_chat(char *, int, int);
diff --git a/libexec/getty/getty.8 b/libexec/getty/getty.8
new file mode 100644
index 0000000..0f3bca7
--- /dev/null
+++ b/libexec/getty/getty.8
@@ -0,0 +1,124 @@
+.\" 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. 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: @(#)getty.8 8.1 (Berkeley) 6/4/93
+.\" $FreeBSD$
+.\" "
+.Dd June 4, 1993
+.Dt GETTY 8
+.Os
+.Sh NAME
+.Nm getty
+.Nd set terminal mode
+.Sh SYNOPSIS
+.Nm
+.Oo
+.Ar type
+.Op Ar tty
+.Oc
+.Sh DESCRIPTION
+The
+.Nm
+utility 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
+.Sq Fl ,
+the tty line is assumed to be open as file descriptor 0.
+.Pp
+The
+.Ar type
+argument can be used to make
+.Nm
+treat the terminal line specially.
+This argument is used as an index into the
+.Xr 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
+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
+can be circumvented, or modified, by a suitable
+.Pa gettytab
+table.
+.Pp
+The
+.Nm
+utility 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 FILES
+.Bl -tag -width /etc/gettytab -compact
+.It Pa /etc/gettytab
+.It Pa /etc/ttys
+.El
+.Sh DIAGNOSTICS
+.Bl -diag
+.It "ttyxx: No such device or address."
+.It "ttyxx: No such file or address."
+.Pp
+A terminal which is turned
+on in the
+.Pa 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 SEE ALSO
+.Xr login 1 ,
+.Xr ioctl 2 ,
+.Xr tty 4 ,
+.Xr gettytab 5 ,
+.Xr ttys 5 ,
+.Xr init 8
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.At v6 .
diff --git a/libexec/getty/gettytab.5 b/libexec/getty/gettytab.5
new file mode 100644
index 0000000..16cd15a
--- /dev/null
+++ b/libexec/getty/gettytab.5
@@ -0,0 +1,537 @@
+.\" 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. 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: @(#)gettytab.5 8.4 (Berkeley) 4/19/94
+.\" $FreeBSD$
+.\" "
+.Dd April 19, 1994
+.Dt GETTYTAB 5
+.Os
+.Sh NAME
+.Nm gettytab
+.Nd terminal configuration data base
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+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
+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,
+.Va default ,
+that is used to set global defaults for all other classes.
+(That is, the
+.Va 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
+.Va default
+column below lists defaults obtained if there is
+no entry in the table obtained, nor one in the special
+.Va default
+table.
+.Bl -column Name Type /usr/bin/login
+.It Sy "Name Type Default Description"
+.It "ac str unused expect-send chat script for modem answer"
+.It "al str unused user to auto-login instead of prompting"
+.It "ap bool false terminal uses any parity"
+.It "bk str 0377 alternate end of line character (input break)"
+.It "c0 num unused tty control flags to write messages"
+.It "c1 num unused tty control flags to read login name"
+.It "c2 num unused tty control flags to leave terminal as"
+.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 "ct num 10 chat timeout for"
+.Va \&ac
+and
+.Va \&ic
+scripts
+.It "dc num 0 chat debug bitmask"
+.It "de num 0 delay secs and flush input before writing first prompt"
+.It "df str %+ the" Xr strftime 3 "format used for \&%d in the banner message"
+.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"
+.Em 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 "fl str" Ta So Li ^O Sc Ta
+.No "output flush character"
+.It "hc bool false do"
+.Em 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 "hw bool false do cts/rts hardware flow control"
+.It "i0 num unused tty input flags to write messages"
+.It "i1 num unused tty input flags to read login name"
+.It "i2 num unused tty input flags to leave terminal as"
+.It "ic str unused expect-send chat script for modem initialization"
+.It "if str unused display named file before prompt, like /etc/issue"
+.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 "l0 num unused tty local flags to write messages"
+.It "l1 num unused tty local flags to read login name"
+.It "l2 num unused tty local flags to leave terminal as"
+.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 "mb bool false do flow control based on carrier"
+.It "nc bool false terminal does not supply carrier (set clocal)"
+.It "nl bool false terminal has (or might have) a newline character"
+.It "np bool false terminal uses no parity (i.e. 8-bit characters)"
+.It "nx str default next table (for auto speed selection)"
+.It "o0 num unused tty output flags to write messages"
+.It "o1 num unused tty output flags to read login name"
+.It "o2 num unused tty output flags to leave terminal as"
+.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 "pl bool false start PPP login program unconditionally if"
+.Va \&pp
+is specified
+.It "pp str unused PPP login program"
+.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 "rt num unused ring timeout when using"
+.Va \&ac
+.It "rw bool false do"
+.Em 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 "we str" Ta So Li ^W Sc Ta
+.No "word erase character"
+.It "xc bool false do"
+.Em 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
+.It "Lo str C the locale name used for \&%d in the banner message"
+.El
+.Pp
+The following capabilities are no longer supported by
+.Xr getty 8 :
+.Bl -column Name Type /usr/bin/login
+.It "bd num 0 backspace delay"
+.It "cb bool false use crt backspace mode"
+.It "cd num 0 carriage-return delay"
+.It "fd num 0 form-feed (vertical motion) delay"
+.It "lc bool false terminal has lower case"
+.It "nd num 0 newline (line-feed) delay"
+.It "uc bool false terminal is known upper case only"
+.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
+.Va \&c0 ,
+.Va \&c1 ,
+.Va \&c2 ,
+.Va \&i0 ,
+.Va \&i1 ,
+.Va \&i2 ,
+.Va \&l0 ,
+.Va \&l1 ,
+.Va \&l2 ,
+.Va \&o0 ,
+.Va \&o1 ,
+or
+.Va \&o2
+numeric specifications, which can be used to specify
+(usually in octal, with a leading '0')
+the exact values of the flags.
+These flags correspond to the termios
+.Va c_cflag ,
+.Va c_iflag ,
+.Va c_lflag ,
+and
+.Va c_oflag
+fields, respectively.
+Each these sets must be completely specified to be effective.
+The
+.Va \&f0 ,
+.Va \&f1 ,
+and
+.Va \&f2
+are excepted for backwards compatibility with a previous incarnation of
+the TTY sub-system.
+In these flags the bottom 16 bits of the (32 bits)
+value contain the sgttyb
+.Va sg_flags
+field, while the top 16 bits represent the local mode word.
+.Pp
+Should
+.Xr getty 8
+receive a null character
+(presumed to indicate a line break)
+it will restart using the table indicated by the
+.Va \&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
+.Va \&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
+.Va \&pc .
+.Pp
+The initial message, login message, and initial file;
+.Va \&im ,
+.Va \&lm
+and
+.Va \&if
+may include any of the following character sequences, which expand to
+information about the environment in which
+.Xr getty 8
+is running.
+.Bl -tag -offset indent -width \&%xxxxxxxxxxxxxx
+.It \&%d
+The current date and time formatted according to the
+.Va \&Lo
+and
+.Va \&df
+strings.
+.It \&%h
+The hostname of the machine, which is normally obtained from the
+system using
+.Xr gethostname 3 ,
+but may also be overridden by the
+.Va \&hn
+table entry.
+In either case it may be edited with the
+.Va \&he
+string.
+A '@' in the
+.Va \&he
+string causes one character from the real hostname to
+be copied to the final hostname.
+A '#' in the
+.Va \&he
+string causes the next character of the real hostname
+to be skipped.
+Each character that
+is neither '@' nor '#' is copied into the final hostname.
+Surplus '@' and '#' characters are ignored.
+.It \&%t
+The tty name.
+.It "\&%m, \&%r, \&%s, \&%v"
+The type of machine, release of the operating system, name of the
+operating system, and version of the kernel, respectively, as
+returned by
+.Xr uname 3 .
+.It \&%%
+A
+.Dq %
+character.
+.El
+.Pp
+When getty execs the login process, given
+in the
+.Va \&lo
+string (usually
+.Dq Pa /usr/bin/login ) ,
+it will have set
+the environment to include the terminal type, as indicated
+by the
+.Va \&tt
+string (if it exists).
+The
+.Va \&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
+.Li name=value .
+.Pp
+If a non-zero timeout is specified, with
+.Va \&to ,
+then getty will exit within the indicated
+number of seconds, either having
+received a login name and passed control
+to
+.Xr login 1 ,
+or having received an alarm signal, and exited.
+This may be useful to hangup dial in lines.
+.Pp
+Output from
+.Xr getty 8
+is even parity unless
+.Va \&op
+or
+.Va \&np
+is specified.
+The
+.Va \&op
+string
+may be specified with
+.Va \&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.
+The
+.Xr getty 8
+utility does not check parity of input characters in
+.Dv RAW
+mode.
+.Pp
+If a
+.Va \&pp
+string is specified and a PPP link bring-up sequence is recognized,
+getty will invoke the program referenced by the
+.Va \&pp
+option.
+This can be used to handle incoming PPP calls.
+If the
+.Va \&pl
+option is true as well,
+.Xr getty 8
+will skip the user name prompt and the PPP detection phase, and will
+invoke the program specified by
+.Va \&pp
+instantly.
+.Pp
+.Nm Getty
+provides some basic intelligent modem handling by providing a chat
+script feature available via two capabilities:
+.Pp
+.Bl -tag -offset indent -width \&xxxxxxxx -compact
+.It ic
+Chat script to initialize modem.
+.It ac
+Chat script to answer a call.
+.El
+.Pp
+A chat script is a set of expect/send string pairs.
+When a chat string starts,
+.Nm getty
+will wait for the first string, and if it finds it, will send the
+second, and so on.
+Strings specified are separated by one or more tabs or spaces.
+Strings may contain standard ASCII characters and special 'escapes',
+which consist of a backslash character followed by one or more
+characters which are interpreted as follows:
+.Pp
+.Bl -tag -offset indent -width \&xxxxxxxx -compact
+.It \ea
+bell character.
+.It \eb
+backspace.
+.It \en
+newline.
+.It \ee
+escape.
+.It \ef
+formfeed.
+.It \ep
+half-second pause.
+.It \er
+carriage return.
+.It \eS , \es
+space character.
+.It \et
+tab.
+.It \exNN
+hexadecimal byte value.
+.It \e0NNN
+octal byte value.
+.El
+.Pp
+Note that the
+.Ql \ep
+sequence is only valid for send strings and causes a half-second
+pause between sending the previous and next characters.
+Hexadecimal values are, at most, 2 hex digits long, and octal
+values are a maximum of 3 octal digits.
+.Pp
+The
+.Va \&ic
+chat sequence is used to initialize a modem or similar device.
+A typical example of an init chat script for a modem with a
+hayes compatible command set might look like this:
+.Pp
+.Dl :ic="" ATE0Q0V1\er OK\er ATS0=0\er OK\er:
+.Pp
+This script waits for nothing (which always succeeds), sends
+a sequence to ensure that the modem is in the correct mode
+(suppress command echo, send responses in verbose mode),
+and then disables auto-answer.
+It waits for an "OK" response before it terminates.
+The init sequence is used to check modem responses to ensure that
+the modem is functioning correctly.
+If the init script fails to complete,
+.Nm getty
+considers this to be fatal, and results in an error logged via
+.Xr syslogd 8 ,
+and exiting.
+.Pp
+Similarly, an answer chat script is used to manually answer the
+phone in response to (usually) a "RING".
+When run with an answer script,
+.Nm getty
+opens the port in non-blocking mode, clears any extraneous input
+and waits for data on the port.
+As soon as any data is available, the answer chat script is
+started and scanned for a string, and responds according to
+the answer chat script.
+With a hayes compatible modem, this would normally look something
+like:
+.Pp
+.Dl :ac=RING\er ATA\er CONNECT:
+.Pp
+This causes the modem to answer the call via the "ATA" command,
+then scans input for a "CONNECT" string.
+If this is received before a
+.Va \&ct
+timeout, then a normal login sequence commences.
+.Pp
+The
+.Va \&ct
+capability specifies a timeout for all send and expect strings.
+This timeout is set individually for each expect wait and send
+string and must be at least as long as the time it takes for
+a connection to be established between a remote and local
+modem (usually around 10 seconds).
+.Pp
+In most situations, you will want to flush any additional
+input after the connection has been detected, and the
+.Va \&de
+capability may be used to do that, as well as delay for a
+short time after the connection has been established during
+which all of the connection data has been sent by the modem.
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr gethostname 3 ,
+.Xr uname 3 ,
+.Xr termcap 5 ,
+.Xr getty 8 ,
+.Xr telnetd 8
+.Sh HISTORY
+The
+.Nm
+file format appeared in
+.Bx 4.2 .
+.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
+.Va \&he
+capability is stupid.
+.Pp
+The
+.Xr termcap 5
+format is horrid, something more rational should
+have been chosen.
diff --git a/libexec/getty/gettytab.h b/libexec/getty/gettytab.h
new file mode 100644
index 0000000..67ad95f
--- /dev/null
+++ b/libexec/getty/gettytab.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 1983, 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. 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: @(#)gettytab.h 8.2 (Berkeley) 3/30/94
+ * $FreeBSD$
+ */
+
+/*
+ * Getty description definitions.
+ */
+struct gettystrs {
+ const 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 {
+ const 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 {
+ const 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
+#define Lo gettystrs[24].value
+#define PP gettystrs[25].value
+#define IF gettystrs[26].value
+#define IC gettystrs[27].value
+#define AC gettystrs[28].value
+#define AL gettystrs[29].value
+#define DF gettystrs[30].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
+#define C0 gettynums[13].value
+#define C0set gettynums[13].set
+#define C1 gettynums[14].value
+#define C1set gettynums[14].set
+#define C2 gettynums[15].value
+#define C2set gettynums[15].set
+#define I0 gettynums[16].value
+#define I0set gettynums[16].set
+#define I1 gettynums[17].value
+#define I1set gettynums[17].set
+#define I2 gettynums[18].value
+#define I2set gettynums[18].set
+#define L0 gettynums[19].value
+#define L0set gettynums[19].set
+#define L1 gettynums[20].value
+#define L1set gettynums[20].set
+#define L2 gettynums[21].value
+#define L2set gettynums[21].set
+#define O0 gettynums[22].value
+#define O0set gettynums[22].set
+#define O1 gettynums[23].value
+#define O1set gettynums[23].set
+#define O2 gettynums[24].value
+#define O2set gettynums[24].set
+#define DE gettynums[25].value
+#define RTset gettynums[26].set
+#define RT gettynums[26].value
+#define CT gettynums[27].value
+#define DC gettynums[28].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
+#define NPset gettyflags[21].set
+#define MB gettyflags[22].value
+#define HW gettyflags[23].value
+#define NC gettyflags[24].value
+#define PL gettyflags[25].value
diff --git a/libexec/getty/init.c b/libexec/getty/init.c
new file mode 100644
index 0000000..6959e8d
--- /dev/null
+++ b/libexec/getty/init.c
@@ -0,0 +1,150 @@
+/*
+ * 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. 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
+#if 0
+static char sccsid[] = "@(#)from: init.c 8.1 (Berkeley) 6/4/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Getty table initializations.
+ *
+ * Melbourne getty.
+ */
+#include <termios.h>
+#include "gettytab.h"
+#include "extern.h"
+#include "pathnames.h"
+
+static char loginmsg[] = "login: ";
+static char nullstr[] = "";
+static char loginprg[] = _PATH_LOGIN;
+static char datefmt[] = "%+";
+
+struct gettystrs gettystrs[] = {
+ { "nx" }, /* next table */
+ { "cl" }, /* screen clear characters */
+ { "im" }, /* initial message */
+ { "lm", loginmsg }, /* login message */
+ { "er", &omode.c_cc[VERASE] }, /* erase character */
+ { "kl", &omode.c_cc[VKILL] }, /* kill character */
+ { "et", &omode.c_cc[VEOF] }, /* eof chatacter (eot) */
+ { "pc", nullstr }, /* pad character */
+ { "tt" }, /* terminal type */
+ { "ev" }, /* environment */
+ { "lo", loginprg }, /* login program */
+ { "hn", hostname }, /* host name */
+ { "he" }, /* host name edit */
+ { "in", &omode.c_cc[VINTR] }, /* interrupt char */
+ { "qu", &omode.c_cc[VQUIT] }, /* quit char */
+ { "xn", &omode.c_cc[VSTART] }, /* XON (start) char */
+ { "xf", &omode.c_cc[VSTOP] }, /* XOFF (stop) char */
+ { "bk", &omode.c_cc[VEOL] }, /* brk char (alt \n) */
+ { "su", &omode.c_cc[VSUSP] }, /* suspend char */
+ { "ds", &omode.c_cc[VDSUSP] }, /* delayed suspend */
+ { "rp", &omode.c_cc[VREPRINT] },/* reprint char */
+ { "fl", &omode.c_cc[VDISCARD] },/* flush output */
+ { "we", &omode.c_cc[VWERASE] }, /* word erase */
+ { "ln", &omode.c_cc[VLNEXT] }, /* literal next */
+ { "Lo" }, /* locale for strftime() */
+ { "pp" }, /* ppp login program */
+ { "if" }, /* sysv-like 'issue' filename */
+ { "ic" }, /* modem init-chat */
+ { "ac" }, /* modem answer-chat */
+ { "al" }, /* user to auto-login */
+ { "df", datefmt}, /* format for strftime() */
+ { 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 */
+ { "c0" }, /* output c_flags */
+ { "c1" }, /* input c_flags */
+ { "c2" }, /* user mode c_flags */
+ { "i0" }, /* output i_flags */
+ { "i1" }, /* input i_flags */
+ { "i2" }, /* user mode i_flags */
+ { "l0" }, /* output l_flags */
+ { "l1" }, /* input l_flags */
+ { "l2" }, /* user mode l_flags */
+ { "o0" }, /* output o_flags */
+ { "o1" }, /* input o_flags */
+ { "o2" }, /* user mode o_flags */
+ { "de" }, /* delay before sending 1st prompt */
+ { "rt" }, /* reset timeout */
+ { "ct" }, /* chat script timeout */
+ { "dc" }, /* debug chat script value */
+ { 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) */
+ { "mb", 0 }, /* do MDMBUF flow control */
+ { "hw", 0 }, /* do CTSRTS flow control */
+ { "nc", 0 }, /* set clocal (no carrier) */
+ { "pl", 0 }, /* use PPP instead of login(1) */
+ { 0 }
+};
diff --git a/libexec/getty/main.c b/libexec/getty/main.c
new file mode 100644
index 0000000..94ecb07
--- /dev/null
+++ b/libexec/getty/main.c
@@ -0,0 +1,814 @@
+/*-
+ * 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. 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 const char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)from: main.c 8.1 (Berkeley) 6/20/93";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/ttydefaults.h>
+#include <sys/utsname.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <libutil.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "gettytab.h"
+#include "extern.h"
+#include "pathnames.h"
+
+/*
+ * Set the amount of running time that getty should accumulate
+ * before deciding that something is wrong and exit.
+ */
+#define GETTY_TIMEOUT 60 /* seconds */
+
+#undef CTRL
+#define CTRL(x) (x&037)
+
+/* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
+
+#define PPP_FRAME 0x7e /* PPP Framing character */
+#define PPP_STATION 0xff /* "All Station" character */
+#define PPP_ESCAPE 0x7d /* Escape Character */
+#define PPP_CONTROL 0x03 /* PPP Control Field */
+#define PPP_CONTROL_ESCAPED 0x23 /* PPP Control Field, escaped */
+#define PPP_LCP_HI 0xc0 /* LCP protocol - high byte */
+#define PPP_LCP_LOW 0x21 /* LCP protocol - low byte */
+
+/* original mode; flags've been reset using values from <sys/ttydefaults.h> */
+struct termios omode;
+/* current mode */
+struct termios tmode;
+
+int crmod, digit, lower, upper;
+
+char hostname[MAXHOSTNAMELEN];
+char name[MAXLOGNAME*3];
+char dev[] = _PATH_DEV;
+char ttyn[32];
+
+#define OBUFSIZ 128
+#define TABBUFSIZ 512
+
+char defent[TABBUFSIZ];
+char tabent[TABBUFSIZ];
+const char *tname;
+
+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.c_cc[VERASE]
+#define KILL tmode.c_cc[VKILL]
+#define EOT tmode.c_cc[VEOF]
+
+#define puts Gputs
+
+static void defttymode(void);
+static void dingdong(int);
+static void dogettytab(void);
+static int getname(void);
+static void interrupt(int);
+static void oflush(void);
+static void prompt(void);
+static void putchr(int);
+static void putf(const char *);
+static void putpad(const char *);
+static void puts(const char *);
+static void timeoverrun(int);
+static char *getline(int);
+static void setttymode(int);
+static int opentty(const char *, int);
+
+jmp_buf timeout;
+
+static void
+dingdong(int signo __unused)
+{
+ alarm(0);
+ longjmp(timeout, 1);
+}
+
+jmp_buf intrupt;
+
+static void
+interrupt(int signo __unused)
+{
+ longjmp(intrupt, 1);
+}
+
+/*
+ * Action to take when getty is running too long.
+ */
+static void
+timeoverrun(int signo __unused)
+{
+
+ syslog(LOG_ERR, "getty exiting due to excessive running time");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ extern char **environ;
+ int first_sleep = 1, first_time = 1;
+ struct rlimit limit;
+ int rval;
+
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+
+ openlog("getty", LOG_CONS|LOG_PID, LOG_AUTH);
+ gethostname(hostname, sizeof(hostname) - 1);
+ hostname[sizeof(hostname) - 1] = '\0';
+ if (hostname[0] == '\0')
+ strcpy(hostname, "Amnesiac");
+
+ /*
+ * Limit running time to deal with broken or dead lines.
+ */
+ (void)signal(SIGXCPU, timeoverrun);
+ limit.rlim_max = RLIM_INFINITY;
+ limit.rlim_cur = GETTY_TIMEOUT;
+ (void)setrlimit(RLIMIT_CPU, &limit);
+
+ gettable("default", defent);
+ gendefaults();
+ tname = "default";
+ if (argc > 1)
+ tname = argv[1];
+
+ /*
+ * 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(STDIN_FILENO));
+ else {
+ 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);
+
+ /*
+ * Do the first scan through gettytab.
+ * Terminal mode parameters will be wrong until
+ * defttymode() called, but they're irrelevant for
+ * the initial setup of the terminal device.
+ */
+ dogettytab();
+
+ /*
+ * Init or answer modem sequence has been specified.
+ */
+ if (IC || AC) {
+ if (!opentty(ttyn, O_RDWR|O_NONBLOCK))
+ exit(1);
+ defttymode();
+ setttymode(1);
+ }
+
+ if (IC) {
+ if (getty_chat(IC, CT, DC) > 0) {
+ syslog(LOG_ERR, "modem init problem on %s", ttyn);
+ (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
+ exit(1);
+ }
+ }
+
+ if (AC) {
+ int i, rfds;
+ struct timeval to;
+
+ rfds = 1 << 0; /* FD_SET */
+ to.tv_sec = RT;
+ to.tv_usec = 0;
+ i = select(32, (fd_set*)&rfds, (fd_set*)NULL,
+ (fd_set*)NULL, RT ? &to : NULL);
+ if (i < 0) {
+ syslog(LOG_ERR, "select %s: %m", ttyn);
+ } else if (i == 0) {
+ syslog(LOG_NOTICE, "recycle tty %s", ttyn);
+ (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
+ exit(0); /* recycle for init */
+ }
+ i = getty_chat(AC, CT, DC);
+ if (i > 0) {
+ syslog(LOG_ERR, "modem answer problem on %s", ttyn);
+ (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
+ exit(1);
+ }
+ } else { /* maybe blocking open */
+ if (!opentty(ttyn, O_RDWR | (NC ? O_NONBLOCK : 0 )))
+ exit(1);
+ }
+ }
+ }
+
+ defttymode();
+ for (;;) {
+
+ /*
+ * if a delay was specified then sleep for that
+ * number of seconds before writing the initial prompt
+ */
+ if (first_sleep && DE) {
+ sleep(DE);
+ /* remove any noise */
+ (void)tcflush(STDIN_FILENO, TCIOFLUSH);
+ }
+ first_sleep = 0;
+
+ setttymode(0);
+ if (AB) {
+ tname = autobaud();
+ dogettytab();
+ continue;
+ }
+ if (PS) {
+ tname = portselector();
+ dogettytab();
+ continue;
+ }
+ if (CL && *CL)
+ putpad(CL);
+ edithost(HE);
+
+ /* if this is the first time through this, and an
+ issue file has been given, then send it */
+ if (first_time && IF) {
+ int fd;
+
+ if ((fd = open(IF, O_RDONLY)) != -1) {
+ char * cp;
+
+ while ((cp = getline(fd)) != NULL) {
+ putf(cp);
+ }
+ close(fd);
+ }
+ }
+ first_time = 0;
+
+ if (IM && *IM && !(PL && PP))
+ putf(IM);
+ if (setjmp(timeout)) {
+ cfsetispeed(&tmode, B0);
+ cfsetospeed(&tmode, B0);
+ (void)tcsetattr(STDIN_FILENO, TCSANOW, &tmode);
+ exit(1);
+ }
+ if (TO) {
+ signal(SIGALRM, dingdong);
+ alarm(TO);
+ }
+
+ rval = 0;
+ if (AL) {
+ const char *p = AL;
+ char *q = name;
+
+ while (*p && q < &name[sizeof name - 1]) {
+ if (isupper(*p))
+ upper = 1;
+ else if (islower(*p))
+ lower = 1;
+ else if (isdigit(*p))
+ digit = 1;
+ *q++ = *p++;
+ }
+ } else if (!(PL && PP))
+ rval = getname();
+ if (rval == 2 || (PL && PP)) {
+ oflush();
+ alarm(0);
+ limit.rlim_max = RLIM_INFINITY;
+ limit.rlim_cur = RLIM_INFINITY;
+ (void)setrlimit(RLIMIT_CPU, &limit);
+ execle(PP, "ppplogin", ttyn, (char *) 0, env);
+ syslog(LOG_ERR, "%s: %m", PP);
+ exit(1);
+ } else if (rval || AL) {
+ int i;
+
+ oflush();
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+ if (name[0] == '\0')
+ continue;
+ if (name[0] == '-') {
+ puts("user names may not start with '-'.");
+ continue;
+ }
+ if (!(upper || lower || digit)) {
+ if (AL) {
+ syslog(LOG_ERR,
+ "invalid auto-login name: %s", AL);
+ exit(1);
+ } else
+ continue;
+ }
+ set_flags(2);
+ if (crmod) {
+ tmode.c_iflag |= ICRNL;
+ tmode.c_oflag |= ONLCR;
+ }
+#if REALLY_OLD_TTYS
+ if (upper || UC)
+ tmode.sg_flags |= LCASE;
+ if (lower || LC)
+ tmode.sg_flags &= ~LCASE;
+#endif
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
+ syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
+ exit(1);
+ }
+ signal(SIGINT, SIG_DFL);
+ for (i = 0; environ[i] != (char *)0; i++)
+ env[i] = environ[i];
+ makeenv(&env[i]);
+
+ limit.rlim_max = RLIM_INFINITY;
+ limit.rlim_cur = RLIM_INFINITY;
+ (void)setrlimit(RLIMIT_CPU, &limit);
+ execle(LO, "login", AL ? "-fp" : "-p", name,
+ (char *) 0, env);
+ syslog(LOG_ERR, "%s: %m", LO);
+ exit(1);
+ }
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+ signal(SIGINT, SIG_IGN);
+ if (NX && *NX) {
+ tname = NX;
+ dogettytab();
+ }
+ }
+}
+
+static int
+opentty(const char *tty, int flags)
+{
+ int i;
+ int failopenlogged = 0;
+
+ while ((i = open(tty, flags)) == -1)
+ {
+ if (!failopenlogged) {
+ syslog(LOG_ERR, "open %s: %m", tty);
+ failopenlogged = 1;
+ }
+ sleep(60);
+ }
+ if (login_tty(i) < 0) {
+ if (daemon(0,0) < 0) {
+ syslog(LOG_ERR,"daemon: %m");
+ close(i);
+ return 0;
+ }
+ if (login_tty(i) < 0) {
+ syslog(LOG_ERR, "login_tty %s: %m", tty);
+ close(i);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static void
+defttymode(void)
+{
+ struct termios def;
+
+ /* Start with default tty settings. */
+ if (tcgetattr(STDIN_FILENO, &tmode) < 0) {
+ syslog(LOG_ERR, "tcgetattr %s: %m", ttyn);
+ exit(1);
+ }
+ omode = tmode; /* fill c_cc for dogettytab() */
+ dogettytab();
+ /*
+ * Don't rely on the driver too much, and initialize crucial
+ * things according to <sys/ttydefaults.h>. Avoid clobbering
+ * the c_cc[] settings however, the console drivers might wish
+ * to leave their idea of the preferred VERASE key value
+ * there.
+ */
+ cfmakesane(&def);
+ tmode.c_iflag = def.c_iflag;
+ tmode.c_oflag = def.c_oflag;
+ tmode.c_lflag = def.c_lflag;
+ tmode.c_cflag = def.c_cflag;
+ if (NC)
+ tmode.c_cflag |= CLOCAL;
+ omode = tmode;
+}
+
+static void
+setttymode(int raw)
+{
+ int off = 0;
+
+ (void)tcflush(STDIN_FILENO, TCIOFLUSH); /* clear out the crap */
+ ioctl(STDIN_FILENO, FIONBIO, &off); /* turn off non-blocking mode */
+ ioctl(STDIN_FILENO, FIOASYNC, &off); /* ditto for async mode */
+
+ if (IS)
+ cfsetispeed(&tmode, speed(IS));
+ else if (SP)
+ cfsetispeed(&tmode, speed(SP));
+ if (OS)
+ cfsetospeed(&tmode, speed(OS));
+ else if (SP)
+ cfsetospeed(&tmode, speed(SP));
+ set_flags(0);
+ setchars();
+ if (raw)
+ cfmakeraw(&tmode);
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
+ syslog(LOG_ERR, "tcsetattr %s: %m", ttyn);
+ exit(1);
+ }
+}
+
+
+static int
+getname(void)
+{
+ int c;
+ char *np;
+ unsigned char cs;
+ int ppp_state = 0;
+ int ppp_connection = 0;
+
+ /*
+ * Interrupt may happen if we use CBREAK mode
+ */
+ if (setjmp(intrupt)) {
+ signal(SIGINT, SIG_IGN);
+ return (0);
+ }
+ signal(SIGINT, interrupt);
+ set_flags(1);
+ prompt();
+ oflush();
+ if (PF > 0) {
+ sleep(PF);
+ PF = 0;
+ }
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &tmode) < 0) {
+ syslog(LOG_ERR, "%s: %m", ttyn);
+ exit(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);
+
+ /* PPP detection state machine..
+ Look for sequences:
+ PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
+ PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
+ See RFC1662.
+ Derived from code from Michael Hancock, <michaelh@cet.co.jp>
+ and Erik 'PPP' Olson, <eriko@wrq.com>
+ */
+
+ if (PP && (cs == PPP_FRAME)) {
+ ppp_state = 1;
+ } else if (ppp_state == 1 && cs == PPP_STATION) {
+ ppp_state = 2;
+ } else if (ppp_state == 2 && cs == PPP_ESCAPE) {
+ ppp_state = 3;
+ } else if ((ppp_state == 2 && cs == PPP_CONTROL)
+ || (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
+ ppp_state = 4;
+ } else if (ppp_state == 4 && cs == PPP_LCP_HI) {
+ ppp_state = 5;
+ } else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
+ ppp_connection = 1;
+ break;
+ } else {
+ ppp_state = 0;
+ }
+
+ if (c == EOT || c == CTRL('d'))
+ exit(0);
+ if (c == '\r' || c == '\n' || np >= &name[sizeof name-1]) {
+ 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 (cfgetospeed(&tmode) >= 1200)
+ puts("\b \b");
+ else
+ putchr(cs);
+ }
+ continue;
+ } else if (c == KILL || c == CTRL('u')) {
+ putchr('\r');
+ if (cfgetospeed(&tmode) < 1200)
+ putchr('\n');
+ /* this is the way they do it down under ... */
+ else if (np > name)
+ puts(" \r");
+ prompt();
+ digit = lower = upper = 0;
+ np = name;
+ continue;
+ } else if (isdigit(c))
+ digit = 1;
+ 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 + ppp_connection);
+}
+
+static void
+putpad(const char *s)
+{
+ int pad = 0;
+ speed_t ospeed = cfgetospeed(&tmode);
+
+ 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 || ospeed <= 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.
+ */
+ pad = (pad * ospeed + 50000) / 100000;
+ while (pad--)
+ putchr(*PC);
+}
+
+static void
+puts(const char *s)
+{
+ while (*s)
+ putchr(*s++);
+}
+
+char outbuf[OBUFSIZ];
+int obufcnt = 0;
+
+static void
+putchr(int 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);
+}
+
+static void
+oflush(void)
+{
+ if (obufcnt)
+ write(STDOUT_FILENO, outbuf, obufcnt);
+ obufcnt = 0;
+}
+
+static void
+prompt(void)
+{
+
+ putf(LM);
+ if (CO)
+ putchr('\n');
+}
+
+
+static char *
+getline(int fd)
+{
+ int i = 0;
+ static char linebuf[512];
+
+ /*
+ * This is certainly slow, but it avoids having to include
+ * stdio.h unnecessarily. Issue files should be small anyway.
+ */
+ while (i < (sizeof linebuf - 3) && read(fd, linebuf+i, 1)==1) {
+ if (linebuf[i] == '\n') {
+ /* Don't rely on newline mode, assume raw */
+ linebuf[i++] = '\r';
+ linebuf[i++] = '\n';
+ linebuf[i] = '\0';
+ return linebuf;
+ }
+ ++i;
+ }
+ linebuf[i] = '\0';
+ return i ? linebuf : 0;
+}
+
+static void
+putf(const char *cp)
+{
+ extern char editedhost[];
+ time_t t;
+ char *slash, db[100];
+
+ static struct utsname kerninfo;
+
+ if (!*kerninfo.sysname)
+ uname(&kerninfo);
+
+ while (*cp) {
+ if (*cp != '%') {
+ putchr(*cp++);
+ continue;
+ }
+ switch (*++cp) {
+
+ case 't':
+ slash = strrchr(ttyn, '/');
+ if (slash == (char *) 0)
+ puts(ttyn);
+ else
+ puts(&slash[1]);
+ break;
+
+ case 'h':
+ puts(editedhost);
+ break;
+
+ case 'd': {
+ t = (time_t)0;
+ (void)time(&t);
+ if (Lo)
+ (void)setlocale(LC_TIME, Lo);
+ (void)strftime(db, sizeof(db), DF, localtime(&t));
+ puts(db);
+ break;
+
+ case 's':
+ puts(kerninfo.sysname);
+ break;
+
+ case 'm':
+ puts(kerninfo.machine);
+ break;
+
+ case 'r':
+ puts(kerninfo.release);
+ break;
+
+ case 'v':
+ puts(kerninfo.version);
+ break;
+ }
+
+ case '%':
+ putchr('%');
+ break;
+ }
+ cp++;
+ }
+}
+
+/*
+ * Read a gettytab database entry and perform necessary quirks.
+ */
+static void
+dogettytab(void)
+{
+
+ /* Read the database entry. */
+ gettable(tname, tabent);
+
+ /*
+ * Avoid inheriting the parity values from the default entry
+ * if any of them is set in the current entry.
+ * Mixing different parity settings is unreasonable.
+ */
+ if (OPset || EPset || APset || NPset)
+ OPset = EPset = APset = NPset = 1;
+
+ /* Fill in default values for unset capabilities. */
+ setdefaults();
+}
diff --git a/libexec/getty/pathnames.h b/libexec/getty/pathnames.h
new file mode 100644
index 0000000..df59a2c
--- /dev/null
+++ b/libexec/getty/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. 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: @(#)pathnames.h 8.1 (Berkeley) 6/4/93
+ * $FreeBSD$
+ */
+
+#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..26d2173
--- /dev/null
+++ b/libexec/getty/subr.c
@@ -0,0 +1,693 @@
+/*
+ * 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. 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
+#if 0
+static char sccsid[] = "@(#)from: subr.c 8.1 (Berkeley) 6/4/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Melbourne getty.
+ */
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+#include "gettytab.h"
+#include "pathnames.h"
+#include "extern.h"
+
+
+
+/*
+ * Get a table entry.
+ */
+void
+gettable(const char *name, char *buf)
+{
+ struct gettystrs *sp;
+ struct gettynums *np;
+ struct gettyflags *fp;
+ long n;
+ int l;
+ char *p;
+ char *msg = NULL;
+ const char *dba[2];
+
+ static int firsttime = 1;
+
+ dba[0] = _PATH_GETTYTAB;
+ dba[1] = 0;
+
+ if (firsttime) {
+ /*
+ * we need to strdup() anything in the strings array
+ * initially in order to simplify things later
+ */
+ for (sp = gettystrs; sp->field; sp++)
+ if (sp->value != NULL) {
+ /* handle these ones more carefully */
+ if (sp >= &gettystrs[4] && sp <= &gettystrs[6])
+ l = 2;
+ else
+ l = strlen(sp->value) + 1;
+ if ((p = malloc(l)) != NULL) {
+ strncpy(p, sp->value, l);
+ p[l-1] = '\0';
+ }
+ /*
+ * replace, even if NULL, else we'll
+ * have problems with free()ing static mem
+ */
+ sp->value = p;
+ }
+ firsttime = 0;
+ }
+
+ switch (cgetent(&buf, (char **)dba, (char *)name)) {
+ case 1:
+ msg = "%s: couldn't resolve 'tc=' in gettytab '%s'";
+ case 0:
+ break;
+ case -1:
+ msg = "%s: unknown gettytab entry '%s'";
+ break;
+ case -2:
+ msg = "%s: retrieving gettytab entry '%s': %m";
+ break;
+ case -3:
+ msg = "%s: recursive 'tc=' reference gettytab entry '%s'";
+ break;
+ default:
+ msg = "%s: unexpected cgetent() error for entry '%s'";
+ break;
+ }
+
+ if (msg != NULL) {
+ syslog(LOG_ERR, msg, "getty", name);
+ return;
+ }
+
+ for (sp = gettystrs; sp->field; sp++) {
+ if ((l = cgetstr(buf, (char*)sp->field, &p)) >= 0) {
+ if (sp->value) {
+ /* prefer existing value */
+ if (strcmp(p, sp->value) != 0)
+ free(sp->value);
+ else {
+ free(p);
+ p = sp->value;
+ }
+ }
+ sp->value = p;
+ } else if (l == -1) {
+ free(sp->value);
+ sp->value = NULL;
+ }
+ }
+
+ for (np = gettynums; np->field; np++) {
+ if (cgetnum(buf, (char*)np->field, &n) == -1)
+ np->set = 0;
+ else {
+ np->set = 1;
+ np->value = n;
+ }
+ }
+
+ for (fp = gettyflags; fp->field; fp++) {
+ if (cgetcap(buf, (char *)fp->field, ':') == NULL)
+ fp->set = 0;
+ else {
+ fp->set = 1;
+ fp->value = 1 ^ fp->invrt;
+ }
+ }
+
+#ifdef DEBUG
+ printf("name=\"%s\", buf=\"%s\"\r\n", name, buf);
+ for (sp = gettystrs; sp->field; sp++)
+ printf("cgetstr: %s=%s\r\n", sp->field, sp->value);
+ for (np = gettynums; np->field; np++)
+ printf("cgetnum: %s=%d\r\n", np->field, np->value);
+ for (fp = gettyflags; fp->field; fp++)
+ printf("cgetflags: %s='%c' set='%c'\r\n", fp->field,
+ fp->value + '0', fp->set + '0');
+#endif /* DEBUG */
+}
+
+void
+gendefaults(void)
+{
+ struct gettystrs *sp;
+ struct gettynums *np;
+ struct gettyflags *fp;
+
+ for (sp = gettystrs; sp->field; sp++)
+ if (sp->value)
+ sp->defalt = strdup(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;
+}
+
+void
+setdefaults(void)
+{
+ struct gettystrs *sp;
+ struct gettynums *np;
+ struct gettyflags *fp;
+
+ for (sp = gettystrs; sp->field; sp++)
+ if (!sp->value)
+ sp->value = !sp->defalt ? sp->defalt
+ : strdup(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.c_cc[VERASE], &tmode.c_cc[VKILL], &tmode.c_cc[VINTR],
+ &tmode.c_cc[VQUIT], &tmode.c_cc[VSTART], &tmode.c_cc[VSTOP],
+ &tmode.c_cc[VEOF], &tmode.c_cc[VEOL], &tmode.c_cc[VSUSP],
+ &tmode.c_cc[VDSUSP], &tmode.c_cc[VREPRINT], &tmode.c_cc[VDISCARD],
+ &tmode.c_cc[VWERASE], &tmode.c_cc[VLNEXT], 0
+};
+
+void
+setchars(void)
+{
+ int i;
+ const char *p;
+
+ for (i = 0; charnames[i]; i++) {
+ p = *charnames[i];
+ if (p && *p)
+ *charvars[i] = *p;
+ else
+ *charvars[i] = _POSIX_VDISABLE;
+ }
+}
+
+/* Macros to clear/set/test flags. */
+#define SET(t, f) (t) |= (f)
+#define CLR(t, f) (t) &= ~(f)
+#define ISSET(t, f) ((t) & (f))
+
+void
+set_flags(int n)
+{
+ tcflag_t iflag, oflag, cflag, lflag;
+
+
+ switch (n) {
+ case 0:
+ if (C0set && I0set && L0set && O0set) {
+ tmode.c_cflag = C0;
+ tmode.c_iflag = I0;
+ tmode.c_lflag = L0;
+ tmode.c_oflag = O0;
+ return;
+ }
+ break;
+ case 1:
+ if (C1set && I1set && L1set && O1set) {
+ tmode.c_cflag = C1;
+ tmode.c_iflag = I1;
+ tmode.c_lflag = L1;
+ tmode.c_oflag = O1;
+ return;
+ }
+ break;
+ default:
+ if (C2set && I2set && L2set && O2set) {
+ tmode.c_cflag = C2;
+ tmode.c_iflag = I2;
+ tmode.c_lflag = L2;
+ tmode.c_oflag = O2;
+ return;
+ }
+ break;
+ }
+
+ iflag = omode.c_iflag;
+ oflag = omode.c_oflag;
+ cflag = omode.c_cflag;
+ lflag = omode.c_lflag;
+
+ if (NP) {
+ CLR(cflag, CSIZE|PARENB);
+ SET(cflag, CS8);
+ CLR(iflag, ISTRIP|INPCK|IGNPAR);
+ } else if (AP || EP || OP) {
+ CLR(cflag, CSIZE);
+ SET(cflag, CS7|PARENB);
+ SET(iflag, ISTRIP);
+ if (OP && !EP) {
+ SET(iflag, INPCK|IGNPAR);
+ SET(cflag, PARODD);
+ if (AP)
+ CLR(iflag, INPCK);
+ } else if (EP && !OP) {
+ SET(iflag, INPCK|IGNPAR);
+ CLR(cflag, PARODD);
+ if (AP)
+ CLR(iflag, INPCK);
+ } else if (AP || (EP && OP)) {
+ CLR(iflag, INPCK|IGNPAR);
+ CLR(cflag, PARODD);
+ }
+ } /* else, leave as is */
+
+#if 0
+ if (UC)
+ f |= LCASE;
+#endif
+
+ if (HC)
+ SET(cflag, HUPCL);
+ else
+ CLR(cflag, HUPCL);
+
+ if (MB)
+ SET(cflag, MDMBUF);
+ else
+ CLR(cflag, MDMBUF);
+
+ if (HW)
+ SET(cflag, CRTSCTS);
+ else
+ CLR(cflag, CRTSCTS);
+
+ if (NL) {
+ SET(iflag, ICRNL);
+ SET(oflag, ONLCR|OPOST);
+ } else {
+ CLR(iflag, ICRNL);
+ CLR(oflag, ONLCR);
+ }
+
+ if (!HT)
+ SET(oflag, OXTABS|OPOST);
+ else
+ CLR(oflag, OXTABS);
+
+#ifdef XXX_DELAY
+ SET(f, delaybits());
+#endif
+
+ if (n == 1) { /* read mode flags */
+ if (RW) {
+ iflag = 0;
+ CLR(oflag, OPOST);
+ CLR(cflag, CSIZE|PARENB);
+ SET(cflag, CS8);
+ lflag = 0;
+ } else {
+ CLR(lflag, ICANON);
+ }
+ goto out;
+ }
+
+ if (n == 0)
+ goto out;
+
+#if 0
+ if (CB)
+ SET(f, CRTBS);
+#endif
+
+ if (CE)
+ SET(lflag, ECHOE);
+ else
+ CLR(lflag, ECHOE);
+
+ if (CK)
+ SET(lflag, ECHOKE);
+ else
+ CLR(lflag, ECHOKE);
+
+ if (PE)
+ SET(lflag, ECHOPRT);
+ else
+ CLR(lflag, ECHOPRT);
+
+ if (EC)
+ SET(lflag, ECHO);
+ else
+ CLR(lflag, ECHO);
+
+ if (XC)
+ SET(lflag, ECHOCTL);
+ else
+ CLR(lflag, ECHOCTL);
+
+ if (DX)
+ SET(lflag, IXANY);
+ else
+ CLR(lflag, IXANY);
+
+out:
+ tmode.c_iflag = iflag;
+ tmode.c_oflag = oflag;
+ tmode.c_cflag = cflag;
+ tmode.c_lflag = lflag;
+}
+
+
+#ifdef XXX_DELAY
+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 },
+};
+
+int
+delaybits(void)
+{
+ int f;
+
+ f = adelay(CD, crdelay);
+ f |= adelay(ND, nldelay);
+ f |= adelay(FD, ffdelay);
+ f |= adelay(TD, tbdelay);
+ f |= adelay(BD, bsdelay);
+ return (f);
+}
+
+int
+adelay(int ms, struct delayval *dp)
+{
+ if (ms == 0)
+ return (0);
+ while (dp->delay && ms > dp->delay)
+ dp++;
+ return (dp->bits);
+}
+#endif
+
+char editedhost[MAXHOSTNAMELEN];
+
+void
+edithost(const char *pat)
+{
+ const char *host = HN;
+ 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';
+}
+
+static 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 },
+ { 230400, B230400 },
+ { 0 }
+};
+
+int
+speed(int val)
+{
+ struct speedtab *sp;
+
+ if (val <= B230400)
+ return (val);
+
+ for (sp = speedtab; sp->speed; sp++)
+ if (sp->speed == val)
+ return (sp->uxname);
+
+ return (B300); /* default in impossible cases */
+}
+
+void
+makeenv(char *env[])
+{
+ static char termbuf[128] = "TERM=";
+ char *p, *q;
+ char **ep;
+
+ ep = env;
+ if (TT && *TT) {
+ strlcat(termbuf, TT, sizeof(termbuf));
+ *ep++ = termbuf;
+ }
+ if ((p = EV)) {
+ q = p;
+ while ((q = strchr(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 {
+ const char *ps_baud;
+ const 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 }
+};
+
+const char *
+portselector(void)
+{
+ char c, baud[20];
+ const char *type = "default";
+ 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.
+ */
+const char *
+autobaud(void)
+{
+ int rfds;
+ struct timeval timeout;
+ char c;
+ const char *type = "9600-baud";
+
+ (void)tcflush(0, TCIOFLUSH);
+ 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);
+ (void)tcflush(0, TCIOFLUSH);
+ 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/ttys.5 b/libexec/getty/ttys.5
new file mode 100644
index 0000000..d4c7f1d
--- /dev/null
+++ b/libexec/getty/ttys.5
@@ -0,0 +1,170 @@
+.\" Copyright (c) 1985, 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. 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: @(#)ttys.5 8.1 (Berkeley) 6/4/93
+.\" $FreeBSD$
+.\" "
+.Dd March 9, 2014
+.Dt TTYS 5
+.Os
+.Sh NAME
+.Nm ttys
+.Nd terminal initialization information
+.Sh DESCRIPTION
+The file
+.Nm
+contains information that is used by various routines to initialize
+and control the use of terminal special files.
+Pseudo-terminals (see
+.Xr pts 4 )
+are not listed.
+This information is read with the
+.Xr getttyent 3
+library routines.
+There is one line in the
+.Nm
+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 normally the
+name of the terminal special file as it is found in
+.Pa /dev .
+However, it can be any arbitrary string
+when the associated command is not related to a tty.
+.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
+.Ev 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 ) ,
+specify a window system process that
+.Xr init 8
+will maintain for the terminal line, optionally determine the
+type of tty (whether dialin, network or otherwise),
+or specify a tty group
+name that allows the login class database (see
+.Xr login.conf 5 )
+to refer to many ttys as a group, to selectively allow or
+deny access or enable or disable accounting facilities for
+ttys as a group.
+.Pp
+As flag values, the strings ``on'' and ``off'' specify that
+.Xr init 8
+should (should not) execute the command given in the second field.
+``onifconsole'' will cause this line to be enabled if and only if it is
+an active kernel console device (it is equivalent to ``on'' in this
+case).
+The flag ``secure'' (if the console is enabled) allows users with a
+uid of 0 to login on
+this line.
+The flag ``dialin'' indicates that a tty entry describes a dialin
+line, and ``network'' is obsolete and does nothing.
+Either of these strings may also be specified in the terminal type
+field.
+The string ``window='' may be followed by a quoted command
+string which
+.Xr init 8
+will execute
+.Em before
+starting the command specified by the second field.
+.Pp
+The string ``group='' may be followed by a group name comprised of
+alphanumeric characters that can be used by
+.Xr login.conf 5
+to refer to many tty lines as a group to enable or disable access
+and accounting facilities.
+If no group is specified, then the tty becomes a member of the group
+"none".
+For backwards compatibility, the ``group='' should appear last on the
+line, immediately before the optional comment.
+.Pp
+Both the second field and any command specified with ``window=''
+will be split into words and executed using
+.Xr execve 2 .
+Words are separated by any combinations of tabs and spaces.
+Arguments containing whitespace should be enclosed in single quotes
+.Pq Li ' .
+Note that no shell-style globbing or other variable substitution occurs.
+.Sh FILES
+.Bl -tag -width /etc/ttys -compact
+.It Pa /etc/ttys
+.El
+.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 group=dialup # 555-1234
+# Mike's terminal: hp2621
+ttyh0 "/usr/libexec/getty std.9600" hp2621-nl on group=dialup # 457 Evans
+# John's terminal: vt100
+ttyh1 "/usr/libexec/getty std.9600" vt100 on group=dialup # 459 Evans
+# terminal emulate/window system
+ttyv0 "/usr/local/bin/xterm -display :0" xterm on window="/usr/local/bin/X :0"
+.Ed
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr getttyent 3 ,
+.Xr gettytab 5 ,
+.Xr login.conf 5 ,
+.Xr termcap 5 ,
+.Xr getty 8 ,
+.Xr init 8
+.\".Xr init 8 ,
+.\".Xr ttyflags 8
+.Sh HISTORY
+A
+.Nm
+file appeared in
+.At v6 .
diff --git a/libexec/hyperv/Makefile b/libexec/hyperv/Makefile
new file mode 100644
index 0000000..37abdf5
--- /dev/null
+++ b/libexec/hyperv/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/hyperv/tools/scripts
+
+BINDIR= /usr/libexec/hyperv
+
+SCRIPTS= hv_set_ifconfig hv_get_dns_info hv_get_dhcp_info
+NO_OBJ=
+
+.include <bsd.prog.mk>
diff --git a/libexec/mail.local/Makefile b/libexec/mail.local/Makefile
new file mode 100644
index 0000000..3e59609
--- /dev/null
+++ b/libexec/mail.local/Makefile
@@ -0,0 +1,33 @@
+# @(#)Makefile 8.1 (Berkeley) 7/19/93
+# $FreeBSD$
+
+SENDMAIL_DIR=${.CURDIR}/../../contrib/sendmail
+.PATH: ${SENDMAIL_DIR}/mail.local
+
+PROG= mail.local
+SRCS= mail.local.c
+MAN= mail.local.8
+CFLAGS+=-I${SENDMAIL_DIR}/include -I.
+
+WARNS?= 2
+WFORMAT=0
+
+LIBSMDIR= ${.OBJDIR}/../../lib/libsm
+LIBSM= ${LIBSMDIR}/libsm.a
+
+DPADD= ${LIBSM}
+LDADD= ${LIBSM}
+
+SRCS+= sm_os.h
+CLEANFILES+=sm_os.h
+
+# User customizations to the sendmail build environment
+CFLAGS+=${SENDMAIL_CFLAGS}
+DPADD+=${SENDMAIL_DPADD}
+LDADD+=${SENDMAIL_LDADD}
+LDFLAGS+=${SENDMAIL_LDFLAGS}
+
+sm_os.h:
+ ln -sf ${SENDMAIL_DIR}/include/sm/os/sm_os_freebsd.h sm_os.h
+
+.include <bsd.prog.mk>
diff --git a/libexec/mknetid/Makefile b/libexec/mknetid/Makefile
new file mode 100644
index 0000000..5ca0662
--- /dev/null
+++ b/libexec/mknetid/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= mknetid
+SRCS= mknetid.c hash.c parse_group.c
+
+MAN= netid.5 mknetid.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/libexec/mknetid/hash.c b/libexec/mknetid/hash.c
new file mode 100644
index 0000000..b4a39cb
--- /dev/null
+++ b/libexec/mknetid/hash.c
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "hash.h"
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * 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(const void *keyarg, size_t len)
+{
+ const u_char *key;
+ size_t loop;
+ 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(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). */
+struct grouplist *lookup(struct member_entry *table[], char *key)
+{
+ struct member_entry *cur;
+
+ cur = table[hashkey(key)];
+
+ while (cur) {
+ if (!strcmp(cur->key, key))
+ return(cur->groups);
+ cur = cur->next;
+ }
+
+ return(NULL);
+}
+
+struct grouplist dummy = { 99999, NULL };
+
+/*
+ * Store a group member entry and/or update its grouplist.
+ */
+void mstore (struct member_entry *table[], char *key, int gid, int dup)
+{
+ struct member_entry *cur, *new;
+ struct grouplist *tmp;
+ u_int32_t i;
+
+ i = hashkey(key);
+ cur = table[i];
+
+ if (!dup) {
+ tmp = (struct grouplist *)malloc(sizeof(struct grouplist));
+ tmp->groupid = gid;
+ tmp->next = NULL;
+ }
+
+ /* Check if all we have to do is insert a new groupname. */
+ while (cur) {
+ if (!dup && !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);
+ if (!dup)
+ new->groups = tmp;
+ else
+ new->groups = (struct grouplist *)&dummy;
+ new->next = table[i];
+ table[i] = new;
+
+ return;
+}
diff --git a/libexec/mknetid/hash.h b/libexec/mknetid/hash.h
new file mode 100644
index 0000000..7918ae2
--- /dev/null
+++ b/libexec/mknetid/hash.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 1995, 1996
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/* Groupid entry hung off a member_entry node. */
+struct grouplist {
+ gid_t groupid;
+ struct grouplist *next;
+};
+
+/* Entry in the cooked member list hash table. */
+struct member_entry {
+ char *key; /* username */
+ struct grouplist *groups;
+ struct member_entry *next;
+};
+
+/* Table size (chosen arbitrarily). Not too big, not too small. */
+#define TABLESIZE 1024
+#define HASH_MASK 0x000003FF
+
+extern void mstore(struct member_entry ** , char *, int, int);
+extern struct grouplist *lookup(struct member_entry **, char *);
+
diff --git a/libexec/mknetid/mknetid.8 b/libexec/mknetid/mknetid.8
new file mode 100644
index 0000000..08b8e74
--- /dev/null
+++ b/libexec/mknetid/mknetid.8
@@ -0,0 +1,152 @@
+.\" Copyright (c) 1995, 1996
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 23, 1996
+.Dt MKNETID 8
+.Os
+.Sh NAME
+.Nm mknetid
+.Nd "generate netid map data"
+.Sh SYNOPSIS
+.Nm
+.Op Fl q
+.Op Fl g Ar group_file
+.Op Fl p Ar passwd_file
+.Op Fl h Ar hosts_file
+.Op Fl n Ar netid_file
+.Op Fl d Ar domain
+.Sh DESCRIPTION
+The
+.Nm
+utility processes the contents of the
+.Xr group 5 ,
+.Xr passwd 5 ,
+.Xr hosts 5
+and
+.Xr netid 5
+files into the format used to generate the
+.Pa netid.byname
+.Tn NIS
+map.
+This map is used to hold credential information for both users
+and hosts in an operating system independent format.
+.Pp
+The
+.Nm
+utility checks for duplicate occurrences of netids and filters
+them out.
+.Pp
+The
+.Nm
+utility prints its results on the standard output.
+It is usually called
+only by
+.Pa /var/yp/Makefile
+when rebuilding the
+.Tn NIS
+maps.
+.Sh OPTIONS
+The
+.Nm
+utility supports the following options:
+.Bl -tag -width indent
+.It Fl q
+Normally,
+.Nm
+prints a warning message when it encounters a duplicate netid.
+This flag turns on 'quiet' mode, allowing the warnings to be
+suppressed.
+Other error messages may still be generated.
+.It Fl g Ar group_file
+Specify the location of the group information
+file.
+The compiled-in default is
+.Pa /etc/group .
+.It Fl p Ar passwd_file
+Specify the location of the passwd information
+file.
+The compiled-in default is
+.Pa /etc/passwd .
+.It Fl h Ar hosts_file
+Specify the location of the hosts database
+file.
+The compiled-in default is
+.Pa /etc/hosts .
+.It Fl n Ar netid_file
+Specify the location of the netid information
+file.
+The compiled-in default is
+.Pa /etc/netid .
+Note that no error is generated if the netid database cannot be
+found.
+The netid database is not likely to be present on most systems
+until
+.Tn Secure RPC
+support is added to
+.Fx .
+.It Fl d Ar domain
+By default, the
+.Nm
+utility uses the system domainname when generating netid records.
+If
+the system domainname is not set, the domain must be specified on the
+command line with the
+.Fl d
+flag.
+If the domainname is set, the
+.Fl d
+flag may be used to override it.
+.El
+.Sh FILES
+.Bl -tag -width /var/yp/Makefile -compact
+.It Pa /var/yp/Makefile
+the Makefile that calls
+.Nm yp_mkdb
+and
+.Nm
+to build the
+.Tn NIS
+databases
+.It Pa /etc/group
+the default group database file
+.It Pa /etc/passwd
+the default passwd database file
+.It Pa /etc/hosts
+the default hosts database file
+.It Pa /etc/netid
+the default netid database file
+.El
+.Sh SEE ALSO
+.Xr yp 8 ,
+.Xr yp_mkdb 8
+.Sh AUTHORS
+.An Bill Paul Aq Mt wpaul@ctr.columbia.edu
diff --git a/libexec/mknetid/mknetid.c b/libexec/mknetid/mknetid.c
new file mode 100644
index 0000000..6686866
--- /dev/null
+++ b/libexec/mknetid/mknetid.c
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 1995, 1996
+ * 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.
+ *
+ * netid map generator program
+ *
+ * Written by Bill Paul <wpaul@ctr.columbia.edu>
+ * Center for Telecommunications Research
+ * Columbia University, New York City
+ */
+
+#include <sys/types.h>
+
+#include <rpc/rpc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+
+#include <err.h>
+#include <grp.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "hash.h"
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#define LINSIZ 1024
+#define OPSYS "unix"
+
+/* Default location of group file. */
+char *groupfile = _PATH_GROUP;
+/* Default location of master.passwd file. */
+char *passfile = _PATH_PASSWD;
+/* Default location of hosts file. */
+char *hostsfile = _PATH_HOSTS;
+/* Default location of netid file */
+char *netidfile = "/etc/netid";
+
+/*
+ * Stored hash table of 'reverse' group member database
+ * which we will construct.
+ */
+struct member_entry *mtable[TABLESIZE];
+
+/*
+ * Dupe table: used to keep track of entries so we don't
+ * print the same thing twice.
+ */
+struct member_entry *dtable[TABLESIZE];
+
+extern struct group *_getgrent(void);
+extern int _setgrent(void);
+extern void _endgrent(void);
+
+static void
+usage(void)
+{
+ fprintf (stderr, "%s\n%s\n",
+ "usage: mknetid [-q] [-g group_file] [-p passwd_file] [-h hosts_file]",
+ " [-n netid_file] [-d domain]");
+ exit(1);
+}
+
+extern FILE *_gr_fp;
+
+int
+main(int argc, char *argv[])
+{
+ FILE *gfp, *pfp, *hfp, *nfp;
+ char readbuf[LINSIZ];
+ char writebuf[LINSIZ];
+ struct group *gr;
+ struct grouplist *glist;
+ char *domain;
+ int ch;
+ gid_t i;
+ char *ptr, *pidptr, *gidptr, *hptr;
+ int quiet = 0;
+
+ domain = NULL;
+ while ((ch = getopt(argc, argv, "g:p:h:n:d:q")) != -1) {
+ switch(ch) {
+ case 'g':
+ groupfile = optarg;
+ break;
+ case 'p':
+ passfile = optarg;
+ break;
+ case 'h':
+ hostsfile = optarg;
+ break;
+ case 'n':
+ netidfile = optarg;
+ break;
+ case 'd':
+ domain = optarg;
+ break;
+ case 'q':
+ quiet++;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (domain == NULL) {
+ if (yp_get_default_domain(&domain))
+ errx(1, "no domain name specified and default \
+domain not set");
+ }
+
+ if ((gfp = fopen(groupfile, "r")) == NULL) {
+ err(1, "%s", groupfile);
+ }
+
+ if ((pfp = fopen(passfile, "r")) == NULL) {
+ err(1, "%s", passfile);
+ }
+
+ if ((hfp = fopen(hostsfile, "r")) == NULL) {
+ err(1, "%s", hostsfile);
+ }
+
+ if ((nfp = fopen(netidfile, "r")) == NULL) {
+ /* netid is optional -- just continue */
+ nfp = NULL;
+ }
+
+ _gr_fp = gfp;
+
+ /* Load all the group membership info into a hash table. */
+
+ _setgrent();
+ while((gr = _getgrent()) != NULL) {
+ while(*gr->gr_mem) {
+ mstore(mtable, *gr->gr_mem, gr->gr_gid, 0);
+ gr->gr_mem++;
+ }
+ }
+
+ fclose(gfp);
+ _endgrent();
+
+ /*
+ * Now parse the passwd database, spewing out the extra
+ * group information we just stored if necessary.
+ */
+ while(fgets(readbuf, LINSIZ, pfp)) {
+ /* Ignore comments: ^[ \t]*# */
+ for (ptr = readbuf; *ptr != '\0'; ptr++)
+ if (*ptr != ' ' && *ptr != '\t')
+ break;
+ if (*ptr == '#' || *ptr == '\0')
+ continue;
+ if ((ptr = strchr(readbuf, ':')) == NULL) {
+ warnx("bad passwd file entry: %s", readbuf);
+ continue;
+ }
+ *ptr = '\0';
+ ptr++;
+ if ((ptr = strchr(ptr, ':')) == NULL) {
+ warnx("bad passwd file entry: %s", readbuf);
+ continue;
+ }
+ *ptr = '\0';
+ ptr++;
+ pidptr = ptr;
+ if ((ptr = strchr(ptr, ':')) == NULL) {
+ warnx("bad passwd file entry: %s", readbuf);
+ continue;
+ }
+ *ptr = '\0';
+ ptr++;
+ gidptr = ptr;
+ if ((ptr = strchr(ptr, ':')) == NULL) {
+ warnx("bad passwd file entry: %s", readbuf);
+ continue;
+ }
+ *ptr = '\0';
+ i = atol(gidptr);
+
+ snprintf(writebuf, sizeof(writebuf), "%s.%s@%s", OPSYS,
+ pidptr, domain);
+
+ if (lookup(dtable, writebuf)) {
+ if (!quiet)
+ warnx("duplicate netid '%s.%s@%s' -- skipping",
+ OPSYS, pidptr, domain);
+ continue;
+ } else {
+ mstore(dtable, writebuf, 0, 1);
+ }
+ printf("%s.%s@%s %s:%s", OPSYS, pidptr, domain, pidptr, gidptr);
+ if ((glist = lookup(mtable, (char *)&readbuf)) != NULL) {
+ while(glist) {
+ if (glist->groupid != i)
+ printf(",%lu", (u_long)glist->groupid);
+ glist = glist->next;
+ }
+ }
+ printf ("\n");
+ }
+
+ fclose(pfp);
+
+ /*
+ * Now parse the hosts database (this part sucks).
+ */
+
+ while ((ptr = fgets(readbuf, LINSIZ, hfp))) {
+ if (*ptr == '#')
+ continue;
+ if (!(hptr = strpbrk(ptr, "#\n")))
+ continue;
+ *hptr = '\0';
+ if (!(hptr = strpbrk(ptr, " \t")))
+ continue;
+ *hptr++ = '\0';
+ ptr = hptr;
+ while (*ptr == ' ' || *ptr == '\t')
+ ptr++;
+ if (!(hptr = strpbrk(ptr, " \t")))
+ continue;
+ *hptr++ = '\0';
+ snprintf(writebuf, sizeof(writebuf), "%s.%s@%s", OPSYS,
+ ptr, domain);
+ if (lookup(dtable, (char *)&writebuf)) {
+ if (!quiet)
+ warnx("duplicate netid '%s' -- skipping",
+ writebuf);
+ continue;
+ } else {
+ mstore(dtable, (char *)&writebuf, 0, 1);
+ }
+ printf ("%s.%s@%s 0:%s\n", OPSYS, ptr, domain, ptr);
+ }
+
+ fclose(hfp);
+
+ /*
+ * Lastly, copy out any extra information in the netid
+ * file. If it's not open, just ignore it: it's optional anyway.
+ */
+
+ if (nfp != NULL) {
+ while(fgets(readbuf, LINSIZ, nfp)) {
+ if (readbuf[0] == '#')
+ continue;
+ if ((ptr = strpbrk((char*)&readbuf, " \t")) == NULL) {
+ warnx("bad netid entry: '%s'", readbuf);
+ continue;
+ }
+
+ writebuf[0] = *ptr;
+ *ptr = '\0';
+ if (lookup(dtable, (char *)&readbuf)) {
+ if (!quiet)
+ warnx("duplicate netid '%s' -- skipping",
+ readbuf);
+ continue;
+ } else {
+ mstore(dtable, (char *)&readbuf, 0, 1);
+ }
+ *ptr = writebuf[0];
+ printf("%s",readbuf);
+ }
+ fclose(nfp);
+ }
+
+ exit(0);
+}
diff --git a/libexec/mknetid/netid.5 b/libexec/mknetid/netid.5
new file mode 100644
index 0000000..b2ec827
--- /dev/null
+++ b/libexec/mknetid/netid.5
@@ -0,0 +1,91 @@
+.\" Copyright (c) 1996 Mats O Jansson <moj@stacken.kth.se>
+.\" 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 Mats O Jansson
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 13, 1996
+.Dt NETID 5
+.Os
+.Sh NAME
+.Nm netid
+.Nd
+.Tn YP
+network credential file
+.Sh DESCRIPTION
+Files in
+.Nm
+format are rare.
+One lives in the
+.Tn YP
+map
+.Pa netid.byname .
+The format is rather simple.
+Each row consists of two items: a key and a value.
+When created by
+.Xr mknetid 8
+there are three types of records.
+.Pp
+The first type is information about which GIDs a UID has:
+.Pp
+.Sm off
+.Li unix . Ao Ar uid Ac @ Aq Ar yp-domain
+.Sm on
+.Sm off
+.Ao Ar uid Ac : Ao Ar gid Ac , Aq Ar gid
+.Sm on
+.Pp
+The second type contains information about hosts:
+.Pp
+.Sm off
+.Li unix . Ao Ar hostname Ac @ Aq Ar yp-domain
+.Sm on
+.Sm off
+.Li 0 : Aq Ar hostname
+.Sm on
+.Pp
+The third type refers to records from a
+.Nm
+file other than the two types above.
+.Sh FILES
+.Bl -tag -width ".Pa /etc/netid" -compact
+.It Pa /etc/netid
+for lines not generated automatically by
+.Xr mknetid 8
+.El
+.Sh EXAMPLES
+A configuration file might look like the following:
+.Bd -literal
+unix.10714@kaka 10714:400,10
+unix.jodie@kaka 0:jodie
+.Ed
+.Sh SEE ALSO
+.Xr mknetid 8 ,
+.Xr yp 8
+.Sh AUTHORS
+.An Mats O Jansson Aq Mt moj@stacken.kth.se
diff --git a/libexec/mknetid/parse_group.c b/libexec/mknetid/parse_group.c
new file mode 100644
index 0000000..6244a8c
--- /dev/null
+++ b/libexec/mknetid/parse_group.c
@@ -0,0 +1,157 @@
+/*
+ * 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. 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
+#if 0
+static const char sccsid[] = "@(#)getgrent.c 8.2 (Berkeley) 3/21/94";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * This is a slightly modified chunk of getgrent(3). All the YP support
+ * and unneeded functions have been stripped out.
+ */
+
+#include <sys/types.h>
+#include <grp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+FILE *_gr_fp;
+static struct group _gr_group;
+static int _gr_stayopen;
+static int grscan(int, int);
+static int start_gr(void);
+
+#define MAXGRP 200
+static char *members[MAXGRP];
+#define MAXLINELENGTH 1024
+static char line[MAXLINELENGTH];
+
+struct group *
+_getgrent(void)
+{
+ if (!_gr_fp && !start_gr()) {
+ return NULL;
+ }
+
+
+ if (!grscan(0, 0))
+ return(NULL);
+ return(&_gr_group);
+}
+
+static int
+start_gr(void)
+{
+ return 1;
+}
+
+int
+_setgroupent(int stayopen)
+{
+ if (!start_gr())
+ return(0);
+ _gr_stayopen = stayopen;
+ return(1);
+}
+
+int
+_setgrent(void)
+{
+ return(_setgroupent(0));
+}
+
+void
+_endgrent(void)
+{
+ if (_gr_fp) {
+ (void)fclose(_gr_fp);
+ _gr_fp = NULL;
+ }
+}
+
+static int
+grscan(int search, int gid)
+{
+ char *cp, **m;
+ char *bp;
+ for (;;) {
+ if (!fgets(line, sizeof(line), _gr_fp))
+ return(0);
+ bp = line;
+ /* skip lines that are too big */
+ if (!strchr(line, '\n')) {
+ int ch;
+
+ while ((ch = getc(_gr_fp)) != '\n' && ch != EOF)
+ ;
+ continue;
+ }
+ if ((_gr_group.gr_name = strsep(&bp, ":\n")) == NULL)
+ break;
+ if (_gr_group.gr_name[0] == '+')
+ continue;
+ if ((_gr_group.gr_passwd = strsep(&bp, ":\n")) == NULL)
+ break;
+ if (!(cp = strsep(&bp, ":\n")))
+ continue;
+ _gr_group.gr_gid = atoi(cp);
+ if (search && _gr_group.gr_gid != gid)
+ continue;
+ cp = NULL;
+ if (bp == NULL) /* !! Must check for this! */
+ break;
+ for (m = _gr_group.gr_mem = members;; bp++) {
+ if (m == &members[MAXGRP - 1])
+ break;
+ if (*bp == ',') {
+ if (cp) {
+ *bp = '\0';
+ *m++ = cp;
+ cp = NULL;
+ }
+ } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
+ if (cp) {
+ *bp = '\0';
+ *m++ = cp;
+ }
+ break;
+ } else if (cp == NULL)
+ cp = bp;
+ }
+ *m = NULL;
+ return(1);
+ }
+ /* NOTREACHED */
+ return (0);
+}
diff --git a/libexec/pppoed/Makefile b/libexec/pppoed/Makefile
new file mode 100644
index 0000000..1ffaffe
--- /dev/null
+++ b/libexec/pppoed/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG= pppoed
+DPADD= ${LIBNETGRAPH}
+LDADD= -lnetgraph
+MAN= pppoed.8
+
+WARNS?= 1
+WFORMAT=0
+
+.include <bsd.prog.mk>
diff --git a/libexec/pppoed/pppoed.8 b/libexec/pppoed/pppoed.8
new file mode 100644
index 0000000..6841bfd
--- /dev/null
+++ b/libexec/pppoed/pppoed.8
@@ -0,0 +1,219 @@
+.\"-
+.\" Copyright (c) 1999-2001 Brian Somers <brian@Awfulhak.org>
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 8, 1999
+.Dt PPPOED 8
+.Os
+.Sh NAME
+.Nm pppoed
+.Nd handle incoming PPP over Ethernet connections
+.Sh SYNOPSIS
+.Nm
+.Op Fl Fd\&
+.Op Fl P Ar pidfile
+.Op Fl a Ar name
+.Op Fl e Ar exec | Fl l Ar label
+.Op Fl n Ar ngdebug
+.Op Fl p Ar provider
+.Ar interface
+.Sh DESCRIPTION
+The
+.Nm
+utility listens to the given
+.Ar interface
+for PPP over Ethernet (PPPoE) service request packets, and actions them
+by negotiating a session then invoking a
+.Xr ppp 8
+program.
+The negotiation is implemented by the
+.Dq pppoe
+netgraph node.
+See
+.Xr ng_pppoe 4
+for details.
+.Pp
+The
+.Nm
+utility
+will only offer services to clients requesting services from the given
+.Ar provider ,
+which is taken as an empty name if not provided.
+If a provider name of
+.Dq *
+is given, any PPPoE requests will be offered service.
+.Pp
+The supplied
+.Ar name
+will be given as the access concentrator name when establishing the connection.
+If no
+.Ar name
+is given, the current base hostname is used.
+.Pp
+After receiving a request (PADI) from the PPPoE netgraph node,
+.Nm
+.Xr fork 2 Ns s
+a child process and returns to service further requests.
+The child process offers service
+(using
+.Ar name )
+and waits for a
+.Dv SUCCESS
+indication from the PPPoE node.
+On receipt of the
+.Dv SUCCESS
+indication,
+.Nm
+will execute
+.Pp
+.D1 Ic exec Pa /usr/sbin/ppp Fl direct Ar label
+.Pp
+as a shell sub-process.
+If
+.Ar label
+has not been specified, it defaults to
+.Ar provider .
+It is possible to specify another command using the
+.Ar exec
+argument.
+This is mandatory if
+.Ar provider
+and
+.Ar label
+are not given.
+The child process will have standard input and standard output
+attached to the same
+.Xr netgraph 4
+data socket
+(see
+.Xr ng_socket 4 )
+when started.
+.Pp
+The environment variables
+.Ev HISMACADDR
+and
+.Ev ACNAME
+are made available to the child process and are set to the MAC address of
+the peer and the name of the AC respectively.
+.Pp
+Upon invocation,
+.Nm
+will attach a
+.Dq pppoe
+netgraph node to the relevant
+.Dq ether
+node using
+.Dq Ar interface Ns \&:
+as the node name, and then connect that
+.Dq pppoe
+node to a local
+.Dq socket
+node.
+If the
+.Fl F
+option has not been given,
+.Nm
+will then go into the background and disassociate itself from the controlling
+terminal.
+When the
+.Fl F
+option is given,
+.Nm
+stays in the foreground.
+.Pp
+If the
+.Fl d
+option is given, additional diagnostics are provided (see the
+.Sx DIAGNOSTICS
+section below).
+If the
+.Fl n
+option is given,
+.Fn NgSetDebug
+is called with an argument of
+.Ar ngdebug .
+.Pp
+If
+.Ar pidfile
+is given,
+.Nm
+will write its process ID to this file on startup.
+.Sh DIAGNOSTICS
+After creating the necessary
+.Xr netgraph 4
+nodes as described above,
+.Nm
+uses
+.Xr syslogd 8
+to report all incoming connections.
+If the
+.Fl d
+option is given,
+.Nm
+will report on the child processes creation of a new netgraph socket, its
+service offer and the invocation of the
+.Xr ppp 8
+program.
+If the
+.Fl n
+option is given, netgraph diagnostic messages are also redirected to
+.Xr syslogd 8 .
+.Pp
+It is sometimes useful to add the following to
+.Pa /etc/syslog.conf :
+.Bd -literal -offset indent
+!pppoed
+*.* /var/log/pppoed.log
+.Ed
+.Pp
+and the following to
+.Pa /etc/newsyslog.conf :
+.Pp
+.Dl "/var/log/pppoed.log 640 3 100 * Z"
+.Sh SEE ALSO
+.Xr NgSetDebug 3 ,
+.Xr netgraph 4 ,
+.Xr ng_ether 4 ,
+.Xr ng_pppoe 4 ,
+.Xr ng_socket 4 ,
+.Xr syslog.conf 5 ,
+.Xr ppp 8 ,
+.Xr syslogd 8
+.Sh HISTORY
+The
+.Nm
+utility was written by
+.An Brian Somers Aq Mt brian@Awfulhak.org
+and first appeared in
+.Fx 3.4 .
+.Sh BUGS
+If another netgraph node is using the given interface,
+.Nm
+will fail to start.
+This is because
+.Xr netgraph 4
+does not currently allow node chaining.
+This may change in the future.
diff --git a/libexec/pppoed/pppoed.c b/libexec/pppoed/pppoed.c
new file mode 100644
index 0000000..a5c0b7a
--- /dev/null
+++ b/libexec/pppoed/pppoed.c
@@ -0,0 +1,692 @@
+/*-
+ * Copyright (c) 1999-2001 Brian Somers <brian@Awfulhak.org>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netgraph.h>
+#include <net/ethernet.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netgraph/ng_ether.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/ng_pppoe.h>
+#include <netgraph/ng_socket.h>
+
+#include <errno.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <sys/fcntl.h>
+#ifndef NOKLDLOAD
+#include <sys/linker.h>
+#include <sys/module.h>
+#endif
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include <termios.h>
+#include <unistd.h>
+
+
+#define DEFAULT_EXEC_PREFIX "exec /usr/sbin/ppp -direct "
+#define HISMACADDR "HISMACADDR"
+#define SESSION_ID "SESSION_ID"
+
+static void nglogx(const char *, ...) __printflike(1, 2);
+
+static int ReceivedSignal;
+
+static int
+usage(const char *prog)
+{
+ fprintf(stderr, "usage: %s [-Fd] [-P pidfile] [-a name] [-e exec | -l label]"
+ " [-n ngdebug] [-p provider] interface\n", prog);
+ return EX_USAGE;
+}
+
+static void
+Farewell(int sig)
+{
+ ReceivedSignal = sig;
+}
+
+static int
+ConfigureNode(const char *prog, const char *iface, const char *provider,
+ int cs, int ds, int debug, struct ngm_connect *ngc)
+{
+ /*
+ * We're going to do this with the passed `ds' & `cs' descriptors:
+ *
+ * .---------.
+ * | ether |
+ * | <iface> |
+ * `---------'
+ * (orphan) ds cs
+ * | | |
+ * | | |
+ * (ethernet) | |
+ * .---------. .-----------.
+ * | pppoe | | socket |
+ * | <iface> |(pppoe-<pid>)<---->(pppoe-<pid>)| <unnamed> |
+ * `--------- `-----------'
+ * (exec-<pid>)
+ * ^ .-----------. .-------------.
+ * | | socket | | ppp -direct |
+ * `--->(exec-<pid>)| <unnamed> |--fd--| provider |
+ * `-----------' `-------------'
+ *
+ * where there are potentially many ppp processes running off of the
+ * same PPPoE node.
+ * The exec-<pid> hook isn't made 'till we Spawn().
+ */
+
+ char *epath, *spath;
+ struct ngpppoe_init_data *data;
+ const struct hooklist *hlist;
+ const struct nodeinfo *ninfo;
+ const struct linkinfo *nlink;
+ struct ngm_mkpeer mkp;
+ struct ng_mesg *resp;
+ u_char rbuf[2048];
+ int f, plen;
+
+ /*
+ * Ask for a list of hooks attached to the "ether" node. This node should
+ * magically exist as a way of hooking stuff onto an ethernet device
+ */
+ epath = (char *)alloca(strlen(iface) + 2);
+ sprintf(epath, "%s:", iface);
+
+ if (debug)
+ fprintf(stderr, "Sending NGM_LISTHOOKS to %s\n", epath);
+
+ if (NgSendMsg(cs, epath, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0) < 0) {
+ if (errno == ENOENT)
+ fprintf(stderr, "%s Cannot send a netgraph message: Invalid interface\n",
+ epath);
+ else
+ fprintf(stderr, "%s Cannot send a netgraph message: %s\n",
+ epath, strerror(errno));
+ return EX_UNAVAILABLE;
+ }
+
+ /* Get our list back */
+ resp = (struct ng_mesg *)rbuf;
+ if (NgRecvMsg(cs, resp, sizeof rbuf, NULL) <= 0) {
+ perror("Cannot get netgraph response");
+ return EX_UNAVAILABLE;
+ }
+
+ hlist = (const struct hooklist *)resp->data;
+ ninfo = &hlist->nodeinfo;
+
+ if (debug)
+ fprintf(stderr, "Got reply from id [%x]: Type %s with %d hooks\n",
+ ninfo->id, ninfo->type, ninfo->hooks);
+
+ /* Make sure we've got the right type of node */
+ if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE, sizeof NG_ETHER_NODE_TYPE - 1)) {
+ fprintf(stderr, "%s Unexpected node type ``%s'' (wanted ``"
+ NG_ETHER_NODE_TYPE "'')\n", epath, ninfo->type);
+ return EX_DATAERR;
+ }
+
+ /* look for a hook already attached. */
+ for (f = 0; f < ninfo->hooks; f++) {
+ nlink = &hlist->link[f];
+
+ if (debug)
+ fprintf(stderr, " Got [%x]:%s -> [%x]:%s\n", ninfo->id,
+ nlink->ourhook, nlink->nodeinfo.id, nlink->peerhook);
+
+ if (!strcmp(nlink->ourhook, NG_ETHER_HOOK_ORPHAN) ||
+ !strcmp(nlink->ourhook, NG_ETHER_HOOK_DIVERT)) {
+ /*
+ * Something is using the data coming out of this `ether' node.
+ * If it's a PPPoE node, we use that node, otherwise we complain that
+ * someone else is using the node.
+ */
+ if (strcmp(nlink->nodeinfo.type, NG_PPPOE_NODE_TYPE)) {
+ fprintf(stderr, "%s Node type %s is currently active\n",
+ epath, nlink->nodeinfo.type);
+ return EX_UNAVAILABLE;
+ }
+ break;
+ }
+ }
+
+ if (f == ninfo->hooks) {
+ /*
+ * Create a new PPPoE node connected to the `ether' node using
+ * the magic `orphan' and `ethernet' hooks
+ */
+ snprintf(mkp.type, sizeof mkp.type, "%s", NG_PPPOE_NODE_TYPE);
+ snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", NG_ETHER_HOOK_ORPHAN);
+ snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", NG_PPPOE_HOOK_ETHERNET);
+
+ if (debug)
+ fprintf(stderr, "Send MKPEER: %s%s -> [type %s]:%s\n", epath,
+ mkp.ourhook, mkp.type, mkp.peerhook);
+
+ if (NgSendMsg(cs, epath, NGM_GENERIC_COOKIE,
+ NGM_MKPEER, &mkp, sizeof mkp) < 0) {
+ fprintf(stderr, "%s Cannot create a peer PPPoE node: %s\n",
+ epath, strerror(errno));
+ return EX_OSERR;
+ }
+ }
+
+ /* Connect the PPPoE node to our socket node. */
+ snprintf(ngc->path, sizeof ngc->path, "%s%s", epath, NG_ETHER_HOOK_ORPHAN);
+ snprintf(ngc->ourhook, sizeof ngc->ourhook, "pppoe-%ld", (long)getpid());
+ memcpy(ngc->peerhook, ngc->ourhook, sizeof ngc->peerhook);
+
+ if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE,
+ NGM_CONNECT, ngc, sizeof *ngc) < 0) {
+ perror("Cannot CONNECT PPPoE and socket nodes");
+ return EX_OSERR;
+ }
+
+ plen = strlen(provider);
+
+ data = (struct ngpppoe_init_data *)alloca(sizeof *data + plen);
+ snprintf(data->hook, sizeof data->hook, "%s", ngc->peerhook);
+ memcpy(data->data, provider, plen);
+ data->data_len = plen;
+
+ spath = (char *)alloca(strlen(ngc->peerhook) + 3);
+ strcpy(spath, ".:");
+ strcpy(spath + 2, ngc->ourhook);
+
+ if (debug) {
+ if (provider)
+ fprintf(stderr, "Sending PPPOE_LISTEN to %s, provider %s\n",
+ spath, provider);
+ else
+ fprintf(stderr, "Sending PPPOE_LISTEN to %s\n", spath);
+ }
+
+ if (NgSendMsg(cs, spath, NGM_PPPOE_COOKIE, NGM_PPPOE_LISTEN,
+ data, sizeof *data + plen) == -1) {
+ fprintf(stderr, "%s: Cannot LISTEN on netgraph node: %s\n",
+ spath, strerror(errno));
+ return EX_OSERR;
+ }
+
+ return 0;
+}
+
+static void
+Spawn(const char *prog, const char *acname, const char *provider,
+ const char *exec, struct ngm_connect ngc, int cs, int ds, void *request,
+ int sz, int debug)
+{
+ char msgbuf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_sts)];
+ struct ng_mesg *rep = (struct ng_mesg *)msgbuf;
+ struct ngpppoe_sts *sts = (struct ngpppoe_sts *)(msgbuf + sizeof *rep);
+ struct ngpppoe_init_data *data;
+ char env[18], unknown[14], sessionid[5], *path;
+ unsigned char *macaddr;
+ const char *msg;
+ int ret, slen;
+
+ switch ((ret = fork())) {
+ case -1:
+ syslog(LOG_ERR, "fork: %m");
+ break;
+
+ case 0:
+ switch (fork()) {
+ case 0:
+ break;
+ case -1:
+ _exit(errno);
+ default:
+ _exit(0);
+ }
+ close(cs);
+ close(ds);
+
+ /* Create a new socket node */
+ if (debug)
+ syslog(LOG_INFO, "Creating a new socket node");
+
+ if (NgMkSockNode(NULL, &cs, &ds) == -1) {
+ syslog(LOG_ERR, "Cannot create netgraph socket node: %m");
+ _exit(EX_CANTCREAT);
+ }
+
+ /* Connect the PPPoE node to our new socket node. */
+ snprintf(ngc.ourhook, sizeof ngc.ourhook, "exec-%ld", (long)getpid());
+ memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook);
+
+ if (debug)
+ syslog(LOG_INFO, "Sending CONNECT from .:%s -> %s.%s",
+ ngc.ourhook, ngc.path, ngc.peerhook);
+ if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE,
+ NGM_CONNECT, &ngc, sizeof ngc) < 0) {
+ syslog(LOG_ERR, "Cannot CONNECT PPPoE and socket nodes: %m");
+ _exit(EX_OSERR);
+ }
+
+ /*
+ * If we tell the socket node not to LINGER, it will go away when
+ * the last hook is removed.
+ */
+ if (debug)
+ syslog(LOG_INFO, "Sending NGM_SOCK_CMD_NOLINGER to socket");
+ if (NgSendMsg(cs, ".:", NGM_SOCKET_COOKIE,
+ NGM_SOCK_CMD_NOLINGER, NULL, 0) < 0) {
+ syslog(LOG_ERR, "Cannot send NGM_SOCK_CMD_NOLINGER: %m");
+ _exit(EX_OSERR);
+ }
+
+ /* Put the PPPoE node into OFFER mode */
+ slen = strlen(acname);
+ data = (struct ngpppoe_init_data *)alloca(sizeof *data + slen);
+ snprintf(data->hook, sizeof data->hook, "%s", ngc.ourhook);
+ memcpy(data->data, acname, slen);
+ data->data_len = slen;
+
+ path = (char *)alloca(strlen(ngc.ourhook) + 3);
+ strcpy(path, ".:");
+ strcpy(path + 2, ngc.ourhook);
+
+ syslog(LOG_INFO, "Offering to %s as access concentrator %s",
+ path, acname);
+ if (NgSendMsg(cs, path, NGM_PPPOE_COOKIE, NGM_PPPOE_OFFER,
+ data, sizeof *data + slen) == -1) {
+ syslog(LOG_INFO, "%s: Cannot OFFER on netgraph node: %m", path);
+ _exit(EX_OSERR);
+ }
+ /* If we have a provider code, set it */
+ if (provider) {
+ slen = strlen(provider);
+ data = (struct ngpppoe_init_data *)alloca(sizeof *data + slen);
+ snprintf(data->hook, sizeof data->hook, "%s", ngc.ourhook);
+ memcpy(data->data, provider, slen);
+ data->data_len = slen;
+
+ syslog(LOG_INFO, "adding to %s as offered service %s",
+ path, acname);
+ if (NgSendMsg(cs, path, NGM_PPPOE_COOKIE, NGM_PPPOE_SERVICE,
+ data, sizeof *data + slen) == -1) {
+ syslog(LOG_INFO, "%s: Cannot add service on netgraph node: %m", path);
+ _exit(EX_OSERR);
+ }
+ }
+
+ /* Put the peer's MAC address in the environment */
+ if (sz >= sizeof(struct ether_header)) {
+ macaddr = ((struct ether_header *)request)->ether_shost;
+ snprintf(env, sizeof(env), "%x:%x:%x:%x:%x:%x",
+ macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4],
+ macaddr[5]);
+ if (setenv(HISMACADDR, env, 1) != 0)
+ syslog(LOG_INFO, "setenv: cannot set %s: %m", HISMACADDR);
+ }
+
+ /* And send our request data to the waiting node */
+ if (debug)
+ syslog(LOG_INFO, "Sending original request to %s (%d bytes)", path, sz);
+ if (NgSendData(ds, ngc.ourhook, request, sz) == -1) {
+ syslog(LOG_ERR, "Cannot send original request to %s: %m", path);
+ _exit(EX_OSERR);
+ }
+
+ /* Then wait for a success indication */
+
+ if (debug)
+ syslog(LOG_INFO, "Waiting for a SUCCESS reply %s", path);
+
+ do {
+ if ((ret = NgRecvMsg(cs, rep, sizeof msgbuf, NULL)) < 0) {
+ syslog(LOG_ERR, "%s: Cannot receive a message: %m", path);
+ _exit(EX_OSERR);
+ }
+
+ if (ret == 0) {
+ /* The socket has been closed */
+ syslog(LOG_INFO, "%s: Client timed out", path);
+ _exit(EX_TEMPFAIL);
+ }
+
+ if (rep->header.version != NG_VERSION) {
+ syslog(LOG_ERR, "%ld: Unexpected netgraph version, expected %ld",
+ (long)rep->header.version, (long)NG_VERSION);
+ _exit(EX_PROTOCOL);
+ }
+
+ if (rep->header.typecookie != NGM_PPPOE_COOKIE) {
+ syslog(LOG_INFO, "%ld: Unexpected netgraph cookie, expected %ld",
+ (long)rep->header.typecookie, (long)NGM_PPPOE_COOKIE);
+ continue;
+ }
+
+ switch (rep->header.cmd) {
+ case NGM_PPPOE_SET_FLAG: msg = "SET_FLAG"; break;
+ case NGM_PPPOE_CONNECT: msg = "CONNECT"; break;
+ case NGM_PPPOE_LISTEN: msg = "LISTEN"; break;
+ case NGM_PPPOE_OFFER: msg = "OFFER"; break;
+ case NGM_PPPOE_SUCCESS: msg = "SUCCESS"; break;
+ case NGM_PPPOE_FAIL: msg = "FAIL"; break;
+ case NGM_PPPOE_CLOSE: msg = "CLOSE"; break;
+ case NGM_PPPOE_GET_STATUS: msg = "GET_STATUS"; break;
+ case NGM_PPPOE_ACNAME:
+ msg = "ACNAME";
+ if (setenv("ACNAME", sts->hook, 1) != 0)
+ syslog(LOG_WARNING, "setenv: cannot set ACNAME=%s: %m",
+ sts->hook);
+ break;
+ case NGM_PPPOE_SESSIONID:
+ msg = "SESSIONID";
+ snprintf(sessionid, sizeof sessionid, "%04x", *(u_int16_t *)sts);
+ if (setenv("SESSIONID", sessionid, 1) != 0)
+ syslog(LOG_WARNING, "setenv: cannot set SESSIONID=%s: %m",
+ sessionid);
+ break;
+ default:
+ snprintf(unknown, sizeof unknown, "<%d>", (int)rep->header.cmd);
+ msg = unknown;
+ break;
+ }
+
+ switch (rep->header.cmd) {
+ case NGM_PPPOE_FAIL:
+ case NGM_PPPOE_CLOSE:
+ syslog(LOG_ERR, "Received NGM_PPPOE_%s (hook \"%s\")",
+ msg, sts->hook);
+ _exit(0);
+ }
+
+ syslog(LOG_INFO, "Received NGM_PPPOE_%s (hook \"%s\")", msg, sts->hook);
+ } while (rep->header.cmd != NGM_PPPOE_SUCCESS);
+
+ dup2(ds, STDIN_FILENO);
+ dup2(ds, STDOUT_FILENO);
+ close(ds);
+ close(cs);
+
+ setsid();
+ syslog(LOG_INFO, "Executing: %s", exec);
+ execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", exec, (char *)NULL);
+ syslog(LOG_ERR, "execlp failed: %m");
+ _exit(EX_OSFILE);
+
+ default:
+ wait(&ret);
+ errno = ret;
+ if (errno)
+ syslog(LOG_ERR, "Second fork failed: %m");
+ break;
+ }
+}
+
+#ifndef NOKLDLOAD
+static int
+LoadModules(void)
+{
+ const char *module[] = { "netgraph", "ng_socket", "ng_ether", "ng_pppoe" };
+ int f;
+
+ for (f = 0; f < sizeof module / sizeof *module; f++)
+ if (modfind(module[f]) == -1 && kldload(module[f]) == -1) {
+ fprintf(stderr, "kldload: %s: %s\n", module[f], strerror(errno));
+ return 0;
+ }
+
+ return 1;
+}
+#endif
+
+static void
+nglog(const char *fmt, ...)
+{
+ char nfmt[256];
+ va_list ap;
+
+ snprintf(nfmt, sizeof nfmt, "%s: %s", fmt, strerror(errno));
+ va_start(ap, fmt);
+ vsyslog(LOG_INFO, nfmt, ap);
+ va_end(ap);
+}
+
+static void
+nglogx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsyslog(LOG_INFO, fmt, ap);
+ va_end(ap);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char hostname[MAXHOSTNAMELEN], *exec, rhook[NG_HOOKSIZ];
+ unsigned char response[1024];
+ const char *label, *prog, *provider, *acname;
+ struct ngm_connect ngc;
+ struct sigaction act;
+ int ch, cs, ds, ret, optF, optd, optn, sz, f;
+ const char *pidfile;
+
+ prog = strrchr(argv[0], '/');
+ prog = prog ? prog + 1 : argv[0];
+ pidfile = NULL;
+ exec = NULL;
+ label = NULL;
+ acname = NULL;
+ provider = "";
+ optF = optd = optn = 0;
+
+ while ((ch = getopt(argc, argv, "FP:a:de:l:n:p:")) != -1) {
+ switch (ch) {
+ case 'F':
+ optF = 1;
+ break;
+
+ case 'P':
+ pidfile = optarg;
+ break;
+
+ case 'a':
+ acname = optarg;
+ break;
+
+ case 'd':
+ optd = 1;
+ break;
+
+ case 'e':
+ exec = optarg;
+ break;
+
+ case 'l':
+ label = optarg;
+ break;
+
+ case 'n':
+ optn = 1;
+ NgSetDebug(atoi(optarg));
+ break;
+
+ case 'p':
+ provider = optarg;
+ break;
+
+ default:
+ return usage(prog);
+ }
+ }
+
+ if (optind >= argc || optind + 2 < argc)
+ return usage(prog);
+
+ if (exec != NULL && label != NULL)
+ return usage(prog);
+
+ if (exec == NULL) {
+ if (label == NULL)
+ label = provider;
+ if (label == NULL) {
+ fprintf(stderr, "%s: Either a provider, a label or an exec command"
+ " must be given\n", prog);
+ return usage(prog);
+ }
+ exec = (char *)alloca(sizeof DEFAULT_EXEC_PREFIX + strlen(label));
+ if (exec == NULL) {
+ fprintf(stderr, "%s: Cannot allocate %zu bytes\n", prog,
+ sizeof DEFAULT_EXEC_PREFIX + strlen(label));
+ return EX_OSERR;
+ }
+ strcpy(exec, DEFAULT_EXEC_PREFIX);
+ strcpy(exec + sizeof DEFAULT_EXEC_PREFIX - 1, label);
+ }
+
+ if (acname == NULL) {
+ char *dot;
+
+ if (gethostname(hostname, sizeof hostname))
+ strcpy(hostname, "localhost");
+ else if ((dot = strchr(hostname, '.')))
+ *dot = '\0';
+
+ acname = hostname;
+ }
+
+#ifndef NOKLDLOAD
+ if (!LoadModules())
+ return EX_UNAVAILABLE;
+#endif
+
+ /* Create a socket node */
+ if (NgMkSockNode(NULL, &cs, &ds) == -1) {
+ perror("Cannot create netgraph socket node");
+ return EX_CANTCREAT;
+ }
+
+ /* Connect it up (and fill in `ngc') */
+ if ((ret = ConfigureNode(prog, argv[optind], provider, cs, ds,
+ optd, &ngc)) != 0) {
+ close(cs);
+ close(ds);
+ return ret;
+ }
+
+ if (!optF && daemon(1, 0) == -1) {
+ perror("daemon()");
+ close(cs);
+ close(ds);
+ return EX_OSERR;
+ }
+
+
+ if (pidfile != NULL) {
+ FILE *fp;
+
+ if ((fp = fopen(pidfile, "w")) == NULL) {
+ perror(pidfile);
+ close(cs);
+ close(ds);
+ return EX_CANTCREAT;
+ } else {
+ fprintf(fp, "%d\n", (int)getpid());
+ fclose(fp);
+ }
+ }
+
+ openlog(prog, LOG_PID | (optF ? LOG_PERROR : 0), LOG_DAEMON);
+ if (!optF && optn)
+ NgSetErrLog(nglog, nglogx);
+
+ memset(&act, '\0', sizeof act);
+ act.sa_handler = Farewell;
+ act.sa_flags = 0;
+ sigemptyset(&act.sa_mask);
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGQUIT, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+
+ while (!ReceivedSignal) {
+ if (*provider)
+ syslog(LOG_INFO, "Listening as provider %s", provider);
+ else
+ syslog(LOG_INFO, "Listening");
+
+ switch (sz = NgRecvData(ds, response, sizeof response, rhook)) {
+ case -1:
+ syslog(LOG_INFO, "NgRecvData: %m");
+ break;
+ case 0:
+ syslog(LOG_INFO, "NgRecvData: socket closed");
+ break;
+ default:
+ if (optd) {
+ char *dbuf, *ptr;
+
+ ptr = dbuf = alloca(sz * 2 + 1);
+ for (f = 0; f < sz; f++, ptr += 2)
+ sprintf(ptr, "%02x", (u_char)response[f]);
+ *ptr = '\0';
+ syslog(LOG_INFO, "Got %d bytes of data: %s", sz, dbuf);
+ }
+ }
+ if (sz <= 0) {
+ ret = EX_UNAVAILABLE;
+ break;
+ }
+ Spawn(prog, acname, provider, exec, ngc, cs, ds, response, sz, optd);
+ }
+
+ if (pidfile)
+ remove(pidfile);
+
+ if (ReceivedSignal) {
+ syslog(LOG_INFO, "Received signal %d, exiting", ReceivedSignal);
+
+ signal(ReceivedSignal, SIG_DFL);
+ raise(ReceivedSignal);
+
+ /* NOTREACHED */
+
+ ret = -ReceivedSignal;
+ }
+
+ return ret;
+}
diff --git a/libexec/rbootd/Makefile b/libexec/rbootd/Makefile
new file mode 100644
index 0000000..70b6555
--- /dev/null
+++ b/libexec/rbootd/Makefile
@@ -0,0 +1,11 @@
+# from: @(#)Makefile 8.1 (Berkeley) 6/4/93
+# $FreeBSD$
+
+PROG= rbootd
+SRCS= bpf.c conf.c parseconf.c rbootd.c rmpproto.c utils.c
+MAN= rbootd.8
+
+WARNS?= 1
+WFORMAT=0
+
+.include <bsd.prog.mk>
diff --git a/libexec/rbootd/bpf.c b/libexec/rbootd/bpf.c
new file mode 100644
index 0000000..6291f1e
--- /dev/null
+++ b/libexec/rbootd/bpf.c
@@ -0,0 +1,406 @@
+/*
+ * 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. 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: @(#)bpf.c 8.1 (Berkeley) 6/4/93
+ *
+ * From: Utah Hdr: bpf.c 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)bpf.c 8.1 (Berkeley) 6/4/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.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_int8_t *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(void)
+{
+ 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;
+ memmove((char *)&ifr.ifr_addr.sa_data[0], &RmpMcastAddr[0], RMP_ADDRLEN);
+ 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_int8_t *)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(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;
+
+ if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 ||
+ ifc.ifc_len < sizeof(struct ifreq)) {
+ (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFCONF): %m");
+ return(NULL);
+ }
+ 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(RMPCONN *rconn, int doread)
+{
+ int datlen, caplen, hdrlen;
+ static u_int8_t *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;
+ memmove((char *)&rconn->tstamp, (char *)&bhp->bh_tstamp,
+ sizeof(struct timeval));
+ memmove((char *)&rconn->rmp, (char *)bp + hdrlen, 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(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(void)
+{
+ 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;
+ memmove((char *)&ifr.ifr_addr.sa_data[0], &RmpMcastAddr[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..32b8782
--- /dev/null
+++ b/libexec/rbootd/conf.c
@@ -0,0 +1,89 @@
+/*
+ * 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. 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: @(#)conf.c 8.1 (Berkeley) 6/4/93
+ *
+ * From: Utah Hdr: conf.c 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)conf.c 8.1 (Berkeley) 6/4/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#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 MyHost[MAXHOSTNAMELEN]; /* host name */
+pid_t 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_int16_t 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 */
+
+u_int8_t 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..ebf8243
--- /dev/null
+++ b/libexec/rbootd/defs.h
@@ -0,0 +1,182 @@
+/*
+ * 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. 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: @(#)defs.h 8.1 (Berkeley) 6/4/93
+ *
+ * From: Utah Hdr: defs.h 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ *
+ * $FreeBSD$
+ */
+
+#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 256
+#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_int8_t 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 MyHost[]; /* this hosts' name */
+extern pid_t 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_int16_t 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 u_int8_t RmpMcastAddr[]; /* RMP multicast address */
+
+void AddConn(RMPCONN *);
+int BootDone(RMPCONN *);
+void BpfClose(void);
+char *BpfGetIntfName(char **);
+int BpfOpen(void);
+int BpfRead(RMPCONN *, int);
+int BpfWrite(RMPCONN *);
+void DebugOff(int);
+void DebugOn(int);
+void DispPkt(RMPCONN *, int);
+void DoTimeout(void);
+void DspFlnm(u_int, char *);
+void Exit(int);
+CLIENT *FindClient(RMPCONN *);
+RMPCONN *FindConn(RMPCONN *);
+void FreeClients(void);
+void FreeConn(RMPCONN *);
+void FreeConns(void);
+int GetBootFiles(void);
+char *GetEtherAddr(u_int8_t *);
+CLIENT *NewClient(u_int8_t *);
+RMPCONN *NewConn(RMPCONN *);
+char *NewStr(char *);
+u_int8_t *ParseAddr(char *);
+int ParseConfig(void);
+void ProcessPacket(RMPCONN *, CLIENT *);
+void ReConfig(int);
+void RemoveConn(RMPCONN *);
+int SendBootRepl(struct rmp_packet *, RMPCONN *, char *[]);
+int SendFileNo(struct rmp_packet *, RMPCONN *, char *[]);
+int SendPacket(RMPCONN *);
+int SendReadRepl(RMPCONN *);
+int SendServerID(RMPCONN *);
diff --git a/libexec/rbootd/parseconf.c b/libexec/rbootd/parseconf.c
new file mode 100644
index 0000000..58b9ac5
--- /dev/null
+++ b/libexec/rbootd/parseconf.c
@@ -0,0 +1,358 @@
+/*
+ * 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. 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: @(#)parseconf.c 8.1 (Berkeley) 6/4/93
+ *
+ * From: Utah Hdr: parseconf.c 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)parseconf.c 8.1 (Berkeley) 6/4/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.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(void)
+{
+ FILE *fp;
+ CLIENT *client;
+ u_int8_t *addr;
+ char line[C_LINELEN];
+ char *cp, *bcp;
+ 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 = strchr(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: can't 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 can't 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.
+*/
+u_int8_t *
+ParseAddr(char *str)
+{
+ static u_int8_t addr[RMP_ADDRLEN];
+ char *cp;
+ unsigned i;
+ int part, subpart;
+
+ memset((char *)&addr[0], 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(void)
+{
+ DIR *dfd;
+ struct stat statb;
+ struct dirent *dp;
+ 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) /* can't 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..54f8c67
--- /dev/null
+++ b/libexec/rbootd/pathnames.h
@@ -0,0 +1,49 @@
+/*
+ * 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. 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: @(#)pathnames.h 8.1 (Berkeley) 6/4/93
+ *
+ * From: Utah Hdr: pathnames.h 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ *
+ * $FreeBSD$
+ */
+
+#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..6baf9f2
--- /dev/null
+++ b/libexec/rbootd/rbootd.8
@@ -0,0 +1,153 @@
+.\" 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. 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: @(#)rbootd.8 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD$
+.\"
+.\" 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
+.Op Fl ad
+.Op Fl i Ar interface
+.Op config_file
+.Sh DESCRIPTION
+The
+.Nm
+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
+only responds to requests from machines listed in its configuration file.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.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
+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
+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
+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:
+.Bd -literal
+#
+# ethernet addr boot file(s) comments
+#
+08:00:09:0:66:ad SYSHPBSD # snake (4.3BSD)
+08:00:09:0:59:5b # vandy (anything)
+8::9:1:C6:75 SYSHPBSD,SYSHPUX # jaguar (either)
+.Ed
+.Pp
+The
+.Nm
+utility 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 xxxxxxxx
+.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 Pa /dev/bpf#
+packet-filter device
+.It Pa /etc/rbootd.conf
+configuration file
+.It Pa /tmp/rbootd.dbg
+debug output
+.It Pa /usr/mdec/rbootd
+directory containing boot files
+.It Pa /var/run/rbootd.pid
+process id
+.El
+.Sh SEE ALSO
+.Xr kill 1 ,
+.Xr socket 2 ,
+.Xr signal 3 ,
+.Xr syslog 3
+.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..de142f1
--- /dev/null
+++ b/libexec/rbootd/rbootd.c
@@ -0,0 +1,449 @@
+/*
+ * 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. 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: @(#)rbootd.c 8.1 (Berkeley) 6/4/93
+ *
+ * From: Utah Hdr: rbootd.c 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)rbootd.c 8.1 (Berkeley) 6/4/93";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <err.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"
+
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int c, fd, omask, maxfds;
+ fd_set rset;
+
+ /*
+ * 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:")) != -1)
+ switch(c) {
+ case 'a':
+ BootAny++;
+ break;
+ case 'd':
+ DebugFlg++;
+ break;
+ case 'i':
+ IntfName = optarg;
+ break;
+ default:
+ usage();
+ }
+ for (; optind < argc; optind++) {
+ if (ConfigFile == NULL)
+ ConfigFile = argv[optind];
+ else {
+ warnx("too many config files (`%s' ignored)",
+ 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);
+ (void) fclose(stderr); /* finished with it */
+ } else {
+ if (daemon(0, 0))
+ err(1, "can't detach from terminal");
+
+ (void) signal(SIGUSR1, DebugOn);
+ (void) signal(SIGUSR2, DebugOff);
+ }
+
+ openlog("rbootd", LOG_PID, LOG_DAEMON);
+
+ /*
+ * 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) {
+ /* Backslash to avoid trigraph '??)'. */
+ syslog(LOG_NOTICE, "restarted (?\?)");
+ /* BpfGetIntfName() returns safe names, using %m */
+ syslog(LOG_ERR, "%s", 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 - 1) < 0) {
+ syslog(LOG_ERR, "gethostname: %m");
+ Exit(0);
+ }
+ MyHost[MAXHOSTNAMELEN - 1] = '\0';
+
+ MyPid = getpid();
+
+ /*
+ * Write proc's pid to a file.
+ */
+ {
+ FILE *fp;
+
+ if ((fp = fopen(PidFile, "w")) != NULL) {
+ (void) fprintf(fp, "%d\n", (int) 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 isn't necessary */
+ nsel = select(maxfds, &r, NULL, NULL, NULL);
+ } else {
+ timeout.tv_sec = RMP_TIMEOUT;
+ timeout.tv_usec = 0;
+ nsel = select(maxfds, &r, NULL, NULL, &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);
+ }
+ }
+ }
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: rbootd [-ad] [-i interface] [config_file]\n");
+ exit (1);
+}
+
+/*
+** DoTimeout -- Free any connections that have timed out.
+**
+** Parameters:
+** None.
+**
+** Returns:
+** Nothing.
+**
+** Side Effects:
+** - Timed out connections in `RmpConns' will be freed.
+*/
+void
+DoTimeout(void)
+{
+ RMPCONN *rtmp;
+ time_t now;
+
+ /*
+ * For each active connection, if RMP_TIMEOUT seconds have passed
+ * since the last packet was sent, delete the connection.
+ */
+ now = time(NULL);
+ for (rtmp = RmpConns; rtmp != NULL; rtmp = rtmp->next)
+ if ((rtmp->tstamp.tv_sec + RMP_TIMEOUT) < now) {
+ 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(RMPCONN *rconn)
+{
+ 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(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(int signo __unused)
+{
+ 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(int signo __unused)
+{
+ 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(int signo __unused)
+{
+ 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..09b67ee
--- /dev/null
+++ b/libexec/rbootd/rmp.h
@@ -0,0 +1,93 @@
+/*
+ * 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. 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: @(#)rmp.h 8.1 (Berkeley) 6/4/93
+ *
+ * From: Utah Hdr: rmp.h 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * 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_int8_t daddr[RMP_ADDRLEN];
+ u_int8_t saddr[RMP_ADDRLEN];
+ u_int16_t len;
+};
+
+/*
+ * HP uses 802.2 LLC with their own local extensions. This struct makes
+ * sense out of this data (encapsulated in the above 802.3 packet).
+ */
+
+struct hp_llc {
+ u_int8_t dsap; /* 802.2 DSAP */
+ u_int8_t ssap; /* 802.2 SSAP */
+ u_int16_t cntrl; /* 802.2 control field */
+ u_int16_t filler; /* HP filler (must be zero) */
+ u_int16_t dxsap; /* HP extended DSAP */
+ u_int16_t sxsap; /* HP extended SSAP */
+};
diff --git a/libexec/rbootd/rmp_var.h b/libexec/rbootd/rmp_var.h
new file mode 100644
index 0000000..fdaacf7
--- /dev/null
+++ b/libexec/rbootd/rmp_var.h
@@ -0,0 +1,242 @@
+/*
+ * 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. 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: @(#)rmp_var.h 8.1 (Berkeley) 6/4/93
+ *
+ * from: Utah Hdr: rmp_var.h 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * 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_int8_t))
+#define RMPDONESIZE (sizeof(struct hp_hdr) + sizeof(struct hp_llc) + \
+ sizeof(struct rmp_boot_done))
+#define RMPBOOTDATA 255
+#define RMPREADDATA (RMPDATALEN - \
+ (2*sizeof(u_int8_t)+sizeof(u_int16_t)+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 can't access (or don't, by default, align) words
+ * on half-word boundaries. If you know that your machine does not suffer
+ * from this problem, add it to the vax/tahoe/m68k #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. Endianness is handled by use of ntohl/htonl
+ */
+#if defined(__vax__) || defined(__tahoe__) || defined(__m68k__)
+
+typedef u_int32_t u_word;
+
+#define WORDZE(w) ((w) == 0)
+#define ZEROWORD(w) (w) = 0
+#define COPYWORD(w1,w2) (w2) = (w1)
+#define GETWORD(w, i) (i) = ntohl(w)
+#define PUTWORD(i, w) (w) = htonl(i)
+
+#else
+
+#define _WORD_HIGHPART 0
+#define _WORD_LOWPART 1
+
+typedef struct _uword { u_int16_t 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_int32_t)ntohs((w).val[_WORD_HIGHPART])) << 16) | ntohs((w).val[_WORD_LOWPART])
+#define PUTWORD(i, w) \
+ { (w).val[_WORD_HIGHPART] = htons((u_int16_t) ((i >> 16) & 0xffff)); \
+ (w).val[_WORD_LOWPART] = htons((u_int16_t) (i & 0xffff)); \
+ }
+
+#endif
+
+/*
+ * Packet structures.
+ */
+
+struct rmp_raw { /* generic RMP packet */
+ u_int8_t rmp_type; /* packet type */
+ u_int8_t rmp_rawdata[RMPDATALEN-1];
+};
+
+struct rmp_boot_req { /* boot request */
+ u_int8_t rmp_type; /* packet type (RMP_BOOT_REQ) */
+ u_int8_t rmp_retcode; /* return code (0) */
+ u_word rmp_seqno; /* sequence number (real time clock) */
+ u_int16_t rmp_session; /* session id (normally 0) */
+ u_int16_t rmp_version; /* protocol version (RMP_VERSION) */
+ char rmp_machtype[RMP_MACHLEN]; /* machine type */
+ u_int8_t rmp_flnmsize; /* length of rmp_flnm */
+ restofpkt rmp_flnm; /* name of file to be read */
+};
+
+struct rmp_boot_repl { /* boot reply */
+ u_int8_t rmp_type; /* packet type (RMP_BOOT_REPL) */
+ u_int8_t rmp_retcode; /* return code (normally 0) */
+ u_word rmp_seqno; /* sequence number (from boot req) */
+ u_int16_t rmp_session; /* session id (generated) */
+ u_int16_t rmp_version; /* protocol version (RMP_VERSION) */
+ u_int8_t rmp_flnmsize; /* length of rmp_flnm */
+ restofpkt rmp_flnm; /* name of file (from boot req) */
+};
+
+struct rmp_read_req { /* read request */
+ u_int8_t rmp_type; /* packet type (RMP_READ_REQ) */
+ u_int8_t rmp_retcode; /* return code (0) */
+ u_word rmp_offset; /* file relative byte offset */
+ u_int16_t rmp_session; /* session id (from boot repl) */
+ u_int16_t rmp_size; /* max no of bytes to send */
+};
+
+struct rmp_read_repl { /* read reply */
+ u_int8_t rmp_type; /* packet type (RMP_READ_REPL) */
+ u_int8_t rmp_retcode; /* return code (normally 0) */
+ u_word rmp_offset; /* byte offset (from read req) */
+ u_int16_t rmp_session; /* session id (from read req) */
+ restofpkt rmp_data; /* data (max size from read req) */
+ u_int8_t rmp_unused; /* padding to 16-bit boundary */
+};
+
+struct rmp_boot_done { /* boot complete */
+ u_int8_t rmp_type; /* packet type (RMP_BOOT_DONE) */
+ u_int8_t rmp_retcode; /* return code (0) */
+ u_word rmp_unused; /* not used (0) */
+ u_int16_t 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_rawdata
+#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..aa4be56
--- /dev/null
+++ b/libexec/rbootd/rmpproto.c
@@ -0,0 +1,583 @@
+/*
+ * 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. 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: @(#)rmpproto.c 8.1 (Berkeley) 6/4/93
+ *
+ * From: Utah Hdr: rmpproto.c 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)rmpproto.c 8.1 (Berkeley) 6/4/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <netinet/in.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(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 (ntohs(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(RMPCONN *rconn)
+{
+ struct rmp_packet *rpl;
+ char *src, *dst;
+ u_int8_t *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 = htons(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(struct rmp_packet *req, RMPCONN *rconn, char *filelist[])
+{
+ struct rmp_packet *rpl;
+ char *src, *dst;
+ u_int8_t *size;
+ int 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 = htons(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(struct rmp_packet *req, RMPCONN *rconn, char *filelist[])
+{
+ int retval;
+ char *filename, filepath[RMPBOOTDATA+1];
+ RMPCONN *oldconn;
+ struct rmp_packet *rpl;
+ char *src, *dst1, *dst2;
+ u_int8_t 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 = htons(GenSessID());
+ rpl->r_brpl.rmp_version = htons(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 = strrchr(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(RMPCONN *rconn)
+{
+ int retval = 0;
+ RMPCONN *oldconn;
+ struct rmp_packet *rpl, *req;
+ int size = 0;
+ int madeconn = 0;
+
+ /*
+ * Find the old connection. If one doesn't 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 (ntohs(req->r_rrq.rmp_session) !=
+ ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session):
+ ntohs(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 (ntohs(req->r_rrq.rmp_size) > RMPREADDATA)
+ req->r_rrq.rmp_size = htons(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, SEEK_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) ntohs(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(RMPCONN *rconn)
+{
+ RMPCONN *oldconn;
+ struct rmp_packet *rpl;
+
+ /*
+ * If we can't 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 (ntohs(rconn->rmp.r_rrq.rmp_session) !=
+ ((rpl->r_type == RMP_BOOT_REPL)? ntohs(rpl->r_brpl.rmp_session):
+ ntohs(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(RMPCONN *rconn)
+{
+ /*
+ * Set Ethernet Destination address to Source (BPF and the enet
+ * driver will take care of getting our source address set).
+ */
+ memmove((char *)&rconn->rmp.hp_hdr.daddr[0],
+ (char *)&rconn->rmp.hp_hdr.saddr[0], RMP_ADDRLEN);
+ rconn->rmp.hp_hdr.len = htons(rconn->rmplen - sizeof(struct hp_hdr));
+
+ /*
+ * Reverse 802.2/HP Extended Source & Destination Access Pts.
+ */
+ rconn->rmp.hp_llc.dxsap = htons(HPEXT_SXSAP);
+ rconn->rmp.hp_llc.sxsap = htons(HPEXT_DXSAP);
+
+ /*
+ * Last time this connection was active.
+ */
+ (void)gettimeofday(&rconn->tstamp, NULL);
+
+ 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..81ef584
--- /dev/null
+++ b/libexec/rbootd/utils.c
@@ -0,0 +1,544 @@
+/*
+ * 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. 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: @(#)utils.c 8.1 (Berkeley) 6/4/93
+ *
+ * From: Utah Hdr: utils.c 3.1 92/07/06
+ * Author: Jeff Forys, University of Utah CSS
+ */
+
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)utils.c 8.1 (Berkeley) 6/4/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <netinet/in.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(RMPCONN *rconn, int direct)
+{
+ static const char BootFmt[] = "\t\tRetCode:%u SeqNo:%x SessID:%x Vers:%u";
+ static const char ReadFmt[] = "\t\tRetCode:%u Offset:%x SessID:%x\n";
+
+ struct tm *tmp;
+ struct rmp_packet *rmp;
+ int i, omask;
+ u_int32_t 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, ntohs(rmp->hp_llc.cntrl));
+
+ /* display HP extensions to 802.2 Logical Link Control header */
+ (void) fprintf(DbgFp, "\tHP Ext: DXSAP:%x SXSAP:%x\n",
+ ntohs(rmp->hp_llc.dxsap), ntohs(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 (ntohs(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, ntohs(rmp->r_brq.rmp_session),
+ ntohs(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, ntohs(rmp->r_brpl.rmp_session),
+ ntohs(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, ntohs(rmp->r_rrq.rmp_session));
+ (void) fprintf(DbgFp, "\t\tNoOfBytes: %u\n",
+ ntohs(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, ntohs(rmp->r_rrpl.rmp_session));
+ (void) fprintf(DbgFp, "\t\tNoOfBytesSent: %zu\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,
+ ntohs(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.
+*/
+char *
+GetEtherAddr(u_int8_t *addr)
+{
+ static char Hex[] = "0123456789abcdef";
+ static char etherstr[RMP_ADDRLEN*3];
+ int i;
+ char *cp;
+
+ /*
+ * For each byte in `addr', convert it to "<hexchar><hexchar>:".
+ * The last byte does not get a trailing `:' appended.
+ */
+ i = 0;
+ cp = etherstr;
+ for(;;) {
+ *cp++ = Hex[*addr >> 4 & 0xf];
+ *cp++ = Hex[*addr++ & 0xf];
+ if (++i == RMP_ADDRLEN)
+ break;
+ *cp++ = ':';
+ }
+ *cp = '\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(u_int size, char *flnm)
+{
+ int i;
+
+ (void) fprintf(DbgFp, "\n\t\tFile Name (%u): <", 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(u_int8_t *addr)
+{
+ CLIENT *ctmp;
+
+ if ((ctmp = (CLIENT *) malloc(sizeof(CLIENT))) == NULL) {
+ syslog(LOG_ERR, "NewClient: out of memory (%s)",
+ GetEtherAddr(addr));
+ return(NULL);
+ }
+
+ memset(ctmp, 0, sizeof(CLIENT));
+ memmove(&ctmp->addr[0], addr, 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(void)
+{
+ 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(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(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.
+ */
+ memmove((char *)rtmp, (char *)rconn, 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(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(void)
+{
+ 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(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(RMPCONN *rconn)
+{
+ 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(RMPCONN *rconn)
+{
+ 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..d3b36fa
--- /dev/null
+++ b/libexec/revnetgroup/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= revnetgroup
+SRCS= revnetgroup.c hash.c parse_netgroup.c
+
+MAN= revnetgroup.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/libexec/revnetgroup/hash.c b/libexec/revnetgroup/hash.c
new file mode 100644
index 0000000..1180080
--- /dev/null
+++ b/libexec/revnetgroup/hash.c
@@ -0,0 +1,208 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#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(const void *keyarg, size_t len)
+{
+ const u_char *key;
+ size_t loop;
+ 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 hold 256 elements.
+ */
+u_int32_t
+hashkey(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(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 little 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(struct group_entry *table[], char *key, char *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 a 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(struct member_entry *table[], char *key, char *data, char *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..2485901
--- /dev/null
+++ b/libexec/revnetgroup/hash.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/* 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 group_entry *next;
+};
+
+/* Table size (chosen arbitrarily). Not too big, not too small. */
+#define TABLESIZE 256
+#define HASH_MASK 0x000000FF
+
+#define LINSIZ 1024 * 10
+
+extern void store(struct group_entry ** , char *, char *);
+extern void mstore(struct member_entry ** , char *, char *, char *);
+extern char *lookup(struct group_entry **, char *);
+extern void __endnetgrent(void);
+extern void __setnetgrent(char *);
+extern int __getnetgrent(char **, char **, char **);
diff --git a/libexec/revnetgroup/parse_netgroup.c b/libexec/revnetgroup/parse_netgroup.c
new file mode 100644
index 0000000..b891481
--- /dev/null
+++ b/libexec/revnetgroup/parse_netgroup.c
@@ -0,0 +1,360 @@
+/*
+ * 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. 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 const char rcsid[] =
+ "$FreeBSD$";
+#endif /* 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 <string.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(char *group);
+static struct linelist *read_for_group(char *group);
+extern struct group_entry *gtable[];
+
+/*
+ * 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(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(char **hostp, char **userp, char **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(void)
+{
+ struct linelist *lp, *olp;
+ 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(char *group)
+{
+ char *spos, *epos;
+ int len, strpos;
+#ifdef DEBUG
+ 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 superfluous since the
+ * code handles the error condition successfully, and
+ * spewing it out from inside libc can actually hose
+ * certain programs.
+ */
+ warnx("cycle in netgroup %s", 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)
+ warnx("bad entry (%s%s%s%s%s) in netgroup \"%s\"",
+ 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(char *group)
+{
+ char *pos, *spos, *linep = NULL, *olinep = NULL;
+ int len, olen;
+ int cont;
+ struct linelist *lp;
+ char line[LINSIZ + 1];
+ char *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..a17d966
--- /dev/null
+++ b/libexec/revnetgroup/revnetgroup.8
@@ -0,0 +1,159 @@
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 24, 1995
+.Dt REVNETGROUP 8
+.Os
+.Sh NAME
+.Nm revnetgroup
+.Nd "generate reverse netgroup data"
+.Sh SYNOPSIS
+.Nm
+.Fl u | h
+.Op Fl f Ar netgroup_file
+.Sh DESCRIPTION
+The
+.Nm
+utility 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
+.Pa netgroup.byuser
+and
+.Pa netgroup.byhost
+.Tn 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
+.Pa /etc/netgroup
+file may list a netgroup and a list of its members.
+Here, the
+netgroup is considered the
+.Em key
+and the member names are the
+.Em data .
+By contrast, the reverse
+.Pa netgroup.byuser
+database lists each unique
+member as the key and the netgroups to which the members belong become
+the data.
+Separate 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
+.Tn 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
+utility prints its results on the standard output.
+It is usually called
+only by
+.Pa /var/yp/Makefile
+when rebuilding the
+.Tn NIS
+netgroup maps.
+.Sh OPTIONS
+The
+.Nm
+utility supports the following options:
+.Bl -tag -width indent
+.It Fl u
+Generate
+.Pa netgroup.byuser
+output; only username information in the
+original netgroup file is processed.
+.It Fl h
+Generate
+.Pa 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
+utility uses
+.Pa /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
+to read from the standard input.
+.El
+.Sh FILES
+.Bl -tag -width /var/yp/Makefile -compact
+.It Pa /var/yp/Makefile
+the Makefile that calls
+.Nm yp_mkdb
+and
+.Nm
+to build the
+.Tn NIS
+databases
+.It Pa /etc/netgroup
+the default netgroup database file.
+This file is most often found
+only on the
+.Tn NIS
+master server
+.El
+.Sh SEE ALSO
+.Xr getnetgrent 3 ,
+.Xr netgroup 5 ,
+.Xr yp 8 ,
+.Xr yp_mkdb 8
+.Sh AUTHORS
+.An Bill Paul Aq Mt wpaul@ctr.columbia.edu
diff --git a/libexec/revnetgroup/revnetgroup.c b/libexec/revnetgroup/revnetgroup.c
new file mode 100644
index 0000000..505c482
--- /dev/null
+++ b/libexec/revnetgroup/revnetgroup.c
@@ -0,0 +1,179 @@
+/*
+ * 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
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "hash.h"
+
+/* 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];
+
+static void
+usage(void)
+{
+ fprintf (stderr,"usage: revnetgroup -u | -h [-f netgroup_file]\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fp;
+ char readbuf[LINSIZ];
+ struct group_entry *gcur;
+ struct member_entry *mcur;
+ char *host, *user, *domain;
+ int ch;
+ char *key = NULL, *data = NULL;
+ int hosts = -1, i;
+
+ if (argc < 2)
+ usage();
+
+ while ((ch = getopt(argc, argv, "uhf:")) != -1) {
+ switch(ch) {
+ case 'u':
+ if (hosts != -1) {
+ warnx("please use only one of -u or -h");
+ usage();
+ }
+ hosts = 0;
+ break;
+ case 'h':
+ if (hosts != -1) {
+ warnx("please use only one of -u or -h");
+ usage();
+ }
+ hosts = 1;
+ break;
+ case 'f':
+ netgroup = optarg;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (hosts == -1)
+ usage();
+
+ if (strcmp(netgroup, "-")) {
+ if ((fp = fopen(netgroup, "r")) == NULL) {
+ err(1, "%s", netgroup);
+ }
+ } else {
+ fp = stdin;
+ }
+
+ /* Stuff all the netgroup names and members into a hash table. */
+ while (fgets(readbuf, LINSIZ, fp)) {
+ if (readbuf[0] == '#')
+ continue;
+ /* handle backslash line continuations */
+ while(readbuf[strlen(readbuf) - 2] == '\\') {
+ fgets((char *)&readbuf[strlen(readbuf) - 2],
+ sizeof(readbuf) - strlen(readbuf), fp);
+ }
+ data = NULL;
+ 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) != 0) {
+ 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/rlogind/Makefile b/libexec/rlogind/Makefile
new file mode 100644
index 0000000..de0fb20
--- /dev/null
+++ b/libexec/rlogind/Makefile
@@ -0,0 +1,16 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+# $FreeBSD$
+
+.include <src.opts.mk>
+
+PROG= rlogind
+MAN= rlogind.8
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+WARNS?= 2
+
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+= -DINET6
+.endif
+
+.include <bsd.prog.mk>
diff --git a/libexec/rlogind/rlogind.8 b/libexec/rlogind/rlogind.8
new file mode 100644
index 0000000..209cec9
--- /dev/null
+++ b/libexec/rlogind/rlogind.8
@@ -0,0 +1,193 @@
+.\" 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. 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
+.\" $FreeBSD$
+.\"
+.Dd February 9, 2005
+.Dt RLOGIND 8
+.Os
+.Sh NAME
+.Nm rlogind
+.Nd remote login server
+.Sh SYNOPSIS
+.Nm
+.Op Fl Daln
+.Sh DESCRIPTION
+The
+.Nm
+utility 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 :
+.Bl -tag -width indent
+.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
+The
+.Nm
+utility listens for service requests at the port indicated in
+the
+.Dq 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
+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 1
+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,
+.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 FILES
+.Bl -tag -width /etc/hostsxxxxxxxx -compact
+.It Pa /etc/hosts
+.It Pa /etc/hosts.equiv
+.It Ev $HOME Ns Pa /.rhosts
+.It Pa /var/run/nologin
+.El
+.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 1
+is invoked, a null byte is returned as in indication of success.
+.Bl -tag -width Ds
+.It Sy Try again.
+A
+.Xr fork 2
+by the server failed.
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr ruserok 3 ,
+.Xr hosts 5 ,
+.Xr hosts.equiv 5 ,
+.Xr login.conf 5 ,
+.Xr nologin 5 ,
+.Xr services 5 ,
+.Xr rshd 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
+.Pp
+IPv6 support was added by WIDE/KAME project.
+.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
+.Dq open
+environment.
+.Pp
+A facility to allow all data exchanges to be encrypted should be
+present.
+.Pp
+A more extensible protocol should be used.
diff --git a/libexec/rlogind/rlogind.c b/libexec/rlogind/rlogind.c
new file mode 100644
index 0000000..d64d7bf
--- /dev/null
+++ b/libexec/rlogind/rlogind.c
@@ -0,0 +1,585 @@
+/*-
+ * Copyright (c) 1983, 1988, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed for the FreeBSD Project by
+ * ThinkSec AS and NAI Labs, the Security Research Division of Network
+ * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
+ *
+ * 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. 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 0
+#ifndef lint
+static const 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 const char sccsid[] = "@(#)rlogind.c 8.1 (Berkeley) 6/4/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * 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/types.h>
+#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 <errno.h>
+#include <libutil.h>
+#include <paths.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+#ifndef TIOCPKT_WINDOW
+#define TIOCPKT_WINDOW 0x80
+#endif
+
+#define ARGSTR "Daln"
+
+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;
+
+union sockunion {
+ struct sockinet {
+ u_char si_len;
+ u_char si_family;
+ u_short si_port;
+ } su_si;
+ struct sockaddr_in su_sin;
+ struct sockaddr_in6 su_sin6;
+};
+#define su_len su_si.si_len
+#define su_family su_si.si_family
+#define su_port su_si.si_port
+
+void doit(int, union sockunion *);
+int control(int, char *, int);
+void protocol(int, int);
+void cleanup(int);
+void fatal(int, char *, int);
+int do_rlogin(union sockunion *);
+void getstr(char *, int, char *);
+void setup_term(int);
+int do_krb_login(struct sockaddr_in *);
+void usage(void);
+
+
+int
+main(int argc, char *argv[])
+{
+ extern int __check_rhosts_file;
+ union sockunion from;
+ socklen_t fromlen;
+ int ch, on;
+
+ openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH);
+
+ opterr = 0;
+ while ((ch = getopt(argc, argv, ARGSTR)) != -1)
+ 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;
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+ 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");
+ if (from.su_family == AF_INET)
+ {
+ 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);
+ return 0;
+}
+
+int child;
+int netf;
+char line[MAXPATHLEN];
+int confirmed;
+
+struct winsize win = { 0, 0, 0, 0 };
+
+
+void
+doit(int f, union sockunion *fromp)
+{
+ int master, pid, on = 1;
+ int authenticated = 0;
+ char hostname[2 * MAXHOSTNAMELEN + 1];
+ char nameinfo[2 * INET6_ADDRSTRLEN + 1];
+ char c;
+
+ alarm(60);
+ read(f, &c, 1);
+
+ if (c != 0)
+ exit(1);
+
+ alarm(0);
+
+ realhostname_sa(hostname, sizeof(hostname) - 1,
+ (struct sockaddr *)fromp, fromp->su_len);
+ /* error check ? */
+ fromp->su_port = ntohs((u_short)fromp->su_port);
+ hostname[sizeof(hostname) - 1] = '\0';
+
+ {
+ if ((fromp->su_family != AF_INET
+#ifdef INET6
+ && fromp->su_family != AF_INET6
+#endif
+ ) ||
+ fromp->su_port >= IPPORT_RESERVED ||
+ fromp->su_port < IPPORT_RESERVED/2) {
+ getnameinfo((struct sockaddr *)fromp,
+ fromp->su_len,
+ nameinfo, sizeof(nameinfo), NULL, 0,
+ NI_NUMERICHOST);
+ /* error check ? */
+ syslog(LOG_NOTICE, "Connection from %s on illegal port",
+ nameinfo);
+ fatal(f, "Permission denied", 0);
+ }
+#ifdef IP_OPTIONS
+ if (fromp->su_family == AF_INET)
+ {
+ u_char optbuf[BUFSIZ/3];
+ socklen_t optsize = sizeof(optbuf);
+ int ipproto, i;
+ 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) {
+ for (i = 0; i < optsize; ) {
+ u_char c = optbuf[i];
+ if (c == IPOPT_LSRR || c == IPOPT_SSRR) {
+ syslog(LOG_NOTICE,
+ "Connection refused from %s with IP option %s",
+ inet_ntoa(fromp->su_sin.sin_addr),
+ c == IPOPT_LSRR ? "LSRR" : "SSRR");
+ exit(1);
+ }
+ if (c == IPOPT_EOL)
+ break;
+ i += (c == IPOPT_NOP) ? 1 : optbuf[i+1];
+ }
+ }
+ }
+#endif
+ if (do_rlogin(fromp) == 0)
+ authenticated++;
+ }
+ if (confirmed == 0) {
+ write(f, "", 1);
+ confirmed = 1; /* we sent the null! */
+ }
+ 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) {
+ 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*/
+ }
+ 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(int pty, char *cp, int n)
+{
+ struct winsize w;
+
+ if (n < 4 + (int)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(int f, int p)
+{
+ char pibuf[1024+1], fibuf[1024], *pbp = NULL, *fbp = NULL;
+ int 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)) {
+ fcc = read(f, fibuf, sizeof(fibuf));
+ if (fcc < 0 && errno == EWOULDBLOCK)
+ fcc = 0;
+ else {
+ 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--;
+ 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) {
+ 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(int signo __unused)
+{
+
+ shutdown(netf, SHUT_RDWR);
+ exit(1);
+}
+
+void
+fatal(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 = snprintf(bp, sizeof(buf), "rlogind: %s: %s.\r\n",
+ msg, strerror(errno));
+ else
+ len = snprintf(bp, sizeof(buf), "rlogind: %s.\r\n", msg);
+ if (len < 0)
+ len = 0;
+ (void) write(f, buf, bp + len - buf);
+ exit(1);
+}
+
+int
+do_rlogin(union sockunion *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_sa(dest, dest->su_len, pwd->pw_uid == 0, rusername,
+ lusername));
+}
+
+void
+getstr(char *buf, int cnt, char *errmsg)
+{
+ char c;
+
+ do {
+ if (read(STDIN_FILENO, &c, 1) != 1)
+ exit(1);
+ if (--cnt < 0)
+ fatal(STDOUT_FILENO, errmsg, 0);
+ *buf++ = c;
+ } while (c != 0);
+}
+
+extern char **environ;
+
+void
+setup_term(int fd)
+{
+ char *cp;
+ char *speed;
+ struct termios tt, def;
+
+ cp = strchr(term + ENVSIZE, '/');
+#ifndef notyet
+ tcgetattr(fd, &tt);
+ if (cp) {
+ *cp++ = '\0';
+ speed = cp;
+ cp = strchr(speed, '/');
+ if (cp)
+ *cp++ = '\0';
+ cfsetspeed(&tt, atoi(speed));
+ }
+
+ cfmakesane(&def);
+ tt.c_iflag = def.c_iflag;
+ tt.c_oflag = def.c_oflag;
+ tt.c_lflag = def.c_lflag;
+ tcsetattr(fd, TCSAFLUSH, &tt);
+#else
+ if (cp) {
+ *cp++ = '\0';
+ speed = cp;
+ cp = strchr(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;
+}
+
+void
+usage(void)
+{
+ syslog(LOG_ERR, "usage: rlogind [-" ARGSTR "]");
+}
diff --git a/libexec/rpc.rquotad/Makefile b/libexec/rpc.rquotad/Makefile
new file mode 100644
index 0000000..95d4415
--- /dev/null
+++ b/libexec/rpc.rquotad/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG = rpc.rquotad
+SRCS = rquotad.c
+MAN = rpc.rquotad.8
+
+DPADD= ${LIBRPCSVC} ${LIBUTIL}
+LDADD= -lrpcsvc -lutil
+
+.include <bsd.prog.mk>
diff --git a/libexec/rpc.rquotad/rpc.rquotad.8 b/libexec/rpc.rquotad/rpc.rquotad.8
new file mode 100644
index 0000000..4e14da0
--- /dev/null
+++ b/libexec/rpc.rquotad/rpc.rquotad.8
@@ -0,0 +1,67 @@
+.\"
+.\" Copyright (c) 1994 Theo de Raadt
+.\" 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 Theo de Raadt.
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 22, 1994
+.Dt RPC.RQUOTAD 8
+.Os
+.Sh NAME
+.Nm rpc.rquotad
+.Nd remote quota server
+.Sh SYNOPSIS
+.Nm /usr/libexec/rpc.rquotad
+.Sh DESCRIPTION
+The
+.Nm
+utility is a
+.Xr rpc 3
+server which returns quotas for a user of a local file system
+which is NFS-mounted onto a remote machine.
+The
+.Xr quota 1
+utility uses the results to display user quotas for remote file systems.
+The
+.Nm
+utility is normally invoked by
+.Xr inetd 8 .
+.Pp
+The
+.Nm
+utility uses an
+.Tn RPC
+protocol defined in
+.Pa /usr/include/rpcsvc/rquota.x .
+.Sh SEE ALSO
+.Xr quota 1
+.Sh BUGS
+.Bx 4.4
+and
+.Fx
+support group quotas but the rquota protocol does not.
diff --git a/libexec/rpc.rquotad/rquotad.c b/libexec/rpc.rquotad/rquotad.c
new file mode 100644
index 0000000..d9ce06a
--- /dev/null
+++ b/libexec/rpc.rquotad/rquotad.c
@@ -0,0 +1,222 @@
+/*
+ * by Manuel Bouyer (bouyer@ensta.fr)
+ *
+ * There is no copyright, you can use it as you want.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <ufs/ufs/quota.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/rquota.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fstab.h>
+#include <grp.h>
+#include <libutil.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+static void rquota_service(struct svc_req *request, SVCXPRT *transp);
+static void sendquota(struct svc_req *request, SVCXPRT *transp);
+static void initfs(void);
+static int getfsquota(long id, char *path, struct dqblk *dqblk);
+
+static struct quotafile **qfa; /* array of qfs */
+static int nqf, szqf; /* number of qfs and size of array */
+static int from_inetd = 1;
+
+static void
+cleanup(int sig)
+{
+
+ (void)sig;
+ (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
+ exit(0);
+}
+
+int
+main(void)
+{
+ SVCXPRT *transp;
+ int ok;
+ struct sockaddr_storage from;
+ socklen_t fromlen;
+
+ fromlen = sizeof(from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0)
+ from_inetd = 0;
+
+ if (!from_inetd) {
+ daemon(0, 0);
+ (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
+ (void)signal(SIGINT, cleanup);
+ (void)signal(SIGTERM, cleanup);
+ (void)signal(SIGHUP, cleanup);
+ }
+
+ openlog("rpc.rquotad", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ /* create and register the service */
+ if (from_inetd) {
+ transp = svc_tli_create(0, NULL, NULL, 0, 0);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "couldn't create udp service.");
+ exit(1);
+ }
+ ok = svc_reg(transp, RQUOTAPROG, RQUOTAVERS,
+ rquota_service, NULL);
+ } else {
+ ok = svc_create(rquota_service,
+ RQUOTAPROG, RQUOTAVERS, "udp");
+ }
+ if (!ok) {
+ syslog(LOG_ERR,
+ "unable to register (RQUOTAPROG, RQUOTAVERS, %s)",
+ from_inetd ? "(inetd)" : "udp");
+ exit(1);
+ }
+
+ initfs();
+ svc_run();
+ syslog(LOG_ERR, "svc_run returned");
+ exit(1);
+}
+
+static void
+rquota_service(struct svc_req *request, SVCXPRT *transp)
+{
+
+ switch (request->rq_proc) {
+ case NULLPROC:
+ (void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
+ break;
+ case RQUOTAPROC_GETQUOTA:
+ case RQUOTAPROC_GETACTIVEQUOTA:
+ sendquota(request, transp);
+ break;
+ default:
+ svcerr_noproc(transp);
+ break;
+ }
+ if (from_inetd)
+ exit(0);
+}
+
+/* read quota for the specified id, and send it */
+static void
+sendquota(struct svc_req *request, SVCXPRT *transp)
+{
+ struct getquota_args getq_args;
+ struct getquota_rslt getq_rslt;
+ struct dqblk dqblk;
+ struct timeval timev;
+ int scale;
+
+ bzero(&getq_args, sizeof(getq_args));
+ if (!svc_getargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
+ svcerr_decode(transp);
+ return;
+ }
+ if (request->rq_cred.oa_flavor != AUTH_UNIX) {
+ /* bad auth */
+ getq_rslt.status = Q_EPERM;
+ } else if (!getfsquota(getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) {
+ /* failed, return noquota */
+ getq_rslt.status = Q_NOQUOTA;
+ } else {
+ gettimeofday(&timev, NULL);
+ getq_rslt.status = Q_OK;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE;
+ scale = 1 << flsll(dqblk.dqb_bhardlimit >> 32);
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize =
+ DEV_BSIZE * scale;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit =
+ dqblk.dqb_bhardlimit / scale;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit =
+ dqblk.dqb_bsoftlimit / scale;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks =
+ dqblk.dqb_curblocks / scale;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit =
+ dqblk.dqb_ihardlimit;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit =
+ dqblk.dqb_isoftlimit;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles =
+ dqblk.dqb_curinodes;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft =
+ dqblk.dqb_btime - timev.tv_sec;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft =
+ dqblk.dqb_itime - timev.tv_sec;
+ }
+ if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt))
+ svcerr_systemerr(transp);
+ if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
+ syslog(LOG_ERR, "unable to free arguments");
+ exit(1);
+ }
+}
+
+static void
+initfs(void)
+{
+ struct fstab *fs;
+
+ setfsent();
+ szqf = 8;
+ if ((qfa = malloc(szqf * sizeof *qfa)) == NULL)
+ goto enomem;
+ while ((fs = getfsent())) {
+ if (strcmp(fs->fs_vfstype, "ufs"))
+ continue;
+ if (nqf >= szqf) {
+ szqf *= 2;
+ if ((qfa = reallocf(qfa, szqf * sizeof *qfa)) == NULL)
+ goto enomem;
+ }
+ if ((qfa[nqf] = quota_open(fs, USRQUOTA, O_RDONLY)) == NULL) {
+ if (errno != EOPNOTSUPP)
+ goto fserr;
+ continue;
+ }
+ ++nqf;
+ /* XXX */
+ }
+ endfsent();
+ return;
+enomem:
+ syslog(LOG_ERR, "out of memory");
+ exit(1);
+fserr:
+ syslog(LOG_ERR, "%s: %s", fs->fs_file, strerror(errno));
+ exit(1);
+}
+
+/*
+ * gets the quotas for id, filesystem path.
+ * Return 0 if fail, 1 otherwise
+ */
+static int
+getfsquota(long id, char *path, struct dqblk *dqblk)
+{
+ int i;
+
+ for (i = 0; i < nqf; ++i)
+ if (quota_check_path(qfa[i], path) == 1)
+ return (quota_read(qfa[i], dqblk, id) == 0);
+ return (0);
+}
diff --git a/libexec/rpc.rstatd/Makefile b/libexec/rpc.rstatd/Makefile
new file mode 100644
index 0000000..a42225f
--- /dev/null
+++ b/libexec/rpc.rstatd/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG = rpc.rstatd
+SRCS = rstatd.c rstat_proc.c
+MAN = rpc.rstatd.8
+
+DPADD= ${LIBRPCSVC} ${LIBUTIL} ${LIBDEVSTAT} ${LIBKVM}
+LDADD= -lrpcsvc -lutil -ldevstat -lkvm
+
+WARNS?= 1
+
+.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..87512ca
--- /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. 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 7, 1993
+.Dt RPC.RSTATD 8
+.Os
+.Sh NAME
+.Nm rpc.rstatd
+.Nd kernel statistics server
+.Sh SYNOPSIS
+.Nm /usr/libexec/rpc.rstatd
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is a server which returns performance statistics obtained from the kernel.
+These statistics are read using the
+.Xr rup 1
+command.
+The
+.Nm
+daemon is normally invoked by
+.Xr inetd 8 .
+.Pp
+The
+.Nm
+utility uses an
+.Tn 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..1b66c64
--- /dev/null
+++ b/libexec/rpc.rstatd/rstat_proc.c
@@ -0,0 +1,477 @@
+/*
+ * 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
+#if 0
+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";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+/*
+ * rstat service: built with rstat.x and derived from rpc.rstatd.c
+ *
+ * Copyright (c) 1984 by Sun Microsystems, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/vmmeter.h>
+#include <sys/param.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <devstat.h>
+
+#include <net/if.h>
+#include <net/if_mib.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>
+
+int haveadisk(void);
+void updatexfers(int, int *);
+int stats_service(void);
+
+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 int stat_is_init = 0;
+
+static int cp_time_xlat[RSTAT_CPUSTATES] = { CP_USER, CP_NICE, CP_SYS,
+ CP_IDLE };
+static long bsd_cp_time[CPUSTATES];
+
+
+#ifndef FSCALE
+#define FSCALE (1 << 8)
+#endif
+
+void
+stat_init(void)
+{
+ stat_is_init = 1;
+ alarm(0);
+ updatestat();
+ (void) signal(SIGALRM, updatestat);
+ alarm(1);
+}
+
+statstime *
+rstatproc_stats_3_svc(void *argp, struct svc_req *rqstp)
+{
+ if (! stat_is_init)
+ stat_init();
+ sincelastreq = 0;
+ return(&stats_all.s3);
+}
+
+statsswtch *
+rstatproc_stats_2_svc(void *argp, struct svc_req *rqstp)
+{
+ if (! stat_is_init)
+ stat_init();
+ sincelastreq = 0;
+ return(&stats_all.s2);
+}
+
+stats *
+rstatproc_stats_1_svc(void *argp, struct svc_req *rqstp)
+{
+ if (! stat_is_init)
+ stat_init();
+ sincelastreq = 0;
+ return(&stats_all.s1);
+}
+
+u_int *
+rstatproc_havedisk_3_svc(void *argp, struct svc_req *rqstp)
+{
+ static u_int have;
+
+ if (! stat_is_init)
+ stat_init();
+ sincelastreq = 0;
+ have = haveadisk();
+ return(&have);
+}
+
+u_int *
+rstatproc_havedisk_2_svc(void *argp, struct svc_req *rqstp)
+{
+ return(rstatproc_havedisk_3_svc(argp, rqstp));
+}
+
+u_int *
+rstatproc_havedisk_1_svc(void *argp, struct svc_req *rqstp)
+{
+ return(rstatproc_havedisk_3_svc(argp, rqstp));
+}
+
+void
+updatestat(void)
+{
+ int i, hz;
+ struct clockinfo clockrate;
+ struct vmmeter cnt;
+ struct ifmibdata ifmd;
+ double avrun[3];
+ struct timeval tm, btm;
+ int mib[6];
+ size_t len;
+ int ifcount;
+
+#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++;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_CLOCKRATE;
+ len = sizeof clockrate;
+ if (sysctl(mib, 2, &clockrate, &len, 0, 0) < 0) {
+ syslog(LOG_ERR, "sysctl(kern.clockrate): %m");
+ exit(1);
+ }
+ hz = clockrate.hz;
+
+ len = sizeof(bsd_cp_time);
+ if (sysctlbyname("kern.cp_time", bsd_cp_time, &len, 0, 0) < 0) {
+ syslog(LOG_ERR, "sysctl(kern.cp_time): %m");
+ exit(1);
+ }
+ for(i = 0; i < RSTAT_CPUSTATES ; i++)
+ stats_all.s1.cp_time[i] = bsd_cp_time[cp_time_xlat[i]];
+
+ (void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0]));
+
+ stats_all.s2.avenrun[0] = avrun[0] * FSCALE;
+ stats_all.s2.avenrun[1] = avrun[1] * FSCALE;
+ stats_all.s2.avenrun[2] = avrun[2] * FSCALE;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_BOOTTIME;
+ len = sizeof btm;
+ if (sysctl(mib, 2, &btm, &len, 0, 0) < 0) {
+ syslog(LOG_ERR, "sysctl(kern.boottime): %m");
+ 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
+
+#define FETCH_CNT(stat, cnt) do { \
+ len = sizeof((stat)); \
+ if (sysctlbyname("vm.stats." #cnt , &(stat), &len, 0, 0) < 0) { \
+ syslog(LOG_ERR, "sysctl(vm.stats." #cnt "): %m"); \
+ exit(1); \
+ } \
+} while (0)
+
+ FETCH_CNT(stats_all.s1.v_pgpgin, vm.v_vnodepgsin);
+ FETCH_CNT(stats_all.s1.v_pgpgout, vm.v_vnodepgsout);
+ FETCH_CNT(stats_all.s1.v_pswpin, vm.v_swappgsin);
+ FETCH_CNT(stats_all.s1.v_pswpout, vm.v_swappgsout);
+ FETCH_CNT(stats_all.s1.v_intr, sys.v_intr);
+ FETCH_CNT(stats_all.s2.v_swtch, sys.v_swtch);
+ (void)gettimeofday(&tm, NULL);
+ stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) +
+ hz*(tm.tv_usec - btm.tv_usec)/1000000;
+
+ /* update disk transfers */
+ updatexfers(RSTAT_DK_NDRIVE, stats_all.s1.dk_xfer);
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_LINK;
+ mib[2] = NETLINK_GENERIC;
+ mib[3] = IFMIB_SYSTEM;
+ mib[4] = IFMIB_IFCOUNT;
+ len = sizeof ifcount;
+ if (sysctl(mib, 5, &ifcount, &len, 0, 0) < 0) {
+ syslog(LOG_ERR, "sysctl(net.link.generic.system.ifcount): %m");
+ 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 (i = 1; i <= ifcount; i++) {
+ len = sizeof ifmd;
+ mib[3] = IFMIB_IFDATA;
+ mib[4] = i;
+ mib[5] = IFDATA_GENERAL;
+ if (sysctl(mib, 6, &ifmd, &len, 0, 0) < 0) {
+ if (errno == ENOENT)
+ continue;
+
+ syslog(LOG_ERR, "sysctl(net.link.ifdata.%d.general)"
+ ": %m", i);
+ exit(1);
+ }
+
+ stats_all.s1.if_ipackets += ifmd.ifmd_data.ifi_ipackets;
+ stats_all.s1.if_opackets += ifmd.ifmd_data.ifi_opackets;
+ stats_all.s1.if_ierrors += ifmd.ifmd_data.ifi_ierrors;
+ stats_all.s1.if_oerrors += ifmd.ifmd_data.ifi_oerrors;
+ stats_all.s1.if_collisions += ifmd.ifmd_data.ifi_collisions;
+ }
+ (void)gettimeofday(&tm, NULL);
+ stats_all.s3.curtime.tv_sec = tm.tv_sec;
+ stats_all.s3.curtime.tv_usec = tm.tv_usec;
+ alarm(1);
+}
+
+/*
+ * returns true if have a disk
+ */
+int
+haveadisk(void)
+{
+ register int i;
+ struct statinfo stats;
+ int num_devices, retval = 0;
+
+ if ((num_devices = devstat_getnumdevs(NULL)) < 0) {
+ syslog(LOG_ERR, "rstatd: can't get number of devices: %s",
+ devstat_errbuf);
+ exit(1);
+ }
+
+ if (devstat_checkversion(NULL) < 0) {
+ syslog(LOG_ERR, "rstatd: %s", devstat_errbuf);
+ exit(1);
+ }
+
+ stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
+ bzero(stats.dinfo, sizeof(struct devinfo));
+
+ if (devstat_getdevs(NULL, &stats) == -1) {
+ syslog(LOG_ERR, "rstatd: can't get device list: %s",
+ devstat_errbuf);
+ exit(1);
+ }
+ for (i = 0; i < stats.dinfo->numdevs; i++) {
+ if (((stats.dinfo->devices[i].device_type
+ & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT)
+ && ((stats.dinfo->devices[i].device_type
+ & DEVSTAT_TYPE_PASS) == 0)) {
+ retval = 1;
+ break;
+ }
+ }
+
+ if (stats.dinfo->mem_ptr)
+ free(stats.dinfo->mem_ptr);
+
+ free(stats.dinfo);
+ return(retval);
+}
+
+void
+updatexfers(int numdevs, int *devs)
+{
+ register int i, j, k, t;
+ struct statinfo stats;
+ int num_devices = 0;
+ u_int64_t total_transfers;
+
+ if ((num_devices = devstat_getnumdevs(NULL)) < 0) {
+ syslog(LOG_ERR, "rstatd: can't get number of devices: %s",
+ devstat_errbuf);
+ exit(1);
+ }
+
+ if (devstat_checkversion(NULL) < 0) {
+ syslog(LOG_ERR, "rstatd: %s", devstat_errbuf);
+ exit(1);
+ }
+
+ stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
+ bzero(stats.dinfo, sizeof(struct devinfo));
+
+ if (devstat_getdevs(NULL, &stats) == -1) {
+ syslog(LOG_ERR, "rstatd: can't get device list: %s",
+ devstat_errbuf);
+ exit(1);
+ }
+
+ for (i = 0, j = 0; i < stats.dinfo->numdevs && j < numdevs; i++) {
+ if (((stats.dinfo->devices[i].device_type
+ & DEVSTAT_TYPE_MASK) == DEVSTAT_TYPE_DIRECT)
+ && ((stats.dinfo->devices[i].device_type
+ & DEVSTAT_TYPE_PASS) == 0)) {
+ total_transfers = 0;
+ for (k = 0; k < DEVSTAT_N_TRANS_FLAGS; k++)
+ total_transfers +=
+ stats.dinfo->devices[i].operations[k];
+ /*
+ * XXX KDM If the total transfers for this device
+ * are greater than the amount we can fit in a
+ * signed integer, just set them to the maximum
+ * amount we can fit in a signed integer. I have a
+ * feeling that the rstat protocol assumes 32-bit
+ * integers, so this could well break on a 64-bit
+ * architecture like the Alpha.
+ */
+ if (total_transfers > INT_MAX)
+ t = INT_MAX;
+ else
+ t = total_transfers;
+ devs[j] = t;
+ j++;
+ }
+ }
+
+ if (stats.dinfo->mem_ptr)
+ free(stats.dinfo->mem_ptr);
+
+ free(stats.dinfo);
+}
+
+void
+rstat_service(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, (xdrproc_t)xdr_void, 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_svc;
+ break;
+ case RSTATVERS_SWTCH:
+ local = (char *(*)()) rstatproc_stats_2_svc;
+ break;
+ case RSTATVERS_TIME:
+ local = (char *(*)()) rstatproc_stats_3_svc;
+ 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_svc;
+ break;
+ case RSTATVERS_SWTCH:
+ local = (char *(*)()) rstatproc_havedisk_2_svc;
+ break;
+ case RSTATVERS_TIME:
+ local = (char *(*)()) rstatproc_havedisk_3_svc;
+ 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, (xdrproc_t)xdr_argument, &argument)) {
+ svcerr_decode(transp);
+ goto leave;
+ }
+ result = (*local)(&argument, rqstp);
+ if (result != NULL &&
+ !svc_sendreply(transp, (xdrproc_t)xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, &argument))
+ errx(1, "unable to free arguments");
+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..bb74eb8
--- /dev/null
+++ b/libexec/rpc.rstatd/rstatd.c
@@ -0,0 +1,129 @@
+/*-
+ * 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. 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 const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <rpc/rpc.h>
+#include <signal.h>
+#include <syslog.h>
+#include <rpcsvc/rstat.h>
+
+extern void rstat_service(struct svc_req *, SVCXPRT *);
+
+int from_inetd = 1; /* started from inetd ? */
+int closedown = 20; /* how long to wait before going dormant */
+
+void
+cleanup(int sig __unused)
+{
+ (void) rpcb_unset(RSTATPROG, RSTATVERS_TIME, NULL);
+ (void) rpcb_unset(RSTATPROG, RSTATVERS_SWTCH, NULL);
+ (void) rpcb_unset(RSTATPROG, RSTATVERS_ORIG, NULL);
+ exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ SVCXPRT *transp;
+ int ok;
+ struct sockaddr_storage from;
+ socklen_t 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;
+ }
+
+ if (!from_inetd) {
+ daemon(0, 0);
+
+ (void)rpcb_unset(RSTATPROG, RSTATVERS_TIME, NULL);
+ (void)rpcb_unset(RSTATPROG, RSTATVERS_SWTCH, NULL);
+ (void)rpcb_unset(RSTATPROG, RSTATVERS_ORIG, NULL);
+
+ (void) signal(SIGINT, cleanup);
+ (void) signal(SIGTERM, cleanup);
+ (void) signal(SIGHUP, cleanup);
+ }
+
+ openlog("rpc.rstatd", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ if (from_inetd) {
+ transp = svc_tli_create(0, NULL, NULL, 0, 0);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "cannot create udp service.");
+ exit(1);
+ }
+ ok = svc_reg(transp, RSTATPROG, RSTATVERS_TIME,
+ rstat_service, NULL);
+ } else
+ ok = svc_create(rstat_service,
+ RSTATPROG, RSTATVERS_TIME, "udp");
+ if (!ok) {
+ syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_TIME, %s)", (!from_inetd)?"udp":"(inetd)");
+ exit(1);
+ }
+ if (from_inetd)
+ ok = svc_reg(transp, RSTATPROG, RSTATVERS_SWTCH,
+ rstat_service, NULL);
+ else
+ ok = svc_create(rstat_service,
+ RSTATPROG, RSTATVERS_SWTCH, "udp");
+ if (!ok) {
+ syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_SWTCH, %s)", (!from_inetd)?"udp":"(inetd)");
+ exit(1);
+ }
+ if (from_inetd)
+ ok = svc_reg(transp, RSTATPROG, RSTATVERS_ORIG,
+ rstat_service, NULL);
+ else
+ ok = svc_create(rstat_service,
+ RSTATPROG, RSTATVERS_ORIG, "udp");
+ if (!ok) {
+ syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_ORIG, %s)", (!from_inetd)?"udp":"(inetd)");
+ 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..df2be37
--- /dev/null
+++ b/libexec/rpc.rusersd/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+PROG = rpc.rusersd
+SRCS = rusersd.c rusers_proc.c extern.h
+MAN = rpc.rusersd.8
+
+DPADD= ${LIBRPCSVC} ${LIBUTIL}
+LDADD= -lrpcsvc -lutil
+
+#.if exists(/usr/X11R6/include/X11/extensions/xidle.h)
+#CFLAGS+= -DXIDLE
+#LDADD+= -L/usr/X11R6/lib -lXext -lX11
+#.endif
+
+.include <bsd.prog.mk>
diff --git a/libexec/rpc.rusersd/extern.h b/libexec/rpc.rusersd/extern.h
new file mode 100644
index 0000000..ac91526
--- /dev/null
+++ b/libexec/rpc.rusersd/extern.h
@@ -0,0 +1,34 @@
+/*-
+ * 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. 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.
+ *
+ * $FreeBSD$
+ */
+
+extern int from_inetd;
+
+void rusers_service(struct svc_req *, SVCXPRT *);
diff --git a/libexec/rpc.rusersd/rpc.rusersd.8 b/libexec/rpc.rusersd/rpc.rusersd.8
new file mode 100644
index 0000000..f9f1a1d
--- /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. 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 7, 1993
+.Dt RPC.RUSERSD 8
+.Os
+.Sh NAME
+.Nm rpc.rusersd
+.Nd logged in users server
+.Sh SYNOPSIS
+.Nm /usr/libexec/rpc.rusersd
+.Sh DESCRIPTION
+The
+.Nm
+utility 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
+daemon is normally invoked by
+.Xr inetd 8 .
+.Pp
+The
+.Nm
+utility uses an
+.Tn RPC
+protocol defined in
+.Pa /usr/include/rpcsvc/rnusers.x .
+.Sh SEE ALSO
+.Xr rusers 1 ,
+.Xr w 1 ,
+.Xr who 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..51e9a4d
--- /dev/null
+++ b/libexec/rpc.rusersd/rusers_proc.c
@@ -0,0 +1,332 @@
+/*-
+ * 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. 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 const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#ifdef DEBUG
+#include <errno.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <utmpx.h>
+#ifdef XIDLE
+#include <setjmp.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/xidle.h>
+#endif
+#include <rpcsvc/rnusers.h>
+
+#include "extern.h"
+
+#ifndef _PATH_DEV
+#define _PATH_DEV "/dev"
+#endif
+
+static utmpidle utmp_idle[MAXUSERS];
+static utmp old_utmp[MAXUSERS];
+static struct utmpx utmp_list[MAXUSERS];
+
+#ifdef XIDLE
+static Display *dpy;
+
+static jmp_buf openAbort;
+
+static void
+abortOpen(void)
+{
+ 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(const char *tty, const char *display __unused)
+{
+ struct stat st;
+ char ttyname[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(ttyname, "%s/%s", _PATH_DEV, tty);
+ if (stat(ttyname, &st) < 0) {
+#ifdef DEBUG
+ printf("%s: %s\n", ttyname, strerror(errno));
+#endif
+ return(-1);
+ }
+ time(&now);
+#ifdef DEBUG
+ printf("%s: now=%d atime=%d\n", ttyname, now,
+ st.st_atime);
+#endif
+ idle = now - st.st_atime;
+ idle = (idle + 30) / 60; /* secs->mins */
+ }
+
+ return(idle);
+}
+
+static utmpidlearr *
+do_names_2(void)
+{
+ static utmpidlearr ut;
+ struct utmpx *usr;
+ int nusers = 0;
+
+ memset(&ut, 0, sizeof(ut));
+ ut.utmpidlearr_val = &utmp_idle[0];
+
+ setutxent();
+ while ((usr = getutxent()) != NULL && nusers < MAXUSERS) {
+ if (usr->ut_type != USER_PROCESS)
+ continue;
+
+ memcpy(&utmp_list[nusers], usr, sizeof(*usr));
+ utmp_idle[nusers].ui_utmp.ut_time = usr->ut_tv.tv_sec;
+ utmp_idle[nusers].ui_idle =
+ getidle(usr->ut_line, usr->ut_host);
+ utmp_idle[nusers].ui_utmp.ut_line =
+ utmp_list[nusers].ut_line;
+ utmp_idle[nusers].ui_utmp.ut_name =
+ utmp_list[nusers].ut_user;
+ utmp_idle[nusers].ui_utmp.ut_host =
+ utmp_list[nusers].ut_host;
+
+ nusers++;
+ }
+ endutxent();
+
+ ut.utmpidlearr_len = nusers;
+ return(&ut);
+}
+
+static int *
+rusers_num(void *argp __unused, struct svc_req *rqstp __unused)
+{
+ static int num_users = 0;
+ struct utmpx *usr;
+
+ setutxent();
+ while ((usr = getutxent()) != NULL) {
+ if (usr->ut_type != USER_PROCESS)
+ continue;
+ num_users++;
+ }
+ endutxent();
+
+ return(&num_users);
+}
+
+static utmparr *
+do_names_1(void)
+{
+ utmpidlearr *utidle;
+ static utmparr ut;
+ unsigned int i;
+
+ bzero((char *)&ut, sizeof(ut));
+
+ utidle = do_names_2();
+ 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_svc(void *argp __unused, struct svc_req *rqstp __unused)
+{
+
+ return (do_names_2());
+}
+
+utmpidlearr *
+rusersproc_allnames_2_svc(void *argp __unused, struct svc_req *rqstp __unused)
+{
+
+ return (do_names_2());
+}
+
+utmparr *
+rusersproc_names_1_svc(void *argp __unused, struct svc_req *rqstp __unused)
+{
+
+ return (do_names_1());
+}
+
+utmparr *
+rusersproc_allnames_1_svc(void *argp __unused, struct svc_req *rqstp __unused)
+{
+
+ return (do_names_1());
+}
+
+typedef void *(*rusersproc_t)(void *, struct svc_req *);
+
+void
+rusers_service(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ union {
+ int fill;
+ } argument;
+ char *result;
+ xdrproc_t xdr_argument, xdr_result;
+ rusersproc_t local;
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL);
+ goto leave;
+
+ case RUSERSPROC_NUM:
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_int;
+ local = (rusersproc_t)rusers_num;
+ break;
+
+ case RUSERSPROC_NAMES:
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_utmpidlearr;
+ switch (rqstp->rq_vers) {
+ case RUSERSVERS_ORIG:
+ local = (rusersproc_t)rusersproc_names_1_svc;
+ break;
+ case RUSERSVERS_IDLE:
+ local = (rusersproc_t)rusersproc_names_2_svc;
+ break;
+ default:
+ svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE);
+ goto leave;
+ /*NOTREACHED*/
+ }
+ break;
+
+ case RUSERSPROC_ALLNAMES:
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_utmpidlearr;
+ switch (rqstp->rq_vers) {
+ case RUSERSVERS_ORIG:
+ local = (rusersproc_t)rusersproc_allnames_1_svc;
+ break;
+ case RUSERSVERS_IDLE:
+ local = (rusersproc_t)rusersproc_allnames_2_svc;
+ break;
+ default:
+ svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE);
+ goto leave;
+ /*NOTREACHED*/
+ }
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ goto leave;
+ }
+ bzero(&argument, sizeof(argument));
+ if (!svc_getargs(transp, (xdrproc_t)xdr_argument, &argument)) {
+ svcerr_decode(transp);
+ goto leave;
+ }
+ result = (*local)(&argument, rqstp);
+ if (result != NULL &&
+ !svc_sendreply(transp, (xdrproc_t)xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, &argument)) {
+ syslog(LOG_ERR, "unable to free arguments");
+ 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..2a1e7bd
--- /dev/null
+++ b/libexec/rpc.rusersd/rusersd.c
@@ -0,0 +1,112 @@
+/*-
+ * 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. 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 const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <rpc/rpc.h>
+#include <sys/socket.h>
+#include <signal.h>
+#include <syslog.h>
+#include <rpcsvc/rnusers.h>
+
+#include "extern.h"
+
+int from_inetd = 1;
+
+static void
+cleanup(int sig __unused)
+{
+ (void) rpcb_unset(RUSERSPROG, RUSERSVERS_IDLE, NULL);
+ (void) rpcb_unset(RUSERSPROG, RUSERSVERS_ORIG, NULL);
+ exit(0);
+}
+
+int
+main(int argc __unused, char *argv[] __unused)
+{
+ SVCXPRT *transp = NULL; /* Keep compiler happy. */
+ int ok;
+ struct sockaddr_storage from;
+ socklen_t fromlen;
+
+ /*
+ * See if inetd started us
+ */
+ fromlen = sizeof(from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ from_inetd = 0;
+ }
+
+ if (!from_inetd) {
+ daemon(0, 0);
+
+ (void) rpcb_unset(RUSERSPROG, RUSERSVERS_IDLE, NULL);
+ (void) rpcb_unset(RUSERSPROG, RUSERSVERS_ORIG, NULL);
+
+ (void) signal(SIGINT, cleanup);
+ (void) signal(SIGTERM, cleanup);
+ (void) signal(SIGHUP, cleanup);
+ }
+
+ openlog("rpc.rusersd", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ if (from_inetd) {
+ transp = svc_tli_create(0, NULL, NULL, 0, 0);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "cannot create udp service.");
+ exit(1);
+ }
+ ok = svc_reg(transp, RUSERSPROG, RUSERSVERS_IDLE,
+ rusers_service, NULL);
+ } else
+ ok = svc_create(rusers_service,
+ RUSERSPROG, RUSERSVERS_IDLE, "udp");
+ if (!ok) {
+ syslog(LOG_ERR, "unable to register (RUSERSPROG, RUSERSVERS_IDLE, %s)", (!from_inetd)?"udp":"(inetd)");
+ exit(1);
+ }
+ if (from_inetd)
+ ok = svc_reg(transp, RUSERSPROG, RUSERSVERS_ORIG,
+ rusers_service, NULL);
+ else
+ ok = svc_create(rusers_service,
+ RUSERSPROG, RUSERSVERS_ORIG, "udp");
+ if (!ok) {
+ syslog(LOG_ERR, "unable to register (RUSERSPROG, RUSERSVERS_ORIG, %s)", (!from_inetd)?"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..b09d663
--- /dev/null
+++ b/libexec/rpc.rwalld/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG = rpc.rwalld
+SRCS = rwalld.c
+MAN = rpc.rwalld.8
+
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+WARNS?= 2
+
+.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..6b2d206
--- /dev/null
+++ b/libexec/rpc.rwalld/rpc.rwalld.8
@@ -0,0 +1,79 @@
+.\" -*- 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. 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 7, 1993
+.Dt RPC.RWALLD 8
+.Os
+.Sh NAME
+.Nm rpc.rwalld
+.Nd write messages to users currently logged in server
+.Sh SYNOPSIS
+.Nm /usr/libexec/rpc.rwalld
+.Op Fl n
+.Sh DESCRIPTION
+The
+.Nm
+utility 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
+The following option is available:
+.Bl -tag -width indent
+.It Fl n
+Do not become a daemon.
+This option is only available when
+.Nm
+is not invoked by
+.Xr inetd 8 .
+.El
+.Pp
+Messages are sent to this server by the
+.Xr rwall 1
+command.
+The
+.Nm
+daemon is normally invoked by
+.Xr inetd 8 .
+.Pp
+The
+.Nm
+utility uses an
+.Tn 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..41ddb14
--- /dev/null
+++ b/libexec/rpc.rwalld/rwalld.c
@@ -0,0 +1,204 @@
+/*
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/rwall.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#ifdef OSF
+#define WALL_CMD "/usr/sbin/wall"
+#else
+#define WALL_CMD "/usr/bin/wall -n"
+#endif
+
+void wallprog_1(struct svc_req *rqstp, SVCXPRT *transp);
+void possess(void);
+void killkids(int sig);
+static void usage(void);
+
+int nodaemon = 0;
+int from_inetd = 1;
+
+int
+main(int argc, char *argv[])
+{
+ SVCXPRT *transp;
+ socklen_t salen;
+ int ok;
+ struct sockaddr_storage sa;
+
+ if (argc == 2 && !strcmp(argv[1], "-n"))
+ nodaemon = 1;
+ if (argc != 1 && !nodaemon)
+ usage();
+
+ 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;
+ }
+
+ if (!from_inetd) {
+ if (!nodaemon)
+ possess();
+
+ (void)rpcb_unset(WALLPROG, WALLVERS, NULL);
+ }
+
+ (void)signal(SIGCHLD, killkids);
+
+ openlog("rpc.rwalld", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ /* create and register the service */
+ if (from_inetd) {
+ transp = svc_tli_create(0, NULL, NULL, 0, 0);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "couldn't create udp service.");
+ exit(1);
+ }
+ ok = svc_reg(transp, WALLPROG, WALLVERS,
+ wallprog_1, NULL);
+ } else
+ ok = svc_create(wallprog_1,
+ WALLPROG, WALLVERS, "udp");
+ if (!ok) {
+ syslog(LOG_ERR, "unable to register (WALLPROG, WALLVERS, %s)", (!from_inetd)?"udp":"(inetd)");
+ exit(1);
+ }
+ svc_run();
+ syslog(LOG_ERR, "svc_run returned");
+ exit(1);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: rpc.rwalld [-n]\n");
+ exit(1);
+}
+
+void
+possess(void)
+{
+ daemon(0, 0);
+}
+
+void
+killkids(int sig __unused)
+{
+ while(wait4(-1, NULL, WNOHANG, NULL) > 0)
+ ;
+}
+
+void *
+wallproc_wall_1_svc(wrapstring *s, struct svc_req *rqstp __unused)
+{
+ static void *dummy = NULL;
+
+ /* 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);
+ }
+ }
+ return(&dummy);
+}
+
+void
+wallprog_1(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, (xdrproc_t)xdr_void, NULL);
+ goto leave;
+
+ case WALLPROC_WALL:
+ xdr_argument = xdr_wrapstring;
+ xdr_result = xdr_void;
+ local = (char *(*)()) wallproc_wall_1_svc;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ goto leave;
+ }
+ bzero(&argument, sizeof(argument));
+ if (!svc_getargs(transp, (xdrproc_t)xdr_argument, &argument)) {
+ svcerr_decode(transp);
+ goto leave;
+ }
+ result = (*local)(&argument, rqstp);
+ if (result != NULL &&
+ !svc_sendreply(transp, (xdrproc_t)xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, &argument)) {
+ syslog(LOG_ERR, "unable to free arguments");
+ exit(1);
+ }
+leave:
+ if (from_inetd)
+ exit(0);
+}
diff --git a/libexec/rpc.sprayd/Makefile b/libexec/rpc.sprayd/Makefile
new file mode 100644
index 0000000..5b1cb24
--- /dev/null
+++ b/libexec/rpc.sprayd/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG = rpc.sprayd
+SRCS = sprayd.c
+MAN = rpc.sprayd.8
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/libexec/rpc.sprayd/rpc.sprayd.8 b/libexec/rpc.sprayd/rpc.sprayd.8
new file mode 100644
index 0000000..8840043
--- /dev/null
+++ b/libexec/rpc.sprayd/rpc.sprayd.8
@@ -0,0 +1,59 @@
+.\"
+.\" Copyright (c) 1994 Christos Zoulas
+.\" 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 Christos Zoulas.
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 22, 1994
+.Dt RPC.SPRAYD 8
+.Os
+.Sh NAME
+.Nm rpc.sprayd
+.Nd spray server
+.Sh SYNOPSIS
+.Nm /usr/libexec/rpc.sprayd
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is a server which records packets sent by the
+.Xr spray 8
+command and sends a traffic report to the originator of the packets.
+The
+.Nm
+daemon is normally invoked by
+.Xr inetd 8 .
+.Pp
+The
+.Nm
+utility uses an
+.Tn RPC
+protocol defined in
+.Pa /usr/include/rpcsvc/spray.x .
+.Sh SEE ALSO
+.Xr spray 8
diff --git a/libexec/rpc.sprayd/sprayd.c b/libexec/rpc.sprayd/sprayd.c
new file mode 100644
index 0000000..9c7cee1
--- /dev/null
+++ b/libexec/rpc.sprayd/sprayd.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 1994 Christos Zoulas
+ * 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 Christos Zoulas.
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <rpc/rpc.h>
+#include <rpcsvc/spray.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <syslog.h>
+#include <unistd.h>
+
+static void spray_service(struct svc_req *, SVCXPRT *);
+
+static int from_inetd = 1;
+
+#define timersub(tvp, uvp, vvp) \
+ do { \
+ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
+ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
+ if ((vvp)->tv_usec < 0) { \
+ (vvp)->tv_sec--; \
+ (vvp)->tv_usec += 1000000; \
+ } \
+ } while (0)
+
+#define TIMEOUT 120
+
+void
+cleanup(int sig __unused)
+{
+ (void) rpcb_unset(SPRAYPROG, SPRAYVERS, NULL);
+ exit(0);
+}
+
+void
+die(int sig __unused)
+{
+ exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ SVCXPRT *transp;
+ int ok;
+ struct sockaddr_storage from;
+ socklen_t fromlen;
+
+ /*
+ * See if inetd started us
+ */
+ fromlen = sizeof(from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ from_inetd = 0;
+ }
+
+ if (!from_inetd) {
+ daemon(0, 0);
+
+ (void) rpcb_unset(SPRAYPROG, SPRAYVERS, NULL);
+
+ (void) signal(SIGINT, cleanup);
+ (void) signal(SIGTERM, cleanup);
+ (void) signal(SIGHUP, cleanup);
+ } else {
+ (void) signal(SIGALRM, die);
+ alarm(TIMEOUT);
+ }
+
+ openlog("rpc.sprayd", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ if (from_inetd) {
+ transp = svc_tli_create(0, NULL, NULL, 0, 0);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "cannot create udp service.");
+ exit(1);
+ }
+ ok = svc_reg(transp, SPRAYPROG, SPRAYVERS,
+ spray_service, NULL);
+ } else
+ ok = svc_create(spray_service,
+ SPRAYPROG, SPRAYVERS, "udp");
+ if (!ok) {
+ syslog(LOG_ERR,
+ "unable to register (SPRAYPROG, SPRAYVERS, %s)",
+ (!from_inetd)?"udp":"(inetd)");
+ return 1;
+ }
+
+ svc_run();
+ syslog(LOG_ERR, "svc_run returned");
+ return 1;
+}
+
+
+static void
+spray_service(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ static spraycumul scum;
+ static struct timeval clear, get;
+
+ switch (rqstp->rq_proc) {
+ case SPRAYPROC_CLEAR:
+ scum.counter = 0;
+ (void) gettimeofday(&clear, 0);
+ /*FALLTHROUGH*/
+
+ case NULLPROC:
+ (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL);
+ return;
+
+ case SPRAYPROC_SPRAY:
+ scum.counter++;
+ return;
+
+ case SPRAYPROC_GET:
+ (void) gettimeofday(&get, 0);
+ timersub(&get, &clear, &get);
+ scum.clock.sec = get.tv_sec;
+ scum.clock.usec = get.tv_usec;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+
+ if (!svc_sendreply(transp, (xdrproc_t)xdr_spraycumul, &scum)) {
+ svcerr_systemerr(transp);
+ syslog(LOG_ERR, "bad svc_sendreply");
+ }
+}
diff --git a/libexec/rshd/Makefile b/libexec/rshd/Makefile
new file mode 100644
index 0000000..f6881e8
--- /dev/null
+++ b/libexec/rshd/Makefile
@@ -0,0 +1,13 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/4/93
+# $FreeBSD$
+
+PROG= rshd
+MAN= rshd.8
+
+WARNS?= 3
+WFORMAT=0
+
+DPADD= ${LIBUTIL} ${LIBPAM}
+LDADD= -lutil ${MINUSLPAM}
+
+.include <bsd.prog.mk>
diff --git a/libexec/rshd/rshd.8 b/libexec/rshd/rshd.8
new file mode 100644
index 0000000..6c6e57c
--- /dev/null
+++ b/libexec/rshd/rshd.8
@@ -0,0 +1,268 @@
+.\" 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. 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
+.\" $FreeBSD$
+.\"
+.Dd June 4, 1993
+.Dt RSHD 8
+.Os
+.Sh NAME
+.Nm rshd
+.Nd remote shell server
+.Sh SYNOPSIS
+.Nm
+.Op Fl aDLln
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is the server for the
+.Xr rcmd 3
+routine and, consequently, for the
+.Xr rsh 1
+utility.
+The server provides remote execution facilities
+with authentication based on privileged port numbers from trusted hosts.
+.Pp
+The
+.Nm
+utility listens for service requests at the port indicated in
+the
+.Dq 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
+.Tn NUL
+(`\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 or the hostname and address do
+not match after verification,
+the dot-notation representation of the host address is used.
+.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
+.Em 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
+The
+.Nm
+utility 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
+.Pa .rhosts
+file,
+unless the user is the superuser.
+.It
+A
+.Tn 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 .
+.El
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a
+This flag is ignored, and is present for compatibility purposes.
+.It Fl D
+Sets the TCP_NODELAY socket option, which improves the performance
+of small back-to-back writes at the expense of additional network
+traffic.
+.It Fl L
+Causes all successful accesses to be logged to
+.Xr syslogd 8
+as
+.Li auth.info
+messages.
+.It Fl l
+Do not use the user's
+.Pa .rhosts
+file for authentication, unless the user is the superuser.
+.It Fl n
+Turn off transport level keepalive messages.
+This will prevent sessions
+from timing out if the client crashes or becomes unreachable.
+.El
+.Sh FILES
+.Bl -tag -width /var/run/nologin -compact
+.It Pa /etc/hosts
+.It Pa /etc/hosts.equiv
+.It Pa /etc/login.conf
+.It Ev $HOME Ns Pa /.rhosts
+.Pp
+.It Pa /etc/pam.conf
+.Nm
+uses
+.Pa /etc/pam.conf
+entries with service name
+.Dq rsh .
+Authentication modules requiring passwords (such as
+.Nm pam_unix )
+are not supported.
+.El
+.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
+or the authentication procedure described above failed.
+.It Sy Remote directory.
+The
+.Xr chdir 2
+function to the home directory failed.
+.It Sy Logins not available right now.
+The
+.Xr rsh 1
+utility was attempted outside the allowed hours defined in
+.Pa /etc/login.conf
+for the local user's login class.
+.It Sy Can't make pipe.
+The pipe needed for the
+.Em stderr ,
+was not created.
+.It Sy Can't fork; try again.
+A
+.Xr fork 2
+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 rlogin 1 ,
+.Xr rsh 1 ,
+.Xr gethostbyaddr 3 ,
+.Xr rcmd 3 ,
+.Xr ruserok 3 ,
+.Xr hosts 5 ,
+.Xr hosts.equiv 5 ,
+.Xr login.conf 5 ,
+.Xr services 5 ,
+.Xr named 8 ,
+.Xr rlogind 8 ,
+.Xr syslogd 8
+.Sh HISTORY
+IPv6 support was added by WIDE/KAME project.
+.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
+.Dq open
+environment.
+.Pp
+A facility to allow all data exchanges to be encrypted should be
+present.
+.Pp
+Post-PAM,
+.Fx
+also needs the following patch applied besides properly configuring
+.Pa .rhosts :
+.Bd -literal -offset indent
+--- etc/pam.d/rsh.orig Wed Dec 17 14:36:20 2003
++++ etc/pam.d/rsh Wed Dec 17 14:30:43 2003
+@@ -9 +9 @@
+-auth required pam_rhosts.so no_warn
++auth required pam_rhosts.so no_warn allow_root
+.Ed
+.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..b315040
--- /dev/null
+++ b/libexec/rshd/rshd.c
@@ -0,0 +1,589 @@
+/*-
+ * Copyright (c) 1988, 1989, 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed for the FreeBSD Project by
+ * ThinkSec AS and NAI Labs, the Security Research Division of Network
+ * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
+ * ("CBOSS"), as part of the DARPA CHATS research program.
+ *
+ * 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. 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 const 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
+#if 0
+static const char sccsid[] = "@(#)rshd.c 8.2 (Berkeley) 4/6/94";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * remote shell server:
+ * [port]\0
+ * ruser\0
+ * luser\0
+ * command\0
+ * data
+ */
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <login_cap.h>
+
+#include <security/pam_appl.h>
+#include <security/openpam.h>
+#include <sys/wait.h>
+
+static struct pam_conv pamc = { openpam_nullconv, NULL };
+static pam_handle_t *pamh;
+static int pam_err;
+
+#define PAM_END { \
+ if ((pam_err = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) \
+ syslog(LOG_ERR|LOG_AUTH, "pam_setcred(): %s", pam_strerror(pamh, pam_err)); \
+ if ((pam_err = pam_close_session(pamh,0)) != PAM_SUCCESS) \
+ syslog(LOG_ERR|LOG_AUTH, "pam_close_session(): %s", pam_strerror(pamh, pam_err)); \
+ if ((pam_err = pam_end(pamh, pam_err)) != PAM_SUCCESS) \
+ syslog(LOG_ERR|LOG_AUTH, "pam_end(): %s", pam_strerror(pamh, pam_err)); \
+}
+
+int keepalive = 1;
+int log_success; /* If TRUE, log all successful accesses */
+int sent_null;
+int no_delay;
+
+void doit(struct sockaddr *);
+static void rshd_errx(int, const char *, ...) __printf0like(2, 3);
+void getstr(char *, int, const char *);
+int local_domain(char *);
+char *topdomain(char *);
+void usage(void);
+
+char slash[] = "/";
+char bshell[] = _PATH_BSHELL;
+
+#define OPTIONS "aDLln"
+
+int
+main(int argc, char *argv[])
+{
+ extern int __check_rhosts_file;
+ struct linger linger;
+ socklen_t fromlen;
+ int ch, on = 1;
+ struct sockaddr_storage from;
+
+ openlog("rshd", LOG_PID, LOG_DAEMON);
+
+ opterr = 0;
+ while ((ch = getopt(argc, argv, OPTIONS)) != -1)
+ switch (ch) {
+ case 'a':
+ /* ignored for compatibility */
+ break;
+ case 'l':
+ __check_rhosts_file = 0;
+ break;
+ case 'n':
+ keepalive = 0;
+ break;
+ case 'D':
+ no_delay = 1;
+ break;
+ case 'L':
+ log_success = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ break;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ 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");
+ if (no_delay &&
+ setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
+ syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m");
+ doit((struct sockaddr *)&from);
+ /* NOTREACHED */
+ return(0);
+}
+
+extern char **environ;
+
+void
+doit(struct sockaddr *fromp)
+{
+ extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */
+ struct passwd *pwd;
+ u_short port;
+ fd_set ready, readfrom;
+ int cc, fd, nfd, pv[2], pid, s;
+ int one = 1;
+ const char *cp, *errorstr;
+ char sig, buf[BUFSIZ];
+ char *cmdbuf, luser[16], ruser[16];
+ char rhost[2 * MAXHOSTNAMELEN + 1];
+ char numericname[INET6_ADDRSTRLEN];
+ int af, srcport;
+ int maxcmdlen;
+ login_cap_t *lc;
+
+ maxcmdlen = (int)sysconf(_SC_ARG_MAX);
+ if (maxcmdlen <= 0 || (cmdbuf = malloc(maxcmdlen)) == NULL)
+ exit(1);
+
+ (void) signal(SIGINT, SIG_DFL);
+ (void) signal(SIGQUIT, SIG_DFL);
+ (void) signal(SIGTERM, SIG_DFL);
+ af = fromp->sa_family;
+ srcport = ntohs(*((in_port_t *)&fromp->sa_data));
+ if (af == AF_INET) {
+ inet_ntop(af, &((struct sockaddr_in *)fromp)->sin_addr,
+ numericname, sizeof numericname);
+ } else if (af == AF_INET6) {
+ inet_ntop(af, &((struct sockaddr_in6 *)fromp)->sin6_addr,
+ numericname, sizeof numericname);
+ } else {
+ syslog(LOG_ERR, "malformed \"from\" address (af %d)", af);
+ exit(1);
+ }
+#ifdef IP_OPTIONS
+ if (af == AF_INET) {
+ u_char optbuf[BUFSIZ/3];
+ socklen_t optsize = sizeof(optbuf), ipproto, i;
+ struct protoent *ip;
+
+ if ((ip = getprotobyname("ip")) != NULL)
+ ipproto = ip->p_proto;
+ else
+ ipproto = IPPROTO_IP;
+ if (!getsockopt(0, ipproto, IP_OPTIONS, optbuf, &optsize) &&
+ optsize != 0) {
+ for (i = 0; i < optsize; ) {
+ u_char c = optbuf[i];
+ if (c == IPOPT_LSRR || c == IPOPT_SSRR) {
+ syslog(LOG_NOTICE,
+ "connection refused from %s with IP option %s",
+ numericname,
+ c == IPOPT_LSRR ? "LSRR" : "SSRR");
+ exit(1);
+ }
+ if (c == IPOPT_EOL)
+ break;
+ i += (c == IPOPT_NOP) ? 1 : optbuf[i+1];
+ }
+ }
+ }
+#endif
+
+ if (srcport >= IPPORT_RESERVED ||
+ srcport < IPPORT_RESERVED/2) {
+ syslog(LOG_NOTICE|LOG_AUTH,
+ "connection from %s on illegal port %u",
+ numericname,
+ srcport);
+ exit(1);
+ }
+
+ (void) alarm(60);
+ port = 0;
+ s = 0; /* not set or used if port == 0 */
+ for (;;) {
+ char c;
+ if ((cc = read(STDIN_FILENO, &c, 1)) != 1) {
+ if (cc < 0)
+ syslog(LOG_NOTICE, "read: %m");
+ shutdown(0, SHUT_RDWR);
+ exit(1);
+ }
+ if (c == 0)
+ break;
+ port = port * 10 + c - '0';
+ }
+
+ (void) alarm(0);
+ if (port != 0) {
+ int lport = IPPORT_RESERVED - 1;
+ s = rresvport_af(&lport, af);
+ if (s < 0) {
+ syslog(LOG_ERR, "can't get stderr port: %m");
+ exit(1);
+ }
+ if (port >= IPPORT_RESERVED ||
+ port < IPPORT_RESERVED/2) {
+ syslog(LOG_NOTICE|LOG_AUTH,
+ "2nd socket from %s on unreserved port %u",
+ numericname,
+ port);
+ exit(1);
+ }
+ *((in_port_t *)&fromp->sa_data) = htons(port);
+ if (connect(s, fromp, fromp->sa_len) < 0) {
+ syslog(LOG_INFO, "connect second port %d: %m", port);
+ exit(1);
+ }
+ }
+
+ errorstr = NULL;
+ realhostname_sa(rhost, sizeof(rhost) - 1, fromp, fromp->sa_len);
+ rhost[sizeof(rhost) - 1] = '\0';
+ /* XXX truncation! */
+
+ (void) alarm(60);
+ getstr(ruser, sizeof(ruser), "ruser");
+ getstr(luser, sizeof(luser), "luser");
+ getstr(cmdbuf, maxcmdlen, "command");
+ (void) alarm(0);
+
+ pam_err = pam_start("rsh", luser, &pamc, &pamh);
+ if (pam_err != PAM_SUCCESS) {
+ syslog(LOG_ERR|LOG_AUTH, "pam_start(): %s",
+ pam_strerror(pamh, pam_err));
+ rshd_errx(1, "Login incorrect.");
+ }
+
+ if ((pam_err = pam_set_item(pamh, PAM_RUSER, ruser)) != PAM_SUCCESS ||
+ (pam_err = pam_set_item(pamh, PAM_RHOST, rhost)) != PAM_SUCCESS) {
+ syslog(LOG_ERR|LOG_AUTH, "pam_set_item(): %s",
+ pam_strerror(pamh, pam_err));
+ rshd_errx(1, "Login incorrect.");
+ }
+
+ pam_err = pam_authenticate(pamh, 0);
+ if (pam_err == PAM_SUCCESS) {
+ if ((pam_err = pam_get_user(pamh, &cp, NULL)) == PAM_SUCCESS) {
+ strncpy(luser, cp, sizeof(luser));
+ luser[sizeof(luser) - 1] = '\0';
+ /* XXX truncation! */
+ }
+ pam_err = pam_acct_mgmt(pamh, 0);
+ }
+ if (pam_err != PAM_SUCCESS) {
+ syslog(LOG_INFO|LOG_AUTH,
+ "%s@%s as %s: permission denied (%s). cmd='%.80s'",
+ ruser, rhost, luser, pam_strerror(pamh, pam_err), cmdbuf);
+ rshd_errx(1, "Login incorrect.");
+ }
+
+ setpwent();
+ pwd = getpwnam(luser);
+ if (pwd == NULL) {
+ syslog(LOG_INFO|LOG_AUTH,
+ "%s@%s as %s: unknown login. cmd='%.80s'",
+ ruser, rhost, luser, cmdbuf);
+ if (errorstr == NULL)
+ errorstr = "Login incorrect.";
+ rshd_errx(1, errorstr, rhost);
+ }
+
+ lc = login_getpwclass(pwd);
+ if (pwd->pw_uid)
+ auth_checknologin(lc);
+
+ if (chdir(pwd->pw_dir) < 0) {
+ if (chdir("/") < 0 ||
+ login_getcapbool(lc, "requirehome", !!pwd->pw_uid)) {
+ syslog(LOG_INFO|LOG_AUTH,
+ "%s@%s as %s: no home directory. cmd='%.80s'",
+ ruser, rhost, luser, cmdbuf);
+ rshd_errx(0, "No remote home directory.");
+ }
+ pwd->pw_dir = slash;
+ }
+
+ if (lc != NULL && fromp->sa_family == AF_INET) { /*XXX*/
+ char remote_ip[MAXHOSTNAMELEN];
+
+ strncpy(remote_ip, numericname,
+ sizeof(remote_ip) - 1);
+ remote_ip[sizeof(remote_ip) - 1] = 0;
+ /* XXX truncation! */
+ if (!auth_hostok(lc, rhost, remote_ip)) {
+ syslog(LOG_INFO|LOG_AUTH,
+ "%s@%s as %s: permission denied (%s). cmd='%.80s'",
+ ruser, rhost, luser, __rcmd_errstr,
+ cmdbuf);
+ rshd_errx(1, "Login incorrect.");
+ }
+ if (!auth_timeok(lc, time(NULL)))
+ rshd_errx(1, "Logins not available right now");
+ }
+
+ /*
+ * PAM modules might add supplementary groups in
+ * pam_setcred(), so initialize them first.
+ * But we need to open the session as root.
+ */
+ if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) {
+ syslog(LOG_ERR, "setusercontext: %m");
+ exit(1);
+ }
+
+ if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, pam_err));
+ } else if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) {
+ syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, pam_err));
+ }
+
+ (void) write(STDERR_FILENO, "\0", 1);
+ sent_null = 1;
+
+ if (port) {
+ if (pipe(pv) < 0)
+ rshd_errx(1, "Can't make pipe.");
+ pid = fork();
+ if (pid == -1)
+ rshd_errx(1, "Can't fork; try again.");
+ if (pid) {
+ (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;
+ ioctl(pv[0], FIONBIO, (char *)&one);
+
+ /* should set s nbio! */
+ nfd++;
+ do {
+ ready = readfrom;
+ if (select(nfd, &ready, (fd_set *)0,
+ (fd_set *)0, (struct timeval *)0) < 0)
+ break;
+ if (FD_ISSET(s, &ready)) {
+ int ret;
+ 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, SHUT_RDWR);
+ FD_CLR(pv[0], &readfrom);
+ } else {
+ (void)write(s, buf, cc);
+ }
+ }
+
+ } while (FD_ISSET(s, &readfrom) ||
+ FD_ISSET(pv[0], &readfrom));
+ PAM_END;
+ exit(0);
+ }
+ (void) close(s);
+ (void) close(pv[0]);
+ dup2(pv[1], 2);
+ close(pv[1]);
+ }
+ else {
+ pid = fork();
+ if (pid == -1)
+ rshd_errx(1, "Can't fork; try again.");
+ if (pid) {
+ /* Parent. */
+ while (wait(NULL) > 0 || errno == EINTR)
+ /* nothing */ ;
+ PAM_END;
+ exit(0);
+ }
+ }
+
+ for (fd = getdtablesize(); fd > 2; fd--)
+ (void) close(fd);
+ if (setsid() == -1)
+ syslog(LOG_ERR, "setsid() failed: %m");
+ if (setlogin(pwd->pw_name) < 0)
+ syslog(LOG_ERR, "setlogin() failed: %m");
+
+ if (*pwd->pw_shell == '\0')
+ pwd->pw_shell = bshell;
+ (void) pam_setenv(pamh, "HOME", pwd->pw_dir, 1);
+ (void) pam_setenv(pamh, "SHELL", pwd->pw_shell, 1);
+ (void) pam_setenv(pamh, "USER", pwd->pw_name, 1);
+ (void) pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1);
+ environ = pam_getenvlist(pamh);
+ (void) pam_end(pamh, pam_err);
+ cp = strrchr(pwd->pw_shell, '/');
+ if (cp)
+ cp++;
+ else
+ cp = pwd->pw_shell;
+
+ if (setusercontext(lc, pwd, pwd->pw_uid,
+ LOGIN_SETALL & ~LOGIN_SETGROUP) < 0) {
+ syslog(LOG_ERR, "setusercontext(): %m");
+ exit(1);
+ }
+ login_close(lc);
+ endpwent();
+ if (log_success || pwd->pw_uid == 0) {
+ syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'",
+ ruser, rhost, luser, cmdbuf);
+ }
+ execl(pwd->pw_shell, cp, "-c", cmdbuf, (char *)NULL);
+ err(1, "%s", 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.
+ */
+
+static void
+rshd_errx(int errcode, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if (sent_null == 0)
+ write(STDERR_FILENO, "\1", 1);
+
+ verrx(errcode, fmt, ap);
+ /* NOTREACHED */
+}
+
+void
+getstr(char *buf, int cnt, const char *error)
+{
+ char c;
+
+ do {
+ if (read(STDIN_FILENO, &c, 1) != 1)
+ exit(1);
+ *buf++ = c;
+ if (--cnt == 0)
+ rshd_errx(1, "%s too long", error);
+ } 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(char *h)
+{
+ char localhost[MAXHOSTNAMELEN];
+ char *p1, *p2;
+
+ localhost[0] = 0;
+ (void) gethostname(localhost, sizeof(localhost) - 1);
+ localhost[sizeof(localhost) - 1] = '\0';
+ /* XXX truncation! */
+ p1 = topdomain(localhost);
+ p2 = topdomain(h);
+ if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2))
+ return (1);
+ return (0);
+}
+
+char *
+topdomain(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(void)
+{
+
+ syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS);
+ exit(2);
+}
diff --git a/libexec/rtld-aout/shlib.c b/libexec/rtld-aout/shlib.c
new file mode 100644
index 0000000..70c5313
--- /dev/null
+++ b/libexec/rtld-aout/shlib.c
@@ -0,0 +1,325 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <a.out.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/link_aout.h>
+#include "shlib.h"
+#include "support.h"
+
+/*
+ * Standard directories to search for files specified by -l.
+ */
+#ifndef STANDARD_SEARCH_DIRS
+#define STANDARD_SEARCH_DIRS "/usr/lib/aout"
+#endif
+
+/*
+ * Actual vector of library search directories,
+ * including `-L'ed and LD_LIBRARY_PATH spec'd ones.
+ */
+char **search_dirs;
+int n_search_dirs;
+
+static const char *standard_search_dirs[] = {
+ STANDARD_SEARCH_DIRS
+};
+
+
+void
+add_search_dir(const char *name)
+{
+ int n;
+
+ for (n = 0; n < n_search_dirs; n++)
+ if (strcmp(search_dirs[n], name) == 0)
+ return;
+ n_search_dirs++;
+ search_dirs = (char **)
+ xrealloc(search_dirs, n_search_dirs * sizeof search_dirs[0]);
+ search_dirs[n_search_dirs - 1] = strdup(name);
+}
+
+void
+add_search_path(char *path)
+{
+ register char *cp, *dup;
+
+ if (path == NULL)
+ return;
+
+ /* Add search directories from `path' */
+ path = dup = strdup(path);
+ while ((cp = strsep(&path, ":")) != NULL)
+ add_search_dir(cp);
+ free(dup);
+}
+
+void
+std_search_path(void)
+{
+ 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(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(int d1[], int n1, int d2[], int 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: can't happen");
+ return 0;
+}
+
+/*
+ * Search directories for a shared library matching the given
+ * major and minor version numbers. See search_lib_dir() below for
+ * the detailed matching rules.
+ *
+ * As soon as a directory with an acceptable match is found, the search
+ * terminates. Subsequent directories are not searched for a better
+ * match. This is in conformance with the SunOS searching rules. Also,
+ * it avoids a lot of directory searches that are virtually guaranteed to
+ * be fruitless.
+ *
+ * The return value is a full pathname to the matching library. The
+ * string is dynamically allocated. If no matching library is found, the
+ * function returns NULL.
+ */
+
+char *
+findshlib(char *name, int *majorp, int *minorp, int do_dot_a)
+{
+ int i;
+
+ for (i = 0; i < n_search_dirs; i++) {
+ char *path;
+
+ path = search_lib_dir(search_dirs[i], name, majorp, minorp,
+ do_dot_a);
+ if(path != NULL)
+ return path;
+ }
+
+ return NULL;
+}
+
+/*
+ * Search library directories for a file with the given name. The
+ * return value is a full pathname to the matching file. The string
+ * is dynamically allocated. If no matching file is found, the function
+ * returns NULL.
+ */
+
+char *
+find_lib_file(const char *name)
+{
+ int i;
+
+ for (i = 0; i < n_search_dirs; i++) {
+ char *path = concat(search_dirs[i], "/", name);
+ struct stat sb;
+
+ if (lstat(path, &sb) != -1) /* We found it */
+ return path;
+
+ free(path);
+ }
+
+ return NULL;
+}
+
+/*
+ * Search a given directory for a library (preferably shared) satisfying
+ * the given criteria.
+ *
+ * The matching rules are as follows:
+ *
+ * if(*majorp == -1)
+ * find the library with the highest major version;
+ * else
+ * insist on a major version identical to *majorp;
+ *
+ * Always find the library with the highest minor version;
+ * if(*minorp != -1)
+ * insist on a minor version >= *minorp;
+ *
+ * It is invalid to specify a specific minor number while wildcarding
+ * the major number.
+ *
+ * The actual major and minor numbers found are returned via the pointer
+ * arguments.
+ *
+ * A suitable shared library is always preferred over a static (.a) library.
+ * If do_dot_a is false, then a static library will not be accepted in
+ * any case.
+ *
+ * The return value is a full pathname to the matching library. The
+ * string is dynamically allocated. If no matching library is found, the
+ * function returns NULL.
+ */
+
+char *
+search_lib_dir(char *dir, char *name, int *majorp, int *minorp, int do_dot_a)
+{
+ size_t namelen;
+ DIR *dd;
+ struct dirent *dp;
+ int best_dewey[MAXDEWEY];
+ int best_ndewey;
+ char dot_a_name[MAXNAMLEN+1];
+ char dot_so_name[MAXNAMLEN+1];
+
+ if((dd = opendir(dir)) == NULL)
+ return NULL;
+
+ namelen = strlen(name);
+ best_ndewey = 0;
+ dot_a_name[0] = '\0';
+ dot_so_name[0] = '\0';
+
+ while((dp = readdir(dd)) != NULL) {
+ char *extension;
+
+ if(strlen(dp->d_name) < 3 + namelen + 2 || /* lib+xxx+.a */
+ strncmp(dp->d_name, "lib", 3) != 0 ||
+ strncmp(dp->d_name + 3, name, namelen) != 0 ||
+ dp->d_name[3+namelen] != '.')
+ continue;
+
+ extension = dp->d_name + 3 + namelen + 1; /* a or so.* */
+
+ if(strncmp(extension, "so.", 3) == 0) {
+ int cur_dewey[MAXDEWEY];
+ int cur_ndewey;
+
+ cur_ndewey = getdewey(cur_dewey, extension+3);
+ if(cur_ndewey < 2) /* Too few version numbers */
+ continue;
+
+ if(*majorp != -1) { /* Need exact match on major */
+ if(cur_dewey[0] != *majorp)
+ continue;
+ if(*minorp != -1) { /* Need minor >= minimum */
+ if(cur_dewey[1] < *minorp)
+ continue;
+ }
+ }
+
+ if(cmpndewey(cur_dewey, cur_ndewey, best_dewey,
+ best_ndewey) <= 0) /* No better than prior match */
+ continue;
+
+ /* We found a better match */
+ strcpy(dot_so_name, dp->d_name);
+ bcopy(cur_dewey, best_dewey,
+ cur_ndewey * sizeof best_dewey[0]);
+ best_ndewey = cur_ndewey;
+ } else if(do_dot_a && strcmp(extension, "a") == 0)
+ strcpy(dot_a_name, dp->d_name);
+ }
+ closedir(dd);
+
+ if(dot_so_name[0] != '\0') {
+ *majorp = best_dewey[0];
+ *minorp = best_dewey[1];
+ return concat(dir, "/", dot_so_name);
+ }
+
+ if(dot_a_name[0] != '\0')
+ return concat(dir, "/", dot_a_name);
+
+ return NULL;
+}
diff --git a/libexec/rtld-aout/shlib.h b/libexec/rtld-aout/shlib.h
new file mode 100644
index 0000000..4b6d753
--- /dev/null
+++ b/libexec/rtld-aout/shlib.h
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (C) 1996
+ * Peter Wemm. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *-
+ * $FreeBSD$
+ */
+
+/*
+ * prototypes for shlib.c. Big deal.
+ */
+
+extern char **search_dirs;
+extern int n_search_dirs;
+
+void add_search_dir __P((const char *));
+void add_search_path __P((char *));
+void std_search_path __P((void));
+int getdewey __P((int[], char *));
+int cmpndewey __P((int[], int, int[], int));
+char *findshlib __P((char *, int *, int *, int));
+char *find_lib_file __P((const char *));
+char *search_lib_dir __P((char *, char *, int *, int *, int));
diff --git a/libexec/rtld-aout/support.c b/libexec/rtld-aout/support.c
new file mode 100644
index 0000000..eb8bd29
--- /dev/null
+++ b/libexec/rtld-aout/support.c
@@ -0,0 +1,82 @@
+/*
+ * Generic "support" routines to replace those obtained from libiberty for ld.
+ *
+ * I've collected these from random bits of (published) code I've written
+ * over the years, not that they are a big deal. peter@freebsd.org
+ *-
+ * Copyright (C) 1996
+ * Peter Wemm. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *-
+ * $FreeBSD$
+ */
+#include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <err.h>
+
+#include "support.h"
+
+char *
+concat(const char *s1, const char *s2, const char *s3)
+{
+ int len = 1;
+ char *s;
+ if (s1)
+ len += strlen(s1);
+ if (s2)
+ len += strlen(s2);
+ if (s3)
+ len += strlen(s3);
+ s = xmalloc(len);
+ s[0] = '\0';
+ if (s1)
+ strcat(s, s1);
+ if (s2)
+ strcat(s, s2);
+ if (s3)
+ strcat(s, s3);
+ return s;
+}
+
+void *
+xmalloc(size_t n)
+{
+ char *p = malloc(n);
+
+ if (p == NULL)
+ errx(1, "Could not allocate memory");
+
+ return p;
+}
+
+void *
+xrealloc(void *p, size_t n)
+{
+ p = realloc(p, n);
+
+ if (p == NULL)
+ errx(1, "Could not allocate memory");
+
+ return p;
+}
diff --git a/libexec/rtld-aout/support.h b/libexec/rtld-aout/support.h
new file mode 100644
index 0000000..da03407
--- /dev/null
+++ b/libexec/rtld-aout/support.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (C) 1996
+ * Peter Wemm. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *-
+ * $FreeBSD$
+ */
+
+/*
+ * prototypes for support.c. Big deal.
+ */
+
+void *xmalloc __P((size_t));
+void *xrealloc __P((void *, size_t));
+char *concat __P((const char *, const char *, const char *));
diff --git a/libexec/rtld-elf/Makefile b/libexec/rtld-elf/Makefile
new file mode 100644
index 0000000..cc30132
--- /dev/null
+++ b/libexec/rtld-elf/Makefile
@@ -0,0 +1,90 @@
+# $FreeBSD$
+
+# Use the following command to build local debug version of dynamic
+# linker:
+# make DEBUG_FLAGS=-g DEBUG=-DDEBUG MK_TESTS=no all
+
+.include <src.opts.mk>
+MK_SSP= no
+
+PROG?= ld-elf.so.1
+SRCS= rtld_start.S \
+ reloc.c rtld.c rtld_lock.c rtld_printf.c map_object.c \
+ malloc.c xmalloc.c debug.c libmap.c
+MAN= rtld.1
+CSTD?= gnu99
+TOPSRCDIR= ${.CURDIR}/../..
+CFLAGS+= -Wall -DFREEBSD_ELF -DIN_RTLD
+CFLAGS+= -I${TOPSRCDIR}/lib/csu/common
+.if exists(${.CURDIR}/${MACHINE_ARCH})
+RTLD_ARCH= ${MACHINE_ARCH}
+.else
+RTLD_ARCH= ${MACHINE_CPUARCH}
+.endif
+CFLAGS+= -I${.CURDIR}/${RTLD_ARCH} -I${.CURDIR}
+.if ${MACHINE_ARCH} == "powerpc64"
+CFLAGS+= -mcall-aixdesc
+LDFLAGS+= -nostdlib -e _rtld_start
+.else
+LDFLAGS+= -nostdlib -e .rtld_start
+.endif
+WARNS?= 2
+INSTALLFLAGS= -C -b
+PRECIOUSPROG=
+BINDIR= /libexec
+SYMLINKS= ${BINDIR}/${PROG} /usr/libexec/${PROG}
+MLINKS= rtld.1 ld-elf.so.1.1 \
+ rtld.1 ld.so.1
+
+.if ${MACHINE_CPUARCH} == "sparc64"
+CFLAGS+= -fPIC
+.else
+CFLAGS+= -fpic
+.endif
+CFLAGS+= -DPIC $(DEBUG)
+LDFLAGS+= -shared -Wl,-Bsymbolic
+DPADD= ${LIBC_PIC}
+LDADD= -lc_pic
+
+.if ${MACHINE_CPUARCH} == "arm"
+# Some of the required math functions (div & mod) are implemented in
+# libcompiler_rt on ARM. The library also needs to be placed first to be
+# correctly linked. As some of the functions are used before we have
+# shared libraries.
+DPADD+= ${LIBCOMPILER_RT}
+LDADD+= -lcompiler_rt
+.endif
+
+
+
+.if ${MK_SYMVER} == "yes"
+LIBCDIR= ${TOPSRCDIR}/lib/libc
+VERSION_DEF= ${LIBCDIR}/Versions.def
+SYMBOL_MAPS= ${.CURDIR}/Symbol.map
+VERSION_MAP= Version.map
+LDFLAGS+= -Wl,--version-script=${VERSION_MAP}
+
+${PROG}: ${VERSION_MAP}
+
+.if exists(${.CURDIR}/${RTLD_ARCH}/Symbol.map)
+SYMBOL_MAPS+= ${.CURDIR}/${RTLD_ARCH}/Symbol.map
+.endif
+.endif
+
+.sinclude "${.CURDIR}/${RTLD_ARCH}/Makefile.inc"
+
+# Since moving rtld-elf to /libexec, we need to create a symlink.
+# Fixup the existing binary that's there so we can symlink over it.
+beforeinstall:
+.if exists(${DESTDIR}/usr/libexec/${PROG})
+ -chflags -h noschg ${DESTDIR}/usr/libexec/${PROG}
+.endif
+
+.PATH: ${.CURDIR}/${RTLD_ARCH}
+
+.if ${MK_TESTS} != "no"
+SUBDIR+= tests
+.endif
+
+.include <bsd.symver.mk>
+.include <bsd.prog.mk>
diff --git a/libexec/rtld-elf/Symbol.map b/libexec/rtld-elf/Symbol.map
new file mode 100644
index 0000000..5ea7d7e
--- /dev/null
+++ b/libexec/rtld-elf/Symbol.map
@@ -0,0 +1,34 @@
+/*
+ * $FreeBSD$
+ */
+
+FBSD_1.0 {
+ _rtld_error;
+ dlclose;
+ dlerror;
+ dlopen;
+ dlsym;
+ dlfunc;
+ dlvsym;
+ dladdr;
+ dllockinit;
+ dlinfo;
+ dl_iterate_phdr;
+ r_debug_state;
+ __tls_get_addr;
+};
+
+FBSD_1.3 {
+ fdlopen;
+};
+
+FBSDprivate_1.0 {
+ _rtld_thread_init;
+ _rtld_allocate_tls;
+ _rtld_free_tls;
+ _rtld_atfork_pre;
+ _rtld_atfork_post;
+ _rtld_addr_phdr;
+ _rtld_get_stack_prot;
+ _r_debug_postinit;
+};
diff --git a/libexec/rtld-elf/amd64/Makefile.inc b/libexec/rtld-elf/amd64/Makefile.inc
new file mode 100644
index 0000000..7528dbe
--- /dev/null
+++ b/libexec/rtld-elf/amd64/Makefile.inc
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+CFLAGS+= -mno-mmx -mno-3dnow -mno-sse -mno-sse2 -mno-sse3 -msoft-float
+# Uncomment this to build the dynamic linker as an executable instead
+# of a shared library:
+#LDSCRIPT= ${.CURDIR}/${MACHINE_CPUARCH}/elf_rtld.x
diff --git a/libexec/rtld-elf/amd64/elf_rtld.x b/libexec/rtld-elf/amd64/elf_rtld.x
new file mode 100644
index 0000000..e19168c
--- /dev/null
+++ b/libexec/rtld-elf/amd64/elf_rtld.x
@@ -0,0 +1,131 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386",
+ "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/obj/usr/src/tmp/usr/i386-unknown-freebsdelf/lib);
+/* Do we need any of these for elf?
+ __DYNAMIC = 0; */
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x08000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.text :
+ { *(.rel.text) *(.rel.gnu.linkonce.t*) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rel.data :
+ { *(.rel.data) *(.rel.gnu.linkonce.d*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rel.rodata :
+ { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.bss : { *(.rel.bss) }
+ .rela.bss : { *(.rela.bss) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init : { *(.init) } =0x9090
+ .plt : { *(.plt) }
+ .text :
+ {
+ *(.text)
+ *(.stub)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0x9090
+ _etext = .;
+ PROVIDE (etext = .);
+ .fini : { *(.fini) } =0x9090
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x1000) + (. & (0x1000 - 1));
+ .data :
+ {
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .data1 : { *(.data1) }
+ .ctors :
+ {
+ *(.ctors)
+ }
+ .dtors :
+ {
+ *(.dtors)
+ }
+ .got : { *(.got.plt) *(.got) }
+ .dynamic : { *(.dynamic) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : { *(.sdata) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss : { *(.sbss) *(.scommon) }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ . = ALIGN(32 / 8);
+ _end = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* These must appear regardless of . */
+}
diff --git a/libexec/rtld-elf/amd64/reloc.c b/libexec/rtld-elf/amd64/reloc.c
new file mode 100644
index 0000000..35f33cc
--- /dev/null
+++ b/libexec/rtld-elf/amd64/reloc.c
@@ -0,0 +1,471 @@
+/*-
+ * Copyright 1996, 1997, 1998, 1999 John D. Polstra.
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Dynamic linker for ELF.
+ *
+ * John Polstra <jdp@polstra.com>.
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <machine/sysarch.h>
+
+#include <dlfcn.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+#include "rtld.h"
+
+/*
+ * Process the special R_X86_64_COPY relocations in the main program. These
+ * copy data from a shared object into a region in the main program's BSS
+ * segment.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int
+do_copy_relocations(Obj_Entry *dstobj)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */
+
+ relalim = (const Elf_Rela *) ((caddr_t) dstobj->rela + dstobj->relasize);
+ for (rela = dstobj->rela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) == R_X86_64_COPY) {
+ void *dstaddr;
+ const Elf_Sym *dstsym;
+ const char *name;
+ size_t size;
+ const void *srcaddr;
+ const Elf_Sym *srcsym;
+ const Obj_Entry *srcobj, *defobj;
+ SymLook req;
+ int res;
+
+ dstaddr = (void *) (dstobj->relocbase + rela->r_offset);
+ dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
+ name = dstobj->strtab + dstsym->st_name;
+ size = dstsym->st_size;
+ symlook_init(&req, name);
+ req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info));
+ req.flags = SYMLOOK_EARLY;
+
+ for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) {
+ res = symlook_obj(&req, srcobj);
+ if (res == 0) {
+ srcsym = req.sym_out;
+ defobj = req.defobj_out;
+ break;
+ }
+ }
+
+ if (srcobj == NULL) {
+ _rtld_error("Undefined symbol \"%s\" referenced from COPY"
+ " relocation in %s", name, dstobj->path);
+ return -1;
+ }
+
+ srcaddr = (const void *) (defobj->relocbase + srcsym->st_value);
+ memcpy(dstaddr, srcaddr, size);
+ }
+ }
+
+ return 0;
+}
+
+/* Initialize the special GOT entries. */
+void
+init_pltgot(Obj_Entry *obj)
+{
+ if (obj->pltgot != NULL) {
+ obj->pltgot[1] = (Elf_Addr) obj;
+ obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
+ }
+}
+
+/* Process the non-PLT relocations. */
+int
+reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
+ RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ SymCache *cache;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+ Elf_Addr *where, symval;
+ Elf32_Addr *where32;
+ int r;
+
+ r = -1;
+ /*
+ * The dynamic loader may be called from a thread, we have
+ * limited amounts of stack available so we cannot use alloca().
+ */
+ if (obj != obj_rtld) {
+ cache = calloc(obj->dynsymcount, sizeof(SymCache));
+ /* No need to check for NULL here */
+ } else
+ cache = NULL;
+
+ relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize);
+ for (rela = obj->rela; rela < relalim; rela++) {
+ /*
+ * First, resolve symbol for relocations which
+ * reference symbols.
+ */
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_X86_64_64:
+ case R_X86_64_PC32:
+ case R_X86_64_GLOB_DAT:
+ case R_X86_64_TPOFF64:
+ case R_X86_64_TPOFF32:
+ case R_X86_64_DTPMOD64:
+ case R_X86_64_DTPOFF64:
+ case R_X86_64_DTPOFF32:
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj,
+ &defobj, flags, cache, lockstate);
+ if (def == NULL)
+ goto done;
+ /*
+ * If symbol is IFUNC, only perform relocation
+ * when caller allowed it by passing
+ * SYMLOOK_IFUNC flag. Skip the relocations
+ * otherwise.
+ *
+ * Also error out in case IFUNC relocations
+ * are specified for TLS, which cannot be
+ * usefully interpreted.
+ */
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_X86_64_64:
+ case R_X86_64_PC32:
+ case R_X86_64_GLOB_DAT:
+ if ((flags & SYMLOOK_IFUNC) == 0) {
+ obj->non_plt_gnu_ifunc = true;
+ continue;
+ }
+ symval = (Elf_Addr)rtld_resolve_ifunc(
+ defobj, def);
+ break;
+ case R_X86_64_TPOFF64:
+ case R_X86_64_TPOFF32:
+ case R_X86_64_DTPMOD64:
+ case R_X86_64_DTPOFF64:
+ case R_X86_64_DTPOFF32:
+ _rtld_error("%s: IFUNC for TLS reloc",
+ obj->path);
+ goto done;
+ }
+ } else {
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ continue;
+ symval = (Elf_Addr)defobj->relocbase +
+ def->st_value;
+ }
+ break;
+ default:
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ continue;
+ break;
+ }
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ where32 = (Elf32_Addr *)where;
+
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_X86_64_NONE:
+ break;
+ case R_X86_64_64:
+ *where = symval + rela->r_addend;
+ break;
+ case R_X86_64_PC32:
+ /*
+ * I don't think the dynamic linker should
+ * ever see this type of relocation. But the
+ * binutils-2.6 tools sometimes generate it.
+ */
+ *where32 = (Elf32_Addr)(unsigned long)(symval +
+ rela->r_addend - (Elf_Addr)where);
+ break;
+ /* missing: R_X86_64_GOT32 R_X86_64_PLT32 */
+ case R_X86_64_COPY:
+ /*
+ * These are deferred until all other relocations have
+ * been done. All we do here is make sure that the COPY
+ * relocation is not in a shared library. They are allowed
+ * only in executable files.
+ */
+ if (!obj->mainprog) {
+ _rtld_error("%s: Unexpected R_X86_64_COPY "
+ "relocation in shared library", obj->path);
+ goto done;
+ }
+ break;
+ case R_X86_64_GLOB_DAT:
+ *where = symval;
+ break;
+ case R_X86_64_TPOFF64:
+ /*
+ * We lazily allocate offsets for static TLS
+ * as we see the first relocation that
+ * references the TLS block. This allows us to
+ * support (small amounts of) static TLS in
+ * dynamically loaded modules. If we run out
+ * of space, we generate an error.
+ */
+ if (!defobj->tls_done) {
+ if (!allocate_tls_offset((Obj_Entry*) defobj)) {
+ _rtld_error("%s: No space available "
+ "for static Thread Local Storage",
+ obj->path);
+ goto done;
+ }
+ }
+ *where = (Elf_Addr)(def->st_value - defobj->tlsoffset +
+ rela->r_addend);
+ break;
+ case R_X86_64_TPOFF32:
+ /*
+ * We lazily allocate offsets for static TLS
+ * as we see the first relocation that
+ * references the TLS block. This allows us to
+ * support (small amounts of) static TLS in
+ * dynamically loaded modules. If we run out
+ * of space, we generate an error.
+ */
+ if (!defobj->tls_done) {
+ if (!allocate_tls_offset((Obj_Entry*) defobj)) {
+ _rtld_error("%s: No space available "
+ "for static Thread Local Storage",
+ obj->path);
+ goto done;
+ }
+ }
+ *where32 = (Elf32_Addr)(def->st_value -
+ defobj->tlsoffset + rela->r_addend);
+ break;
+ case R_X86_64_DTPMOD64:
+ *where += (Elf_Addr)defobj->tlsindex;
+ break;
+ case R_X86_64_DTPOFF64:
+ *where += (Elf_Addr)(def->st_value + rela->r_addend);
+ break;
+ case R_X86_64_DTPOFF32:
+ *where32 += (Elf32_Addr)(def->st_value +
+ rela->r_addend);
+ break;
+ case R_X86_64_RELATIVE:
+ *where = (Elf_Addr)(obj->relocbase + rela->r_addend);
+ break;
+ /*
+ * missing:
+ * R_X86_64_GOTPCREL, R_X86_64_32, R_X86_64_32S, R_X86_64_16,
+ * R_X86_64_PC16, R_X86_64_8, R_X86_64_PC8
+ */
+ default:
+ _rtld_error("%s: Unsupported relocation type %u"
+ " in non-PLT relocations\n", obj->path,
+ (unsigned int)ELF_R_TYPE(rela->r_info));
+ goto done;
+ }
+ }
+ r = 0;
+done:
+ free(cache);
+ return (r);
+}
+
+/* Process the PLT relocations. */
+int
+reloc_plt(Obj_Entry *obj)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ Elf_Addr *where;
+
+ switch(ELF_R_TYPE(rela->r_info)) {
+ case R_X86_64_JMP_SLOT:
+ /* Relocate the GOT slot pointing into the PLT. */
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ *where += (Elf_Addr)obj->relocbase;
+ break;
+
+ case R_X86_64_IRELATIVE:
+ obj->irelative = true;
+ break;
+
+ default:
+ _rtld_error("Unknown relocation type %x in PLT",
+ (unsigned int)ELF_R_TYPE(rela->r_info));
+ return (-1);
+ }
+ }
+ return 0;
+}
+
+/* Relocate the jump slots in an object. */
+int
+reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ if (obj->jmpslots_done)
+ return 0;
+ relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ Elf_Addr *where, target;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_X86_64_JMP_SLOT:
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL)
+ return (-1);
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ obj->gnu_ifunc = true;
+ continue;
+ }
+ target = (Elf_Addr)(defobj->relocbase + def->st_value + rela->r_addend);
+ reloc_jmpslot(where, target, defobj, obj, (const Elf_Rel *)rela);
+ break;
+
+ case R_X86_64_IRELATIVE:
+ break;
+
+ default:
+ _rtld_error("Unknown relocation type %x in PLT",
+ (unsigned int)ELF_R_TYPE(rela->r_info));
+ return (-1);
+ }
+ }
+ obj->jmpslots_done = true;
+ return 0;
+}
+
+int
+reloc_iresolve(Obj_Entry *obj, RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ if (!obj->irelative)
+ return (0);
+ relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ Elf_Addr *where, target, *ptr;
+
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_X86_64_JMP_SLOT:
+ break;
+
+ case R_X86_64_IRELATIVE:
+ ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ lock_release(rtld_bind_lock, lockstate);
+ target = ((Elf_Addr (*)(void))ptr)();
+ wlock_acquire(rtld_bind_lock, lockstate);
+ *where = target;
+ break;
+ }
+ }
+ obj->irelative = false;
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ if (!obj->gnu_ifunc)
+ return (0);
+ relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ Elf_Addr *where, target;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_X86_64_JMP_SLOT:
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL)
+ return (-1);
+ if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC)
+ continue;
+ lock_release(rtld_bind_lock, lockstate);
+ target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
+ wlock_acquire(rtld_bind_lock, lockstate);
+ reloc_jmpslot(where, target, defobj, obj, (const Elf_Rel *)rela);
+ break;
+ }
+ }
+ obj->gnu_ifunc = false;
+ return (0);
+}
+
+void
+allocate_initial_tls(Obj_Entry *objs)
+{
+ /*
+ * Fix the size of the static TLS block by using the maximum
+ * offset allocated so far and adding a bit for dynamic modules to
+ * use.
+ */
+ tls_static_space = tls_last_offset + RTLD_STATIC_TLS_EXTRA;
+ amd64_set_fsbase(allocate_tls(objs, 0,
+ 3*sizeof(Elf_Addr), sizeof(Elf_Addr)));
+}
+
+void *__tls_get_addr(tls_index *ti)
+{
+ Elf_Addr** segbase;
+
+ __asm __volatile("movq %%fs:0, %0" : "=r" (segbase));
+
+ return tls_get_addr_common(&segbase[1], ti->ti_module, ti->ti_offset);
+}
diff --git a/libexec/rtld-elf/amd64/rtld_machdep.h b/libexec/rtld-elf/amd64/rtld_machdep.h
new file mode 100644
index 0000000..7b5d4d2
--- /dev/null
+++ b/libexec/rtld-elf/amd64/rtld_machdep.h
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 1999, 2000 John D. Polstra.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef RTLD_MACHDEP_H
+#define RTLD_MACHDEP_H 1
+
+#include <sys/types.h>
+#include <machine/atomic.h>
+
+struct Struct_Obj_Entry;
+
+/* Return the address of the .dynamic section in the dynamic linker. */
+#define rtld_dynamic(obj) \
+ ((const Elf_Dyn *)((obj)->relocbase + (Elf_Addr)&_DYNAMIC))
+
+/* Fixup the jump slot at "where" to transfer control to "target". */
+static inline Elf_Addr
+reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
+ const struct Struct_Obj_Entry *obj,
+ const struct Struct_Obj_Entry *refobj, const Elf_Rel *rel)
+{
+#ifdef dbg
+ dbg("reloc_jmpslot: *%p = %p", (void *)(where),
+ (void *)(target));
+#endif
+ (*(Elf_Addr *)(where) = (Elf_Addr)(target));
+ return target;
+}
+
+#define make_function_pointer(def, defobj) \
+ ((defobj)->relocbase + (def)->st_value)
+
+#define call_initfini_pointer(obj, target) \
+ (((InitFunc)(target))())
+
+#define call_init_pointer(obj, target) \
+ (((InitArrFunc)(target))(main_argc, main_argv, environ))
+
+#define round(size, align) \
+ (((size) + (align) - 1) & ~((align) - 1))
+#define calculate_first_tls_offset(size, align) \
+ round(size, align)
+#define calculate_tls_offset(prev_offset, prev_size, size, align) \
+ round((prev_offset) + (size), align)
+#define calculate_tls_end(off, size) (off)
+
+typedef struct {
+ unsigned long ti_module;
+ unsigned long ti_offset;
+} tls_index;
+
+extern void *__tls_get_addr(tls_index *ti);
+
+#define RTLD_DEFAULT_STACK_PF_EXEC PF_X
+#define RTLD_DEFAULT_STACK_EXEC PROT_EXEC
+
+#endif
diff --git a/libexec/rtld-elf/amd64/rtld_start.S b/libexec/rtld-elf/amd64/rtld_start.S
new file mode 100644
index 0000000..2481f09
--- /dev/null
+++ b/libexec/rtld-elf/amd64/rtld_start.S
@@ -0,0 +1,159 @@
+/*-
+ * Copyright 1996-1998 John D. Polstra.
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+ .text
+ .align 4
+ .globl .rtld_start
+ .type .rtld_start,@function
+.rtld_start:
+ xorq %rbp,%rbp # Clear frame pointer for good form
+ subq $24,%rsp # A place to store exit procedure addr
+ movq %rdi,%r12
+ movq %rsp,%rsi # save address of exit proc
+ movq %rsp,%rdx # construct address of obj_main
+ addq $8,%rdx
+ call _rtld@PLT # Call rtld(sp); returns entry point
+ popq %rsi # Get exit procedure address
+ movq %r12,%rdi # *ap
+/*
+ * At this point, %rax contains the entry point of the main program, and
+ * %rdx contains a pointer to a termination function that should be
+ * registered with atexit(). (crt1.o registers it.)
+ */
+.globl .rtld_goto_main
+.rtld_goto_main: # This symbol exists just to make debugging easier.
+ jmp *%rax # Enter main program
+
+
+/*
+ * Binder entry point. Control is transferred to here by code in the PLT.
+ * On entry, there are two arguments on the stack. In ascending address
+ * order, they are (1) "obj", a pointer to the calling object's Obj_Entry,
+ * and (2) "reloff", the byte offset of the appropriate relocation entry
+ * in the PLT relocation table.
+ *
+ * We are careful to preserve all registers, even the caller-save
+ * registers. That is because this code may be invoked by low-level
+ * assembly-language code that is not ABI-compliant.
+ *
+ * Stack map:
+ * reloff 0x60
+ * obj 0x58
+ * spare 0x50
+ * rflags 0x48
+ * rax 0x40
+ * rdx 0x38
+ * rcx 0x30
+ * rsi 0x28
+ * rdi 0x20
+ * r8 0x18
+ * r9 0x10
+ * r10 0x8
+ * r11 0x0
+ */
+ .align 4
+ .globl _rtld_bind_start
+ .type _rtld_bind_start,@function
+_rtld_bind_start:
+ .cfi_startproc
+ .cfi_adjust_cfa_offset 16
+ subq $8,%rsp
+ .cfi_adjust_cfa_offset 8
+ pushfq # Save rflags
+ .cfi_adjust_cfa_offset 8
+ pushq %rax # Save %rax
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset %rax,-32
+ pushq %rdx # Save %rdx
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset %rdx,-40
+ pushq %rcx # Save %rcx
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset %rcx,-48
+ pushq %rsi # Save %rsi
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset %rsi,-56
+ pushq %rdi # Save %rdi
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset %rdi,-64
+ pushq %r8 # Save %r8
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset %r8,-72
+ pushq %r9 # Save %r9
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset %r9,-80
+ pushq %r10 # Save %r10
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset %r10,-88
+ pushq %r11 # Save %r11
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset %r11,-96
+
+ movq 0x58(%rsp),%rdi # Fetch obj argument
+ movq 0x60(%rsp),%rsi # Fetch reloff argument
+ leaq (%rsi,%rsi,2),%rsi # multiply by 3
+ leaq (,%rsi,8),%rsi # now 8, for 24 (sizeof Elf_Rela)
+
+ call _rtld_bind@PLT # Transfer control to the binder
+ /* Now %rax contains the entry point of the function being called. */
+
+ movq %rax,0x60(%rsp) # Store target over reloff argument
+ popq %r11 # Restore %r11
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %r11
+ popq %r10 # Restore %r10
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %r10
+ popq %r9 # Restore %r9
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %r9
+ popq %r8 # Restore %r8
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %r8
+ popq %rdi # Restore %rdi
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %rdi
+ popq %rsi # Restore %rsi
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %rsi
+ popq %rcx # Restore %rcx
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %rcx
+ popq %rdx # Restore %rdx
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %rdx
+ popq %rax # Restore %rax
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %rax
+ popfq # Restore rflags
+ .cfi_adjust_cfa_offset -8
+ leaq 16(%rsp),%rsp # Discard spare, obj, do not change rflags
+ ret # "Return" to target address
+ .cfi_endproc
+ .size _rtld_bind_start, . - _rtld_bind_start
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/libexec/rtld-elf/arm/Makefile.inc b/libexec/rtld-elf/arm/Makefile.inc
new file mode 100644
index 0000000..e8c0da7
--- /dev/null
+++ b/libexec/rtld-elf/arm/Makefile.inc
@@ -0,0 +1 @@
+# $FreeBSD$
diff --git a/libexec/rtld-elf/arm/reloc.c b/libexec/rtld-elf/arm/reloc.c
new file mode 100644
index 0000000..9cbdc0e
--- /dev/null
+++ b/libexec/rtld-elf/arm/reloc.c
@@ -0,0 +1,475 @@
+/* $NetBSD: mdreloc.c,v 1.23 2003/07/26 15:04:38 mrg Exp $ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#include <sys/param.h>
+#include <sys/mman.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "machine/sysarch.h"
+
+#include "debug.h"
+#include "rtld.h"
+
+void
+init_pltgot(Obj_Entry *obj)
+{
+ if (obj->pltgot != NULL) {
+ obj->pltgot[1] = (Elf_Addr) obj;
+ obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
+ }
+}
+
+int
+do_copy_relocations(Obj_Entry *dstobj)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+
+ assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */
+
+ rellim = (const Elf_Rel *) ((caddr_t) dstobj->rel + dstobj->relsize);
+ for (rel = dstobj->rel; rel < rellim; rel++) {
+ if (ELF_R_TYPE(rel->r_info) == R_ARM_COPY) {
+ void *dstaddr;
+ const Elf_Sym *dstsym;
+ const char *name;
+ size_t size;
+ const void *srcaddr;
+ const Elf_Sym *srcsym;
+ const Obj_Entry *srcobj, *defobj;
+ SymLook req;
+ int res;
+
+ dstaddr = (void *) (dstobj->relocbase + rel->r_offset);
+ dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info);
+ name = dstobj->strtab + dstsym->st_name;
+ size = dstsym->st_size;
+
+ symlook_init(&req, name);
+ req.ventry = fetch_ventry(dstobj,
+ ELF_R_SYM(rel->r_info));
+ req.flags = SYMLOOK_EARLY;
+
+ for (srcobj = dstobj->next; srcobj != NULL;
+ srcobj = srcobj->next) {
+ res = symlook_obj(&req, srcobj);
+ if (res == 0) {
+ srcsym = req.sym_out;
+ defobj = req.defobj_out;
+ break;
+ }
+ }
+ if (srcobj == NULL) {
+ _rtld_error(
+"Undefined symbol \"%s\" referenced from COPY relocation in %s",
+ name, dstobj->path);
+ return (-1);
+ }
+
+ srcaddr = (const void *)(defobj->relocbase +
+ srcsym->st_value);
+ memcpy(dstaddr, srcaddr, size);
+ }
+ }
+ return 0;
+}
+
+void _rtld_bind_start(void);
+void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
+
+int open();
+int _open();
+void
+_rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
+{
+ const Elf_Rel *rel = 0, *rellim;
+ Elf_Addr relsz = 0;
+ Elf_Addr *where;
+ uint32_t size;
+
+ for (; dynp->d_tag != DT_NULL; dynp++) {
+ switch (dynp->d_tag) {
+ case DT_REL:
+ rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr);
+ break;
+ case DT_RELSZ:
+ relsz = dynp->d_un.d_val;
+ break;
+ }
+ }
+ rellim = (const Elf_Rel *)((caddr_t)rel + relsz);
+ size = (rellim - 1)->r_offset - rel->r_offset;
+ for (; rel < rellim; rel++) {
+ where = (Elf_Addr *)(relocbase + rel->r_offset);
+
+ *where += (Elf_Addr)relocbase;
+ }
+}
+/*
+ * It is possible for the compiler to emit relocations for unaligned data.
+ * We handle this situation with these inlines.
+ */
+#define RELOC_ALIGNED_P(x) \
+ (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
+
+static __inline Elf_Addr
+load_ptr(void *where)
+{
+ Elf_Addr res;
+
+ memcpy(&res, where, sizeof(res));
+
+ return (res);
+}
+
+static __inline void
+store_ptr(void *where, Elf_Addr val)
+{
+
+ memcpy(where, &val, sizeof(val));
+}
+
+static int
+reloc_nonplt_object(Obj_Entry *obj, const Elf_Rel *rel, SymCache *cache,
+ int flags, RtldLockState *lockstate)
+{
+ Elf_Addr *where;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+ Elf_Addr tmp;
+ unsigned long symnum;
+
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ symnum = ELF_R_SYM(rel->r_info);
+
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_ARM_NONE:
+ break;
+
+#if 1 /* XXX should not occur */
+ case R_ARM_PC24: { /* word32 S - P + A */
+ Elf32_Sword addend;
+
+ /*
+ * Extract addend and sign-extend if needed.
+ */
+ addend = *where;
+ if (addend & 0x00800000)
+ addend |= 0xff000000;
+
+ def = find_symdef(symnum, obj, &defobj, flags, cache,
+ lockstate);
+ if (def == NULL)
+ return -1;
+ tmp = (Elf_Addr)obj->relocbase + def->st_value
+ - (Elf_Addr)where + (addend << 2);
+ if ((tmp & 0xfe000000) != 0xfe000000 &&
+ (tmp & 0xfe000000) != 0) {
+ _rtld_error(
+ "%s: R_ARM_PC24 relocation @ %p to %s failed "
+ "(displacement %ld (%#lx) out of range)",
+ obj->path, where,
+ obj->strtab + obj->symtab[symnum].st_name,
+ (long) tmp, (long) tmp);
+ return -1;
+ }
+ tmp >>= 2;
+ *where = (*where & 0xff000000) | (tmp & 0x00ffffff);
+ dbg("PC24 %s in %s --> %p @ %p in %s",
+ obj->strtab + obj->symtab[symnum].st_name,
+ obj->path, (void *)*where, where, defobj->path);
+ break;
+ }
+#endif
+
+ case R_ARM_ABS32: /* word32 B + S + A */
+ case R_ARM_GLOB_DAT: /* word32 B + S */
+ def = find_symdef(symnum, obj, &defobj, flags, cache,
+ lockstate);
+ if (def == NULL)
+ return -1;
+ if (__predict_true(RELOC_ALIGNED_P(where))) {
+ tmp = *where + (Elf_Addr)defobj->relocbase +
+ def->st_value;
+ *where = tmp;
+ } else {
+ tmp = load_ptr(where) +
+ (Elf_Addr)defobj->relocbase +
+ def->st_value;
+ store_ptr(where, tmp);
+ }
+ dbg("ABS32/GLOB_DAT %s in %s --> %p @ %p in %s",
+ obj->strtab + obj->symtab[symnum].st_name,
+ obj->path, (void *)tmp, where, defobj->path);
+ break;
+
+ case R_ARM_RELATIVE: /* word32 B + A */
+ if (__predict_true(RELOC_ALIGNED_P(where))) {
+ tmp = *where + (Elf_Addr)obj->relocbase;
+ *where = tmp;
+ } else {
+ tmp = load_ptr(where) +
+ (Elf_Addr)obj->relocbase;
+ store_ptr(where, tmp);
+ }
+ dbg("RELATIVE in %s --> %p", obj->path,
+ (void *)tmp);
+ break;
+
+ case R_ARM_COPY:
+ /*
+ * These are deferred until all other relocations have
+ * been done. All we do here is make sure that the
+ * COPY relocation is not in a shared library. They
+ * are allowed only in executable files.
+ */
+ if (!obj->mainprog) {
+ _rtld_error(
+ "%s: Unexpected R_COPY relocation in shared library",
+ obj->path);
+ return -1;
+ }
+ dbg("COPY (avoid in main)");
+ break;
+
+ case R_ARM_TLS_DTPOFF32:
+ def = find_symdef(symnum, obj, &defobj, flags, cache,
+ lockstate);
+ if (def == NULL)
+ return -1;
+
+ tmp = (Elf_Addr)(def->st_value);
+ if (__predict_true(RELOC_ALIGNED_P(where)))
+ *where = tmp;
+ else
+ store_ptr(where, tmp);
+
+ dbg("TLS_DTPOFF32 %s in %s --> %p",
+ obj->strtab + obj->symtab[symnum].st_name,
+ obj->path, (void *)tmp);
+
+ break;
+ case R_ARM_TLS_DTPMOD32:
+ def = find_symdef(symnum, obj, &defobj, flags, cache,
+ lockstate);
+ if (def == NULL)
+ return -1;
+
+ tmp = (Elf_Addr)(defobj->tlsindex);
+ if (__predict_true(RELOC_ALIGNED_P(where)))
+ *where = tmp;
+ else
+ store_ptr(where, tmp);
+
+ dbg("TLS_DTPMOD32 %s in %s --> %p",
+ obj->strtab + obj->symtab[symnum].st_name,
+ obj->path, (void *)tmp);
+
+ break;
+
+ case R_ARM_TLS_TPOFF32:
+ def = find_symdef(symnum, obj, &defobj, flags, cache,
+ lockstate);
+ if (def == NULL)
+ return -1;
+
+ if (!defobj->tls_done && allocate_tls_offset(obj))
+ return -1;
+
+ /* XXX: FIXME */
+ tmp = (Elf_Addr)def->st_value + defobj->tlsoffset +
+ TLS_TCB_SIZE;
+ if (__predict_true(RELOC_ALIGNED_P(where)))
+ *where = tmp;
+ else
+ store_ptr(where, tmp);
+ dbg("TLS_TPOFF32 %s in %s --> %p",
+ obj->strtab + obj->symtab[symnum].st_name,
+ obj->path, (void *)tmp);
+ break;
+
+
+ default:
+ dbg("sym = %lu, type = %lu, offset = %p, "
+ "contents = %p, symbol = %s",
+ symnum, (u_long)ELF_R_TYPE(rel->r_info),
+ (void *)rel->r_offset, (void *)load_ptr(where),
+ obj->strtab + obj->symtab[symnum].st_name);
+ _rtld_error("%s: Unsupported relocation type %ld "
+ "in non-PLT relocations\n",
+ obj->path, (u_long) ELF_R_TYPE(rel->r_info));
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * * Process non-PLT relocations
+ * */
+int
+reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
+ RtldLockState *lockstate)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+ SymCache *cache;
+ int r = -1;
+
+ /* The relocation for the dynamic loader has already been done. */
+ if (obj == obj_rtld)
+ return (0);
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ /* XXX not implemented */
+ return (0);
+
+ /*
+ * The dynamic loader may be called from a thread, we have
+ * limited amounts of stack available so we cannot use alloca().
+ */
+ cache = calloc(obj->dynsymcount, sizeof(SymCache));
+ /* No need to check for NULL here */
+
+ rellim = (const Elf_Rel *)((caddr_t)obj->rel + obj->relsize);
+ for (rel = obj->rel; rel < rellim; rel++) {
+ if (reloc_nonplt_object(obj, rel, cache, flags, lockstate) < 0)
+ goto done;
+ }
+ r = 0;
+done:
+ if (cache != NULL)
+ free(cache);
+ return (r);
+}
+
+/*
+ * * Process the PLT relocations.
+ * */
+int
+reloc_plt(Obj_Entry *obj)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+
+ rellim = (const Elf_Rel *)((char *)obj->pltrel +
+ obj->pltrelsize);
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ Elf_Addr *where;
+
+ assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
+
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ *where += (Elf_Addr )obj->relocbase;
+ }
+
+ return (0);
+}
+
+/*
+ * * LD_BIND_NOW was set - force relocation for all jump slots
+ * */
+int
+reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ const Obj_Entry *defobj;
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+ const Elf_Sym *def;
+ Elf_Addr *where;
+ Elf_Addr target;
+
+ rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
+ SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL) {
+ dbg("reloc_jmpslots: sym not found");
+ return (-1);
+ }
+
+ target = (Elf_Addr)(defobj->relocbase + def->st_value);
+ reloc_jmpslot(where, target, defobj, obj,
+ (const Elf_Rel *) rel);
+ }
+
+ obj->jmpslots_done = true;
+
+ return (0);
+}
+
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, int flags,
+ struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+Elf_Addr
+reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj,
+ const Obj_Entry *obj, const Elf_Rel *rel)
+{
+
+ assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT);
+
+ if (*where != target)
+ *where = target;
+
+ return target;
+}
+
+void
+allocate_initial_tls(Obj_Entry *objs)
+{
+#ifdef ARM_TP_ADDRESS
+ void **_tp = (void **)ARM_TP_ADDRESS;
+#endif
+
+ /*
+ * Fix the size of the static TLS block by using the maximum
+ * offset allocated so far and adding a bit for dynamic modules to
+ * use.
+ */
+
+ tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA;
+
+#ifdef ARM_TP_ADDRESS
+ (*_tp) = (void *) allocate_tls(objs, NULL, TLS_TCB_SIZE, 8);
+#else
+ sysarch(ARM_SET_TP, allocate_tls(objs, NULL, TLS_TCB_SIZE, 8));
+#endif
+}
+
+void *
+__tls_get_addr(tls_index* ti)
+{
+ char *p;
+#ifdef ARM_TP_ADDRESS
+ void **_tp = (void **)ARM_TP_ADDRESS;
+
+ p = tls_get_addr_common((Elf_Addr **)(*_tp), ti->ti_module, ti->ti_offset);
+#else
+ void *_tp;
+ __asm __volatile("mrc p15, 0, %0, c13, c0, 3" \
+ : "=r" (_tp));
+ p = tls_get_addr_common((Elf_Addr **)(_tp), ti->ti_module, ti->ti_offset);
+#endif
+
+ return (p);
+}
diff --git a/libexec/rtld-elf/arm/rtld_machdep.h b/libexec/rtld-elf/arm/rtld_machdep.h
new file mode 100644
index 0000000..f980de0
--- /dev/null
+++ b/libexec/rtld-elf/arm/rtld_machdep.h
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 1999, 2000 John D. Polstra.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef RTLD_MACHDEP_H
+#define RTLD_MACHDEP_H 1
+
+#include <sys/types.h>
+#include <machine/atomic.h>
+
+struct Struct_Obj_Entry;
+
+/* Return the address of the .dynamic section in the dynamic linker. */
+#define rtld_dynamic(obj) (&_DYNAMIC)
+
+Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
+ const struct Struct_Obj_Entry *defobj,
+ const struct Struct_Obj_Entry *obj,
+ const Elf_Rel *rel);
+
+#define make_function_pointer(def, defobj) \
+ ((defobj)->relocbase + (def)->st_value)
+
+#define call_initfini_pointer(obj, target) \
+ (((InitFunc)(target))())
+
+#define call_init_pointer(obj, target) \
+ (((InitArrFunc)(target))(main_argc, main_argv, environ))
+
+#define TLS_TCB_SIZE 8
+typedef struct {
+ unsigned long ti_module;
+ unsigned long ti_offset;
+} tls_index;
+
+#define round(size, align) \
+ (((size) + (align) - 1) & ~((align) - 1))
+#define calculate_first_tls_offset(size, align) \
+ round(8, align)
+#define calculate_tls_offset(prev_offset, prev_size, size, align) \
+ round(prev_offset + prev_size, align)
+#define calculate_tls_end(off, size) ((off) + (size))
+
+/*
+ * Lazy binding entry point, called via PLT.
+ */
+void _rtld_bind_start(void);
+
+extern void *__tls_get_addr(tls_index *ti);
+
+#define RTLD_DEFAULT_STACK_PF_EXEC PF_X
+#define RTLD_DEFAULT_STACK_EXEC PROT_EXEC
+
+#endif
diff --git a/libexec/rtld-elf/arm/rtld_start.S b/libexec/rtld-elf/arm/rtld_start.S
new file mode 100644
index 0000000..c482808
--- /dev/null
+++ b/libexec/rtld-elf/arm/rtld_start.S
@@ -0,0 +1,99 @@
+/* $NetBSD: rtld_start.S,v 1.7 2002/09/12 17:18:38 mycroft Exp $ */
+
+/*-
+ * Copyright (c) 1998, 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Matt Thomas and by Charles M. Hannum.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
+
+ .text
+ .align 0
+ .globl .rtld_start
+ .type .rtld_start,%function
+.rtld_start:
+ mov r6, sp /* save the stack pointer */
+ bic sp, sp, #7 /* align the stack pointer */
+ sub sp, sp, #8 /* make room for obj_main & exit proc */
+ mov r4, r0 /* save ps_strings */
+ ldr sl, .L2
+ ldr r5, .L2+4
+ ldr r0, .L2+8
+.L1:
+ add sl, pc, sl
+ ldr r5, [sl, r5]
+ ldr r0, [sl, r0]
+
+ sub r1, sl, r5 /* relocbase */
+ add r0, r1, r0 /* &_DYNAMIC */
+ bl _rtld_relocate_nonplt_self
+ mov r1, sp
+ add r2, sp, #4
+ mov r0, r6 /* load the sp the kernel gave us */
+ bl _rtld /* call the shared loader */
+ mov r3, r0 /* save entry point */
+
+ ldr r2, [sp, #0] /* r2 = cleanup */
+ ldr r1, [sp, #4] /* r1 = obj_main */
+ mov sp, r6 /* restore stack */
+ mov r0, r4 /* restore ps_strings */
+ mov pc, r3 /* jump to the entry point */
+.L2:
+ .word _GLOBAL_OFFSET_TABLE_ - (.L1+8)
+ .word _GLOBAL_OFFSET_TABLE_(GOT)
+ .word _DYNAMIC(GOT)
+
+ .align 0
+ .globl _rtld_bind_start
+ .type _rtld_bind_start,%function
+/*
+ * stack[0] = RA
+ * ip = &GOT[n+3]
+ * lr = &GOT[2]
+ */
+_rtld_bind_start:
+ stmdb sp!,{r0-r5,sl,fp}
+
+ sub r1, ip, lr /* r1 = 4 * (n + 1) */
+ sub r1, r1, #4 /* r1 = 4 * n */
+ add r1, r1, r1 /* r1 = 8 * n */
+
+ ldr r0, [lr, #-4] /* get obj ptr from GOT[1] */
+ mov r4, ip /* save GOT location */
+
+ mov r5, sp /* Save the stack pointer */
+ bic sp, sp, #7 /* Align the stack pointer */
+ bl _rtld_bind /* Call the binder */
+ mov sp, r5 /* Restore the old stack pointer */
+
+ str r0, [r4] /* save address in GOT */
+ mov ip, r0 /* save new address */
+
+ ldmia sp!,{r0-r5,sl,fp,lr} /* restore the stack */
+ mov pc, ip /* jump to the new address */
+
diff --git a/libexec/rtld-elf/debug.c b/libexec/rtld-elf/debug.c
new file mode 100644
index 0000000..8f8311c
--- /dev/null
+++ b/libexec/rtld-elf/debug.c
@@ -0,0 +1,143 @@
+/*-
+ * Copyright 1996-1998 John D. Polstra.
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Support for printing debugging messages.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "debug.h"
+#include "rtld.h"
+#include "rtld_printf.h"
+
+static const char rel_header[] =
+ " symbol name r_info r_offset st_value st_size address value\n"
+ " ------------------------------------------------------------------------------\n";
+static const char rel_format[] = " %-25s %6lx %08lx %08lx %7d %10p %08lx\n";
+
+int debug = 0;
+
+void
+debug_printf(const char *format, ...)
+{
+ if (debug) {
+ va_list ap;
+ va_start(ap, format);
+
+ rtld_vfdprintf(STDERR_FILENO, format, ap);
+ rtld_fdputchar(STDERR_FILENO, '\n');
+
+ va_end(ap);
+ }
+}
+
+void
+dump_relocations (Obj_Entry *obj0)
+{
+ Obj_Entry *obj;
+
+ for (obj = obj0; obj != NULL; obj = obj->next) {
+ dump_obj_relocations(obj);
+ }
+}
+
+void
+dump_obj_relocations (Obj_Entry *obj)
+{
+
+ rtld_printf("Object \"%s\", relocbase %p\n", obj->path, obj->relocbase);
+
+ if (obj->relsize) {
+ rtld_printf("Non-PLT Relocations: %ld\n",
+ (obj->relsize / sizeof(Elf_Rel)));
+ dump_Elf_Rel(obj, obj->rel, obj->relsize);
+ }
+
+ if (obj->relasize) {
+ rtld_printf("Non-PLT Relocations with Addend: %ld\n",
+ (obj->relasize / sizeof(Elf_Rela)));
+ dump_Elf_Rela(obj, obj->rela, obj->relasize);
+ }
+
+ if (obj->pltrelsize) {
+ rtld_printf("PLT Relocations: %ld\n",
+ (obj->pltrelsize / sizeof(Elf_Rel)));
+ dump_Elf_Rel(obj, obj->pltrel, obj->pltrelsize);
+ }
+
+ if (obj->pltrelasize) {
+ rtld_printf("PLT Relocations with Addend: %ld\n",
+ (obj->pltrelasize / sizeof(Elf_Rela)));
+ dump_Elf_Rela(obj, obj->pltrela, obj->pltrelasize);
+ }
+}
+
+void
+dump_Elf_Rel (Obj_Entry *obj, const Elf_Rel *rel0, u_long relsize)
+{
+ const Elf_Rel *rel;
+ const Elf_Rel *rellim;
+ const Elf_Sym *sym;
+ Elf_Addr *dstaddr;
+
+ rtld_putstr(rel_header);
+ rellim = (const Elf_Rel *)((const char *)rel0 + relsize);
+ for (rel = rel0; rel < rellim; rel++) {
+ dstaddr = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ sym = obj->symtab + ELF_R_SYM(rel->r_info);
+ rtld_printf(rel_format,
+ obj->strtab + sym->st_name,
+ (u_long)rel->r_info, (u_long)rel->r_offset,
+ (u_long)sym->st_value, (int)sym->st_size,
+ dstaddr, (u_long)*dstaddr);
+ }
+ return;
+}
+
+void
+dump_Elf_Rela (Obj_Entry *obj, const Elf_Rela *rela0, u_long relasize)
+{
+ const Elf_Rela *rela;
+ const Elf_Rela *relalim;
+ const Elf_Sym *sym;
+ Elf_Addr *dstaddr;
+
+ rtld_putstr(rel_header);
+ relalim = (const Elf_Rela *)((const char *)rela0 + relasize);
+ for (rela = rela0; rela < relalim; rela++) {
+ dstaddr = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ sym = obj->symtab + ELF_R_SYM(rela->r_info);
+ rtld_printf(rel_format,
+ obj->strtab + sym->st_name,
+ (u_long)rela->r_info, (u_long)rela->r_offset,
+ (u_long)sym->st_value, (int)sym->st_size,
+ dstaddr, (u_long)*dstaddr);
+ }
+ return;
+}
diff --git a/libexec/rtld-elf/debug.h b/libexec/rtld-elf/debug.h
new file mode 100644
index 0000000..98fdfb4
--- /dev/null
+++ b/libexec/rtld-elf/debug.h
@@ -0,0 +1,66 @@
+/*-
+ * Copyright 1996-1998 John D. Polstra.
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Support for printing debugging messages.
+ */
+
+#ifndef DEBUG_H
+#define DEBUG_H 1
+
+#ifndef __GNUC__
+#error "This file must be compiled with GCC"
+#endif
+
+#include <sys/cdefs.h>
+
+#include <string.h>
+#include <unistd.h>
+
+extern void debug_printf(const char *, ...) __printflike(1, 2);
+extern int debug;
+
+#ifdef DEBUG
+#define dbg(...) debug_printf(__VA_ARGS__)
+#else
+#define dbg(...) ((void) 0)
+#endif
+
+#ifndef COMPAT_32BIT
+#define _MYNAME "ld-elf.so.1"
+#else
+#define _MYNAME "ld-elf32.so.1"
+#endif
+
+#define assert(cond) ((cond) ? (void) 0 : \
+ (msg(_MYNAME ": assert failed: " __FILE__ ":" \
+ __XSTRING(__LINE__) "\n"), abort()))
+#define msg(s) write(STDOUT_FILENO, s, strlen(s))
+#define trace() msg(_MYNAME ": " __XSTRING(__LINE__) "\n")
+
+
+#endif /* DEBUG_H */
diff --git a/libexec/rtld-elf/i386/Makefile.inc b/libexec/rtld-elf/i386/Makefile.inc
new file mode 100644
index 0000000..7528dbe
--- /dev/null
+++ b/libexec/rtld-elf/i386/Makefile.inc
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+CFLAGS+= -mno-mmx -mno-3dnow -mno-sse -mno-sse2 -mno-sse3 -msoft-float
+# Uncomment this to build the dynamic linker as an executable instead
+# of a shared library:
+#LDSCRIPT= ${.CURDIR}/${MACHINE_CPUARCH}/elf_rtld.x
diff --git a/libexec/rtld-elf/i386/Symbol.map b/libexec/rtld-elf/i386/Symbol.map
new file mode 100644
index 0000000..b807310
--- /dev/null
+++ b/libexec/rtld-elf/i386/Symbol.map
@@ -0,0 +1,7 @@
+/*
+ * $FreeBSD$
+ */
+
+FBSD_1.0 {
+ ___tls_get_addr;
+};
diff --git a/libexec/rtld-elf/i386/elf_rtld.x b/libexec/rtld-elf/i386/elf_rtld.x
new file mode 100644
index 0000000..e19168c
--- /dev/null
+++ b/libexec/rtld-elf/i386/elf_rtld.x
@@ -0,0 +1,131 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386",
+ "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/obj/usr/src/tmp/usr/i386-unknown-freebsdelf/lib);
+/* Do we need any of these for elf?
+ __DYNAMIC = 0; */
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x08000000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.text :
+ { *(.rel.text) *(.rel.gnu.linkonce.t*) }
+ .rela.text :
+ { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+ .rel.data :
+ { *(.rel.data) *(.rel.gnu.linkonce.d*) }
+ .rela.data :
+ { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+ .rel.rodata :
+ { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+ .rela.rodata :
+ { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.bss : { *(.rel.bss) }
+ .rela.bss : { *(.rela.bss) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init : { *(.init) } =0x9090
+ .plt : { *(.plt) }
+ .text :
+ {
+ *(.text)
+ *(.stub)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.gnu.linkonce.t*)
+ } =0x9090
+ _etext = .;
+ PROVIDE (etext = .);
+ .fini : { *(.fini) } =0x9090
+ .rodata : { *(.rodata) *(.gnu.linkonce.r*) }
+ .rodata1 : { *(.rodata1) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(0x1000) + (. & (0x1000 - 1));
+ .data :
+ {
+ *(.data)
+ *(.gnu.linkonce.d*)
+ CONSTRUCTORS
+ }
+ .data1 : { *(.data1) }
+ .ctors :
+ {
+ *(.ctors)
+ }
+ .dtors :
+ {
+ *(.dtors)
+ }
+ .got : { *(.got.plt) *(.got) }
+ .dynamic : { *(.dynamic) }
+ /* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata : { *(.sdata) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .sbss : { *(.sbss) *(.scommon) }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ . = ALIGN(32 / 8);
+ _end = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /* These must appear regardless of . */
+}
diff --git a/libexec/rtld-elf/i386/reloc.c b/libexec/rtld-elf/i386/reloc.c
new file mode 100644
index 0000000..c1e0a39
--- /dev/null
+++ b/libexec/rtld-elf/i386/reloc.c
@@ -0,0 +1,439 @@
+/*-
+ * Copyright 1996, 1997, 1998, 1999 John D. Polstra.
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Dynamic linker for ELF.
+ *
+ * John Polstra <jdp@polstra.com>.
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <machine/segments.h>
+#include <machine/sysarch.h>
+
+#include <dlfcn.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+#include "rtld.h"
+
+/*
+ * Process the special R_386_COPY relocations in the main program. These
+ * copy data from a shared object into a region in the main program's BSS
+ * segment.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int
+do_copy_relocations(Obj_Entry *dstobj)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+
+ assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */
+
+ rellim = (const Elf_Rel *) ((caddr_t) dstobj->rel + dstobj->relsize);
+ for (rel = dstobj->rel; rel < rellim; rel++) {
+ if (ELF_R_TYPE(rel->r_info) == R_386_COPY) {
+ void *dstaddr;
+ const Elf_Sym *dstsym;
+ const char *name;
+ size_t size;
+ const void *srcaddr;
+ const Elf_Sym *srcsym;
+ const Obj_Entry *srcobj, *defobj;
+ SymLook req;
+ int res;
+
+ dstaddr = (void *) (dstobj->relocbase + rel->r_offset);
+ dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info);
+ name = dstobj->strtab + dstsym->st_name;
+ size = dstsym->st_size;
+ symlook_init(&req, name);
+ req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rel->r_info));
+ req.flags = SYMLOOK_EARLY;
+
+ for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) {
+ res = symlook_obj(&req, srcobj);
+ if (res == 0) {
+ srcsym = req.sym_out;
+ defobj = req.defobj_out;
+ break;
+ }
+ }
+
+ if (srcobj == NULL) {
+ _rtld_error("Undefined symbol \"%s\" referenced from COPY"
+ " relocation in %s", name, dstobj->path);
+ return -1;
+ }
+
+ srcaddr = (const void *) (defobj->relocbase + srcsym->st_value);
+ memcpy(dstaddr, srcaddr, size);
+ }
+ }
+
+ return 0;
+}
+
+/* Initialize the special GOT entries. */
+void
+init_pltgot(Obj_Entry *obj)
+{
+ if (obj->pltgot != NULL) {
+ obj->pltgot[1] = (Elf_Addr) obj;
+ obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start;
+ }
+}
+
+/* Process the non-PLT relocations. */
+int
+reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
+ RtldLockState *lockstate)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+ SymCache *cache;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+ Elf_Addr *where, symval, add;
+ int r;
+
+ r = -1;
+ /*
+ * The dynamic loader may be called from a thread, we have
+ * limited amounts of stack available so we cannot use alloca().
+ */
+ if (obj != obj_rtld) {
+ cache = calloc(obj->dynsymcount, sizeof(SymCache));
+ /* No need to check for NULL here */
+ } else
+ cache = NULL;
+
+ rellim = (const Elf_Rel *)((caddr_t) obj->rel + obj->relsize);
+ for (rel = obj->rel; rel < rellim; rel++) {
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_386_32:
+ case R_386_PC32:
+ case R_386_GLOB_DAT:
+ case R_386_TLS_TPOFF:
+ case R_386_TLS_TPOFF32:
+ case R_386_TLS_DTPMOD32:
+ case R_386_TLS_DTPOFF32:
+ def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
+ flags, cache, lockstate);
+ if (def == NULL)
+ goto done;
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_386_32:
+ case R_386_PC32:
+ case R_386_GLOB_DAT:
+ if ((flags & SYMLOOK_IFUNC) == 0) {
+ obj->non_plt_gnu_ifunc = true;
+ continue;
+ }
+ symval = (Elf_Addr)rtld_resolve_ifunc(
+ defobj, def);
+ break;
+ case R_386_TLS_TPOFF:
+ case R_386_TLS_TPOFF32:
+ case R_386_TLS_DTPMOD32:
+ case R_386_TLS_DTPOFF32:
+ _rtld_error("%s: IFUNC for TLS reloc",
+ obj->path);
+ goto done;
+ }
+ } else {
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ continue;
+ symval = (Elf_Addr)defobj->relocbase +
+ def->st_value;
+ }
+ break;
+ default:
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ continue;
+ break;
+ }
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_386_NONE:
+ break;
+ case R_386_32:
+ *where += symval;
+ break;
+ case R_386_PC32:
+ /*
+ * I don't think the dynamic linker should ever
+ * see this type of relocation. But the
+ * binutils-2.6 tools sometimes generate it.
+ */
+ *where += symval - (Elf_Addr)where;
+ break;
+ case R_386_COPY:
+ /*
+ * These are deferred until all other
+ * relocations have been done. All we do here
+ * is make sure that the COPY relocation is
+ * not in a shared library. They are allowed
+ * only in executable files.
+ */
+ if (!obj->mainprog) {
+ _rtld_error("%s: Unexpected R_386_COPY "
+ "relocation in shared library", obj->path);
+ goto done;
+ }
+ break;
+ case R_386_GLOB_DAT:
+ *where = symval;
+ break;
+ case R_386_RELATIVE:
+ *where += (Elf_Addr)obj->relocbase;
+ break;
+ case R_386_TLS_TPOFF:
+ case R_386_TLS_TPOFF32:
+ /*
+ * We lazily allocate offsets for static TLS
+ * as we see the first relocation that
+ * references the TLS block. This allows us to
+ * support (small amounts of) static TLS in
+ * dynamically loaded modules. If we run out
+ * of space, we generate an error.
+ */
+ if (!defobj->tls_done) {
+ if (!allocate_tls_offset((Obj_Entry*) defobj)) {
+ _rtld_error("%s: No space available "
+ "for static Thread Local Storage",
+ obj->path);
+ goto done;
+ }
+ }
+ add = (Elf_Addr)(def->st_value - defobj->tlsoffset);
+ if (ELF_R_TYPE(rel->r_info) == R_386_TLS_TPOFF)
+ *where += add;
+ else
+ *where -= add;
+ break;
+ case R_386_TLS_DTPMOD32:
+ *where += (Elf_Addr)defobj->tlsindex;
+ break;
+ case R_386_TLS_DTPOFF32:
+ *where += (Elf_Addr) def->st_value;
+ break;
+ default:
+ _rtld_error("%s: Unsupported relocation type %d"
+ " in non-PLT relocations\n", obj->path,
+ ELF_R_TYPE(rel->r_info));
+ goto done;
+ }
+ }
+ r = 0;
+done:
+ free(cache);
+ return (r);
+}
+
+/* Process the PLT relocations. */
+int
+reloc_plt(Obj_Entry *obj)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+
+ rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ Elf_Addr *where/*, val*/;
+
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_386_JMP_SLOT:
+ /* Relocate the GOT slot pointing into the PLT. */
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ *where += (Elf_Addr)obj->relocbase;
+ break;
+
+ case R_386_IRELATIVE:
+ obj->irelative = true;
+ break;
+
+ default:
+ _rtld_error("Unknown relocation type %x in PLT",
+ ELF_R_TYPE(rel->r_info));
+ return (-1);
+ }
+ }
+ return 0;
+}
+
+/* Relocate the jump slots in an object. */
+int
+reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+
+ if (obj->jmpslots_done)
+ return 0;
+ rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ Elf_Addr *where, target;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_386_JMP_SLOT:
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
+ SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL)
+ return (-1);
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ obj->gnu_ifunc = true;
+ continue;
+ }
+ target = (Elf_Addr)(defobj->relocbase + def->st_value);
+ reloc_jmpslot(where, target, defobj, obj, rel);
+ break;
+
+ case R_386_IRELATIVE:
+ break;
+
+ default:
+ _rtld_error("Unknown relocation type %x in PLT",
+ ELF_R_TYPE(rel->r_info));
+ return (-1);
+ }
+ }
+
+ obj->jmpslots_done = true;
+ return 0;
+}
+
+int
+reloc_iresolve(Obj_Entry *obj, RtldLockState *lockstate)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+ Elf_Addr *where, target;
+
+ if (!obj->irelative)
+ return (0);
+ rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_386_IRELATIVE:
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ lock_release(rtld_bind_lock, lockstate);
+ target = ((Elf_Addr (*)(void))(obj->relocbase + *where))();
+ wlock_acquire(rtld_bind_lock, lockstate);
+ *where = target;
+ break;
+ }
+ }
+ obj->irelative = false;
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+
+ if (!obj->gnu_ifunc)
+ return (0);
+ rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ Elf_Addr *where, target;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_386_JMP_SLOT:
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
+ SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL)
+ return (-1);
+ if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC)
+ continue;
+ lock_release(rtld_bind_lock, lockstate);
+ target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
+ wlock_acquire(rtld_bind_lock, lockstate);
+ reloc_jmpslot(where, target, defobj, obj, rel);
+ break;
+ }
+ }
+
+ obj->gnu_ifunc = false;
+ return (0);
+}
+
+void
+allocate_initial_tls(Obj_Entry *objs)
+{
+ void* tls;
+
+ /*
+ * Fix the size of the static TLS block by using the maximum
+ * offset allocated so far and adding a bit for dynamic modules to
+ * use.
+ */
+ tls_static_space = tls_last_offset + RTLD_STATIC_TLS_EXTRA;
+ tls = allocate_tls(objs, NULL, 3*sizeof(Elf_Addr), sizeof(Elf_Addr));
+ i386_set_gsbase(tls);
+}
+
+/* GNU ABI */
+__attribute__((__regparm__(1)))
+void *___tls_get_addr(tls_index *ti)
+{
+ Elf_Addr** segbase;
+
+ __asm __volatile("movl %%gs:0, %0" : "=r" (segbase));
+
+ return tls_get_addr_common(&segbase[1], ti->ti_module, ti->ti_offset);
+}
+
+/* Sun ABI */
+void *__tls_get_addr(tls_index *ti)
+{
+ Elf_Addr** segbase;
+
+ __asm __volatile("movl %%gs:0, %0" : "=r" (segbase));
+
+ return tls_get_addr_common(&segbase[1], ti->ti_module, ti->ti_offset);
+}
diff --git a/libexec/rtld-elf/i386/rtld_machdep.h b/libexec/rtld-elf/i386/rtld_machdep.h
new file mode 100644
index 0000000..dfbe2e1
--- /dev/null
+++ b/libexec/rtld-elf/i386/rtld_machdep.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 1999, 2000 John D. Polstra.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef RTLD_MACHDEP_H
+#define RTLD_MACHDEP_H 1
+
+#include <sys/types.h>
+#include <machine/atomic.h>
+
+struct Struct_Obj_Entry;
+
+/* Return the address of the .dynamic section in the dynamic linker. */
+#define rtld_dynamic(obj) \
+ ((const Elf_Dyn *)((obj)->relocbase + (Elf_Addr)&_DYNAMIC))
+
+/* Fixup the jump slot at "where" to transfer control to "target". */
+static inline Elf_Addr
+reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
+ const struct Struct_Obj_Entry *obj,
+ const struct Struct_Obj_Entry *refobj, const Elf_Rel *rel)
+{
+#ifdef dbg
+ dbg("reloc_jmpslot: *%p = %p", (void *)(where),
+ (void *)(target));
+#endif
+ (*(Elf_Addr *)(where) = (Elf_Addr)(target));
+ return target;
+}
+
+#define make_function_pointer(def, defobj) \
+ ((defobj)->relocbase + (def)->st_value)
+
+#define call_initfini_pointer(obj, target) \
+ (((InitFunc)(target))())
+
+#define call_init_pointer(obj, target) \
+ (((InitArrFunc)(target))(main_argc, main_argv, environ))
+
+#define round(size, align) \
+ (((size) + (align) - 1) & ~((align) - 1))
+#define calculate_first_tls_offset(size, align) \
+ round(size, align)
+#define calculate_tls_offset(prev_offset, prev_size, size, align) \
+ round((prev_offset) + (size), align)
+#define calculate_tls_end(off, size) (off)
+
+typedef struct {
+ unsigned long ti_module;
+ unsigned long ti_offset;
+} tls_index;
+
+extern void *___tls_get_addr(tls_index *ti) __attribute__((__regparm__(1)));
+extern void *__tls_get_addr(tls_index *ti);
+
+#define RTLD_DEFAULT_STACK_PF_EXEC PF_X
+#define RTLD_DEFAULT_STACK_EXEC PROT_EXEC
+
+#endif
diff --git a/libexec/rtld-elf/i386/rtld_start.S b/libexec/rtld-elf/i386/rtld_start.S
new file mode 100644
index 0000000..e7df748
--- /dev/null
+++ b/libexec/rtld-elf/i386/rtld_start.S
@@ -0,0 +1,93 @@
+/*-
+ * Copyright 1996-1998 John D. Polstra.
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+ .text
+ .align 4
+ .globl .rtld_start
+ .type .rtld_start,@function
+.rtld_start:
+ xorl %ebp,%ebp # Clear frame pointer for good form
+ movl %esp,%eax # Save initial stack pointer
+ movl %esp,%esi # Save initial stack pointer
+ andl $0xfffffff0,%esp # Align stack pointer
+ subl $16,%esp # A place to store exit procedure addr
+ movl %esp,%ebx # save address of exit proc
+ movl %esp,%ecx # construct address of obj_main
+ addl $4,%ecx
+ subl $4,%esp # Keep stack aligned
+ pushl %ecx # Pass address of obj_main
+ pushl %ebx # Pass address of exit proc
+ pushl %eax # Pass initial stack pointer to rtld
+ call _rtld@PLT # Call rtld(sp); returns entry point
+ addl $16,%esp # Remove arguments from stack
+ popl %edx # Get exit procedure address
+ movl %esi,%esp # Ignore obj_main
+/*
+ * At this point, %eax contains the entry point of the main program, and
+ * %edx contains a pointer to a termination function that should be
+ * registered with atexit(). (crt1.o registers it.)
+ */
+.globl .rtld_goto_main
+.rtld_goto_main: # This symbol exists just to make debugging easier.
+ jmp *%eax # Enter main program
+
+
+/*
+ * Binder entry point. Control is transferred to here by code in the PLT.
+ * On entry, there are two arguments on the stack. In ascending address
+ * order, they are (1) "obj", a pointer to the calling object's Obj_Entry,
+ * and (2) "reloff", the byte offset of the appropriate relocation entry
+ * in the PLT relocation table.
+ *
+ * We are careful to preserve all registers, even the caller-save
+ * registers. That is because this code may be invoked by low-level
+ * assembly-language code that is not ABI-compliant.
+ */
+ .align 4
+ .globl _rtld_bind_start
+ .type _rtld_bind_start,@function
+_rtld_bind_start:
+ pushf # Save eflags
+ pushl %eax # Save %eax
+ pushl %edx # Save %edx
+ pushl %ecx # Save %ecx
+ pushl 20(%esp) # Copy reloff argument
+ pushl 20(%esp) # Copy obj argument
+
+ call _rtld_bind@PLT # Transfer control to the binder
+ /* Now %eax contains the entry point of the function being called. */
+
+ addl $8,%esp # Discard binder arguments
+ movl %eax,20(%esp) # Store target over obj argument
+ popl %ecx # Restore %ecx
+ popl %edx # Restore %edx
+ popl %eax # Restore %eax
+ popf # Restore eflags
+ leal 4(%esp),%esp # Discard reloff, do not change eflags
+ ret # "Return" to target address
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/libexec/rtld-elf/libmap.c b/libexec/rtld-elf/libmap.c
new file mode 100644
index 0000000..691ad52
--- /dev/null
+++ b/libexec/rtld-elf/libmap.c
@@ -0,0 +1,478 @@
+/*
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/fcntl.h>
+#include <sys/mman.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "debug.h"
+#include "rtld.h"
+#include "libmap.h"
+
+#ifndef _PATH_LIBMAP_CONF
+#define _PATH_LIBMAP_CONF "/etc/libmap.conf"
+#endif
+
+#ifdef COMPAT_32BIT
+#undef _PATH_LIBMAP_CONF
+#define _PATH_LIBMAP_CONF "/etc/libmap32.conf"
+#endif
+
+TAILQ_HEAD(lm_list, lm);
+struct lm {
+ char *f;
+ char *t;
+ TAILQ_ENTRY(lm) lm_link;
+};
+
+TAILQ_HEAD(lmp_list, lmp) lmp_head = TAILQ_HEAD_INITIALIZER(lmp_head);
+struct lmp {
+ char *p;
+ enum { T_EXACT=0, T_BASENAME, T_DIRECTORY } type;
+ struct lm_list lml;
+ TAILQ_ENTRY(lmp) lmp_link;
+};
+
+static TAILQ_HEAD(lmc_list, lmc) lmc_head = TAILQ_HEAD_INITIALIZER(lmc_head);
+struct lmc {
+ char *path;
+ TAILQ_ENTRY(lmc) next;
+};
+
+static int lm_count;
+
+static void lmc_parse(char *, size_t);
+static void lmc_parse_file(char *);
+static void lmc_parse_dir(char *);
+static void lm_add(const char *, const char *, const char *);
+static void lm_free(struct lm_list *);
+static char *lml_find(struct lm_list *, const char *);
+static struct lm_list *lmp_find(const char *);
+static struct lm_list *lmp_init(char *);
+static const char *quickbasename(const char *);
+
+#define iseol(c) (((c) == '#') || ((c) == '\0') || \
+ ((c) == '\n') || ((c) == '\r'))
+
+/*
+ * Do not use ctype.h macros, which rely on working TLS. It is
+ * too early to have thread-local variables functional.
+ */
+#define rtld_isspace(c) ((c) == ' ' || (c) == '\t')
+
+int
+lm_init(char *libmap_override)
+{
+ char *p;
+
+ dbg("lm_init(\"%s\")", libmap_override);
+ TAILQ_INIT(&lmp_head);
+
+ lmc_parse_file(_PATH_LIBMAP_CONF);
+
+ if (libmap_override) {
+ /*
+ * Do some character replacement to make $LDLIBMAP look
+ * like a text file, then parse it.
+ */
+ libmap_override = xstrdup(libmap_override);
+ for (p = libmap_override; *p; p++) {
+ switch (*p) {
+ case '=':
+ *p = ' ';
+ break;
+ case ',':
+ *p = '\n';
+ break;
+ }
+ }
+ lmc_parse(libmap_override, p - libmap_override);
+ free(libmap_override);
+ }
+
+ return (lm_count == 0);
+}
+
+static void
+lmc_parse_file(char *path)
+{
+ struct lmc *p;
+ struct stat st;
+ int fd;
+ char *rpath;
+ char *lm_map;
+
+ rpath = realpath(path, NULL);
+ if (rpath == NULL)
+ return;
+
+ TAILQ_FOREACH(p, &lmc_head, next) {
+ if (strcmp(p->path, rpath) == 0) {
+ free(rpath);
+ return;
+ }
+ }
+
+ fd = open(rpath, O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ dbg("lm_parse_file: open(\"%s\") failed, %s", rpath,
+ rtld_strerror(errno));
+ free(rpath);
+ return;
+ }
+ if (fstat(fd, &st) == -1) {
+ close(fd);
+ dbg("lm_parse_file: fstat(\"%s\") failed, %s", rpath,
+ rtld_strerror(errno));
+ free(rpath);
+ return;
+ }
+ lm_map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (lm_map == (const char *)MAP_FAILED) {
+ close(fd);
+ dbg("lm_parse_file: mmap(\"%s\") failed, %s", rpath,
+ rtld_strerror(errno));
+ free(rpath);
+ return;
+ }
+ close(fd);
+ p = xmalloc(sizeof(struct lmc));
+ p->path = rpath;
+ TAILQ_INSERT_HEAD(&lmc_head, p, next);
+ lmc_parse(lm_map, st.st_size);
+ munmap(lm_map, st.st_size);
+}
+
+static void
+lmc_parse_dir(char *idir)
+{
+ DIR *d;
+ struct dirent *dp;
+ struct lmc *p;
+ char conffile[MAXPATHLEN];
+ char *ext;
+ char *rpath;
+
+ rpath = realpath(idir, NULL);
+ if (rpath == NULL)
+ return;
+
+ TAILQ_FOREACH(p, &lmc_head, next) {
+ if (strcmp(p->path, rpath) == 0) {
+ free(rpath);
+ return;
+ }
+ }
+ d = opendir(idir);
+ if (d == NULL) {
+ free(rpath);
+ return;
+ }
+
+ p = xmalloc(sizeof(struct lmc));
+ p->path = rpath;
+ TAILQ_INSERT_HEAD(&lmc_head, p, next);
+
+ while ((dp = readdir(d)) != NULL) {
+ if (dp->d_ino == 0)
+ continue;
+ if (dp->d_type != DT_REG)
+ continue;
+ ext = strrchr(dp->d_name, '.');
+ if (ext == NULL)
+ continue;
+ if (strcmp(ext, ".conf") != 0)
+ continue;
+ if (strlcpy(conffile, idir, MAXPATHLEN) >= MAXPATHLEN)
+ continue; /* too long */
+ if (strlcat(conffile, "/", MAXPATHLEN) >= MAXPATHLEN)
+ continue; /* too long */
+ if (strlcat(conffile, dp->d_name, MAXPATHLEN) >= MAXPATHLEN)
+ continue; /* too long */
+ lmc_parse_file(conffile);
+ }
+ closedir(d);
+}
+
+static void
+lmc_parse(char *lm_p, size_t lm_len)
+{
+ char *cp, *f, *t, *c, *p;
+ char prog[MAXPATHLEN];
+ /* allow includedir + full length path */
+ char line[MAXPATHLEN + 13];
+ size_t cnt;
+ int i;
+
+ cnt = 0;
+ p = NULL;
+ while (cnt < lm_len) {
+ i = 0;
+ while (cnt < lm_len && lm_p[cnt] != '\n' &&
+ i < sizeof(line) - 1) {
+ line[i] = lm_p[cnt];
+ cnt++;
+ i++;
+ }
+ line[i] = '\0';
+ while (cnt < lm_len && lm_p[cnt] != '\n')
+ cnt++;
+ /* skip over nl */
+ cnt++;
+
+ cp = &line[0];
+ t = f = c = NULL;
+
+ /* Skip over leading space */
+ while (rtld_isspace(*cp)) cp++;
+
+ /* Found a comment or EOL */
+ if (iseol(*cp)) continue;
+
+ /* Found a constraint selector */
+ if (*cp == '[') {
+ cp++;
+
+ /* Skip leading space */
+ while (rtld_isspace(*cp)) cp++;
+
+ /* Found comment, EOL or end of selector */
+ if (iseol(*cp) || *cp == ']')
+ continue;
+
+ c = cp++;
+ /* Skip to end of word */
+ while (!rtld_isspace(*cp) && !iseol(*cp) && *cp != ']')
+ cp++;
+
+ /* Skip and zero out trailing space */
+ while (rtld_isspace(*cp)) *cp++ = '\0';
+
+ /* Check if there is a closing brace */
+ if (*cp != ']') continue;
+
+ /* Terminate string if there was no trailing space */
+ *cp++ = '\0';
+
+ /*
+ * There should be nothing except whitespace or comment
+ from this point to the end of the line.
+ */
+ while(rtld_isspace(*cp)) cp++;
+ if (!iseol(*cp)) continue;
+
+ if (strlcpy(prog, c, sizeof prog) >= sizeof prog)
+ continue;
+ p = prog;
+ continue;
+ }
+
+ /* Parse the 'from' candidate. */
+ f = cp++;
+ while (!rtld_isspace(*cp) && !iseol(*cp)) cp++;
+
+ /* Skip and zero out the trailing whitespace */
+ while (rtld_isspace(*cp)) *cp++ = '\0';
+
+ /* Found a comment or EOL */
+ if (iseol(*cp)) continue;
+
+ /* Parse 'to' mapping */
+ t = cp++;
+ while (!rtld_isspace(*cp) && !iseol(*cp)) cp++;
+
+ /* Skip and zero out the trailing whitespace */
+ while (rtld_isspace(*cp)) *cp++ = '\0';
+
+ /* Should be no extra tokens at this point */
+ if (!iseol(*cp)) continue;
+
+ *cp = '\0';
+ if (strcmp(f, "includedir") == 0)
+ lmc_parse_dir(t);
+ else if (strcmp(f, "include") == 0)
+ lmc_parse_file(t);
+ else
+ lm_add(p, f, t);
+ }
+}
+
+static void
+lm_free (struct lm_list *lml)
+{
+ struct lm *lm;
+
+ dbg("%s(%p)", __func__, lml);
+
+ while (!TAILQ_EMPTY(lml)) {
+ lm = TAILQ_FIRST(lml);
+ TAILQ_REMOVE(lml, lm, lm_link);
+ free(lm->f);
+ free(lm->t);
+ free(lm);
+ }
+ return;
+}
+
+void
+lm_fini (void)
+{
+ struct lmp *lmp;
+ struct lmc *p;
+
+ dbg("%s()", __func__);
+
+ while (!TAILQ_EMPTY(&lmc_head)) {
+ p = TAILQ_FIRST(&lmc_head);
+ TAILQ_REMOVE(&lmc_head, p, next);
+ free(p->path);
+ free(p);
+ }
+
+ while (!TAILQ_EMPTY(&lmp_head)) {
+ lmp = TAILQ_FIRST(&lmp_head);
+ TAILQ_REMOVE(&lmp_head, lmp, lmp_link);
+ free(lmp->p);
+ lm_free(&lmp->lml);
+ free(lmp);
+ }
+ return;
+}
+
+static void
+lm_add (const char *p, const char *f, const char *t)
+{
+ struct lm_list *lml;
+ struct lm *lm;
+
+ if (p == NULL)
+ p = "$DEFAULT$";
+
+ dbg("%s(\"%s\", \"%s\", \"%s\")", __func__, p, f, t);
+
+ if ((lml = lmp_find(p)) == NULL)
+ lml = lmp_init(xstrdup(p));
+
+ lm = xmalloc(sizeof(struct lm));
+ lm->f = xstrdup(f);
+ lm->t = xstrdup(t);
+ TAILQ_INSERT_HEAD(lml, lm, lm_link);
+ lm_count++;
+}
+
+char *
+lm_find (const char *p, const char *f)
+{
+ struct lm_list *lml;
+ char *t;
+
+ dbg("%s(\"%s\", \"%s\")", __func__, p, f);
+
+ if (p != NULL && (lml = lmp_find(p)) != NULL) {
+ t = lml_find(lml, f);
+ if (t != NULL) {
+ /*
+ * Add a global mapping if we have
+ * a successful constrained match.
+ */
+ lm_add(NULL, f, t);
+ return (t);
+ }
+ }
+ lml = lmp_find("$DEFAULT$");
+ if (lml != NULL)
+ return (lml_find(lml, f));
+ else
+ return (NULL);
+}
+
+/* Given a libmap translation list and a library name, return the
+ replacement library, or NULL */
+char *
+lm_findn (const char *p, const char *f, const int n)
+{
+ char pathbuf[64], *s, *t;
+
+ if (n < sizeof(pathbuf) - 1)
+ s = pathbuf;
+ else
+ s = xmalloc(n + 1);
+ memcpy(s, f, n);
+ s[n] = '\0';
+ t = lm_find(p, s);
+ if (s != pathbuf)
+ free(s);
+ return (t);
+}
+
+static char *
+lml_find (struct lm_list *lmh, const char *f)
+{
+ struct lm *lm;
+
+ dbg("%s(%p, \"%s\")", __func__, lmh, f);
+
+ TAILQ_FOREACH(lm, lmh, lm_link)
+ if (strcmp(f, lm->f) == 0)
+ return (lm->t);
+ return (NULL);
+}
+
+/* Given an executable name, return a pointer to the translation list or
+ NULL if no matches */
+static struct lm_list *
+lmp_find (const char *n)
+{
+ struct lmp *lmp;
+
+ dbg("%s(\"%s\")", __func__, n);
+
+ TAILQ_FOREACH(lmp, &lmp_head, lmp_link)
+ if ((lmp->type == T_EXACT && strcmp(n, lmp->p) == 0) ||
+ (lmp->type == T_DIRECTORY && strncmp(n, lmp->p, strlen(lmp->p)) == 0) ||
+ (lmp->type == T_BASENAME && strcmp(quickbasename(n), lmp->p) == 0))
+ return (&lmp->lml);
+ return (NULL);
+}
+
+static struct lm_list *
+lmp_init (char *n)
+{
+ struct lmp *lmp;
+
+ dbg("%s(\"%s\")", __func__, n);
+
+ lmp = xmalloc(sizeof(struct lmp));
+ lmp->p = n;
+ if (n[strlen(n)-1] == '/')
+ lmp->type = T_DIRECTORY;
+ else if (strchr(n,'/') == NULL)
+ lmp->type = T_BASENAME;
+ else
+ lmp->type = T_EXACT;
+ TAILQ_INIT(&lmp->lml);
+ TAILQ_INSERT_HEAD(&lmp_head, lmp, lmp_link);
+
+ return (&lmp->lml);
+}
+
+/* libc basename is overkill. Return a pointer to the character after the
+ last /, or the original string if there are no slashes. */
+static const char *
+quickbasename (const char *path)
+{
+ const char *p = path;
+ for (; *path; path++) {
+ if (*path == '/')
+ p = path+1;
+ }
+ return (p);
+}
diff --git a/libexec/rtld-elf/libmap.h b/libexec/rtld-elf/libmap.h
new file mode 100644
index 0000000..54b4a2f
--- /dev/null
+++ b/libexec/rtld-elf/libmap.h
@@ -0,0 +1,8 @@
+/*
+ * $FreeBSD$
+ */
+
+int lm_init (char *);
+void lm_fini (void);
+char * lm_find (const char *, const char *);
+char * lm_findn (const char *, const char *, const int);
diff --git a/libexec/rtld-elf/malloc.c b/libexec/rtld-elf/malloc.c
new file mode 100644
index 0000000..9f7dbe0
--- /dev/null
+++ b/libexec/rtld-elf/malloc.c
@@ -0,0 +1,491 @@
+/*-
+ * Copyright (c) 1983 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. 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 = "from: @(#)malloc.c 5.11 (Berkeley) 2/23/91";*/
+static char *rcsid = "$FreeBSD$";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * malloc.c (Caltech) 2/21/82
+ * Chris Kingsley, kingsley@cit-20.
+ *
+ * This is a very fast storage allocator. It allocates blocks of a small
+ * number of different sizes, and keeps free lists of each size. Blocks that
+ * don't exactly fit are passed up to the next larger size. In this
+ * implementation, the available sizes are 2^n-4 (or 2^n-10) bytes long.
+ * This is designed for use in a virtual memory environment.
+ */
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <paths.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include "rtld_printf.h"
+
+static void morecore();
+static int findbucket();
+
+/*
+ * Pre-allocate mmap'ed pages
+ */
+#define NPOOLPAGES (32*1024/pagesz)
+static caddr_t pagepool_start, pagepool_end;
+static int morepages();
+
+/*
+ * The overhead on a block is at least 4 bytes. When free, this space
+ * contains a pointer to the next free block, and the bottom two bits must
+ * be zero. When in use, the first byte is set to MAGIC, and the second
+ * byte is the size index. The remaining bytes are for alignment.
+ * If range checking is enabled then a second word holds the size of the
+ * requested block, less 1, rounded up to a multiple of sizeof(RMAGIC).
+ * The order of elements is critical: ov_magic must overlay the low order
+ * bits of ov_next, and ov_magic can not be a valid ov_next bit pattern.
+ */
+union overhead {
+ union overhead *ov_next; /* when free */
+ struct {
+ u_char ovu_magic; /* magic number */
+ u_char ovu_index; /* bucket # */
+#ifdef RCHECK
+ u_short ovu_rmagic; /* range magic number */
+ u_int ovu_size; /* actual block size */
+#endif
+ } ovu;
+#define ov_magic ovu.ovu_magic
+#define ov_index ovu.ovu_index
+#define ov_rmagic ovu.ovu_rmagic
+#define ov_size ovu.ovu_size
+};
+
+#define MAGIC 0xef /* magic # on accounting info */
+#define RMAGIC 0x5555 /* magic # on range info */
+
+#ifdef RCHECK
+#define RSLOP sizeof (u_short)
+#else
+#define RSLOP 0
+#endif
+
+/*
+ * nextf[i] is the pointer to the next free block of size 2^(i+3). The
+ * smallest allocatable block is 8 bytes. The overhead information
+ * precedes the data area returned to the user.
+ */
+#define NBUCKETS 30
+static union overhead *nextf[NBUCKETS];
+
+static int pagesz; /* page size */
+static int pagebucket; /* page size bucket */
+
+#ifdef MSTATS
+/*
+ * nmalloc[i] is the difference between the number of mallocs and frees
+ * for a given block size.
+ */
+static u_int nmalloc[NBUCKETS];
+#include <stdio.h>
+#endif
+
+#if defined(MALLOC_DEBUG) || defined(RCHECK)
+#define ASSERT(p) if (!(p)) botch("p")
+#include <stdio.h>
+static void
+botch(s)
+ char *s;
+{
+ fprintf(stderr, "\r\nassertion botched: %s\r\n", s);
+ (void) fflush(stderr); /* just in case user buffered it */
+ abort();
+}
+#else
+#define ASSERT(p)
+#endif
+
+/* Debugging stuff */
+#define TRACE() rtld_printf("TRACE %s:%d\n", __FILE__, __LINE__)
+
+/*
+ * The array of supported page sizes is provided by the user, i.e., the
+ * program that calls this storage allocator. That program must initialize
+ * the array before making its first call to allocate storage. The array
+ * must contain at least one page size. The page sizes must be stored in
+ * increasing order.
+ */
+extern size_t *pagesizes;
+
+void *
+malloc(nbytes)
+ size_t nbytes;
+{
+ register union overhead *op;
+ register int bucket;
+ register long n;
+ register unsigned amt;
+
+ /*
+ * First time malloc is called, setup page size and
+ * align break pointer so all data will be page aligned.
+ */
+ if (pagesz == 0) {
+ pagesz = n = pagesizes[0];
+ if (morepages(NPOOLPAGES) == 0)
+ return NULL;
+ op = (union overhead *)(pagepool_start);
+ n = n - sizeof (*op) - ((long)op & (n - 1));
+ if (n < 0)
+ n += pagesz;
+ if (n) {
+ pagepool_start += n;
+ }
+ bucket = 0;
+ amt = 8;
+ while ((unsigned)pagesz > amt) {
+ amt <<= 1;
+ bucket++;
+ }
+ pagebucket = bucket;
+ }
+ /*
+ * Convert amount of memory requested into closest block size
+ * stored in hash buckets which satisfies request.
+ * Account for space used per block for accounting.
+ */
+ if (nbytes <= (unsigned long)(n = pagesz - sizeof (*op) - RSLOP)) {
+#ifndef RCHECK
+ amt = 8; /* size of first bucket */
+ bucket = 0;
+#else
+ amt = 16; /* size of first bucket */
+ bucket = 1;
+#endif
+ n = -(sizeof (*op) + RSLOP);
+ } else {
+ amt = pagesz;
+ bucket = pagebucket;
+ }
+ while (nbytes > amt + n) {
+ amt <<= 1;
+ if (amt == 0)
+ return (NULL);
+ bucket++;
+ }
+ /*
+ * If nothing in hash bucket right now,
+ * request more memory from the system.
+ */
+ if ((op = nextf[bucket]) == NULL) {
+ morecore(bucket);
+ if ((op = nextf[bucket]) == NULL)
+ return (NULL);
+ }
+ /* remove from linked list */
+ nextf[bucket] = op->ov_next;
+ op->ov_magic = MAGIC;
+ op->ov_index = bucket;
+#ifdef MSTATS
+ nmalloc[bucket]++;
+#endif
+#ifdef RCHECK
+ /*
+ * Record allocated size of block and
+ * bound space with magic numbers.
+ */
+ op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
+ op->ov_rmagic = RMAGIC;
+ *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
+#endif
+ return ((char *)(op + 1));
+}
+
+void *
+calloc(size_t num, size_t size)
+{
+ void *ret;
+
+ if (size != 0 && (num * size) / size != num) {
+ /* size_t overflow. */
+ return (NULL);
+ }
+
+ if ((ret = malloc(num * size)) != NULL)
+ memset(ret, 0, num * size);
+
+ return (ret);
+}
+
+/*
+ * Allocate more memory to the indicated bucket.
+ */
+static void
+morecore(bucket)
+ int bucket;
+{
+ register union overhead *op;
+ register int sz; /* size of desired block */
+ int amt; /* amount to allocate */
+ int nblks; /* how many blocks we get */
+
+ /*
+ * sbrk_size <= 0 only for big, FLUFFY, requests (about
+ * 2^30 bytes on a VAX, I think) or for a negative arg.
+ */
+ sz = 1 << (bucket + 3);
+#ifdef MALLOC_DEBUG
+ ASSERT(sz > 0);
+#else
+ if (sz <= 0)
+ return;
+#endif
+ if (sz < pagesz) {
+ amt = pagesz;
+ nblks = amt / sz;
+ } else {
+ amt = sz + pagesz;
+ nblks = 1;
+ }
+ if (amt > pagepool_end - pagepool_start)
+ if (morepages(amt/pagesz + NPOOLPAGES) == 0)
+ return;
+ op = (union overhead *)pagepool_start;
+ pagepool_start += amt;
+
+ /*
+ * Add new memory allocated to that on
+ * free list for this hash bucket.
+ */
+ nextf[bucket] = op;
+ while (--nblks > 0) {
+ op->ov_next = (union overhead *)((caddr_t)op + sz);
+ op = (union overhead *)((caddr_t)op + sz);
+ }
+}
+
+void
+free(cp)
+ void *cp;
+{
+ register int size;
+ register union overhead *op;
+
+ if (cp == NULL)
+ return;
+ op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
+#ifdef MALLOC_DEBUG
+ ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */
+#else
+ if (op->ov_magic != MAGIC)
+ return; /* sanity */
+#endif
+#ifdef RCHECK
+ ASSERT(op->ov_rmagic == RMAGIC);
+ ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC);
+#endif
+ size = op->ov_index;
+ ASSERT(size < NBUCKETS);
+ op->ov_next = nextf[size]; /* also clobbers ov_magic */
+ nextf[size] = op;
+#ifdef MSTATS
+ nmalloc[size]--;
+#endif
+}
+
+/*
+ * When a program attempts "storage compaction" as mentioned in the
+ * old malloc man page, it realloc's an already freed block. Usually
+ * this is the last block it freed; occasionally it might be farther
+ * back. We have to search all the free lists for the block in order
+ * to determine its bucket: 1st we make one pass thru the lists
+ * checking only the first block in each; if that fails we search
+ * ``realloc_srchlen'' blocks in each list for a match (the variable
+ * is extern so the caller can modify it). If that fails we just copy
+ * however many bytes was given to realloc() and hope it's not huge.
+ */
+int realloc_srchlen = 4; /* 4 should be plenty, -1 =>'s whole list */
+
+void *
+realloc(cp, nbytes)
+ void *cp;
+ size_t nbytes;
+{
+ register u_int onb;
+ register int i;
+ union overhead *op;
+ char *res;
+ int was_alloced = 0;
+
+ if (cp == NULL)
+ return (malloc(nbytes));
+ op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
+ if (op->ov_magic == MAGIC) {
+ was_alloced++;
+ i = op->ov_index;
+ } else {
+ /*
+ * Already free, doing "compaction".
+ *
+ * Search for the old block of memory on the
+ * free list. First, check the most common
+ * case (last element free'd), then (this failing)
+ * the last ``realloc_srchlen'' items free'd.
+ * If all lookups fail, then assume the size of
+ * the memory block being realloc'd is the
+ * largest possible (so that all "nbytes" of new
+ * memory are copied into). Note that this could cause
+ * a memory fault if the old area was tiny, and the moon
+ * is gibbous. However, that is very unlikely.
+ */
+ if ((i = findbucket(op, 1)) < 0 &&
+ (i = findbucket(op, realloc_srchlen)) < 0)
+ i = NBUCKETS;
+ }
+ onb = 1 << (i + 3);
+ if (onb < (u_int)pagesz)
+ onb -= sizeof (*op) + RSLOP;
+ else
+ onb += pagesz - sizeof (*op) - RSLOP;
+ /* avoid the copy if same size block */
+ if (was_alloced) {
+ if (i) {
+ i = 1 << (i + 2);
+ if (i < pagesz)
+ i -= sizeof (*op) + RSLOP;
+ else
+ i += pagesz - sizeof (*op) - RSLOP;
+ }
+ if (nbytes <= onb && nbytes > (size_t)i) {
+#ifdef RCHECK
+ op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
+ *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
+#endif
+ return(cp);
+ } else
+ free(cp);
+ }
+ if ((res = malloc(nbytes)) == NULL)
+ return (NULL);
+ if (cp != res) /* common optimization if "compacting" */
+ bcopy(cp, res, (nbytes < onb) ? nbytes : onb);
+ return (res);
+}
+
+/*
+ * Search ``srchlen'' elements of each free list for a block whose
+ * header starts at ``freep''. If srchlen is -1 search the whole list.
+ * Return bucket number, or -1 if not found.
+ */
+static int
+findbucket(freep, srchlen)
+ union overhead *freep;
+ int srchlen;
+{
+ register union overhead *p;
+ register int i, j;
+
+ for (i = 0; i < NBUCKETS; i++) {
+ j = 0;
+ for (p = nextf[i]; p && j != srchlen; p = p->ov_next) {
+ if (p == freep)
+ return (i);
+ j++;
+ }
+ }
+ return (-1);
+}
+
+#ifdef MSTATS
+/*
+ * mstats - print out statistics about malloc
+ *
+ * Prints two lines of numbers, one showing the length of the free list
+ * for each size category, the second showing the number of mallocs -
+ * frees for each size category.
+ */
+mstats(s)
+ char *s;
+{
+ register int i, j;
+ register union overhead *p;
+ int totfree = 0,
+ totused = 0;
+
+ fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s);
+ for (i = 0; i < NBUCKETS; i++) {
+ for (j = 0, p = nextf[i]; p; p = p->ov_next, j++)
+ ;
+ fprintf(stderr, " %d", j);
+ totfree += j * (1 << (i + 3));
+ }
+ fprintf(stderr, "\nused:\t");
+ for (i = 0; i < NBUCKETS; i++) {
+ fprintf(stderr, " %d", nmalloc[i]);
+ totused += nmalloc[i] * (1 << (i + 3));
+ }
+ fprintf(stderr, "\n\tTotal in use: %d, total free: %d\n",
+ totused, totfree);
+}
+#endif
+
+
+static int
+morepages(n)
+int n;
+{
+ int fd = -1;
+ int offset;
+
+ if (pagepool_end - pagepool_start > pagesz) {
+ caddr_t addr = (caddr_t)
+ (((long)pagepool_start + pagesz - 1) & ~(pagesz - 1));
+ if (munmap(addr, pagepool_end - addr) != 0)
+ rtld_fdprintf(STDERR_FILENO, "morepages: munmap %p",
+ addr);
+ }
+
+ offset = (long)pagepool_start - ((long)pagepool_start & ~(pagesz - 1));
+
+ if ((pagepool_start = mmap(0, n * pagesz,
+ PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_COPY, fd, 0)) == (caddr_t)-1) {
+ rtld_printf("Cannot map anonymous memory\n");
+ return 0;
+ }
+ pagepool_end = pagepool_start + n * pagesz;
+ pagepool_start += offset;
+
+ return n;
+}
diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c
new file mode 100644
index 0000000..2e17fbf
--- /dev/null
+++ b/libexec/rtld-elf/map_object.c
@@ -0,0 +1,449 @@
+/*-
+ * Copyright 1996-1998 John D. Polstra.
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+#include "rtld.h"
+
+static Elf_Ehdr *get_elf_header(int, const char *);
+static int convert_prot(int); /* Elf flags -> mmap protection */
+static int convert_flags(int); /* Elf flags -> mmap flags */
+
+/*
+ * Map a shared object into memory. The "fd" argument is a file descriptor,
+ * which must be open on the object and positioned at its beginning.
+ * The "path" argument is a pathname that is used only for error messages.
+ *
+ * The return value is a pointer to a newly-allocated Obj_Entry structure
+ * for the shared object. Returns NULL on failure.
+ */
+Obj_Entry *
+map_object(int fd, const char *path, const struct stat *sb)
+{
+ Obj_Entry *obj;
+ Elf_Ehdr *hdr;
+ int i;
+ Elf_Phdr *phdr;
+ Elf_Phdr *phlimit;
+ Elf_Phdr **segs;
+ int nsegs;
+ Elf_Phdr *phdyn;
+ Elf_Phdr *phinterp;
+ Elf_Phdr *phtls;
+ caddr_t mapbase;
+ size_t mapsize;
+ Elf_Addr base_vaddr;
+ Elf_Addr base_vlimit;
+ caddr_t base_addr;
+ int base_flags;
+ Elf_Off data_offset;
+ Elf_Addr data_vaddr;
+ Elf_Addr data_vlimit;
+ caddr_t data_addr;
+ int data_prot;
+ int data_flags;
+ Elf_Addr clear_vaddr;
+ caddr_t clear_addr;
+ caddr_t clear_page;
+ Elf_Addr phdr_vaddr;
+ size_t nclear, phsize;
+ Elf_Addr bss_vaddr;
+ Elf_Addr bss_vlimit;
+ caddr_t bss_addr;
+ Elf_Word stack_flags;
+ Elf_Addr relro_page;
+ size_t relro_size;
+ Elf_Addr note_start;
+ Elf_Addr note_end;
+
+ hdr = get_elf_header(fd, path);
+ if (hdr == NULL)
+ return (NULL);
+
+ /*
+ * Scan the program header entries, and save key information.
+ *
+ * We expect that the loadable segments are ordered by load address.
+ */
+ phdr = (Elf_Phdr *) ((char *)hdr + hdr->e_phoff);
+ phsize = hdr->e_phnum * sizeof (phdr[0]);
+ phlimit = phdr + hdr->e_phnum;
+ nsegs = -1;
+ phdyn = phinterp = phtls = NULL;
+ phdr_vaddr = 0;
+ relro_page = 0;
+ relro_size = 0;
+ note_start = 0;
+ note_end = 0;
+ segs = alloca(sizeof(segs[0]) * hdr->e_phnum);
+ stack_flags = RTLD_DEFAULT_STACK_PF_EXEC | PF_R | PF_W;
+ while (phdr < phlimit) {
+ switch (phdr->p_type) {
+
+ case PT_INTERP:
+ phinterp = phdr;
+ break;
+
+ case PT_LOAD:
+ segs[++nsegs] = phdr;
+ if ((segs[nsegs]->p_align & (PAGE_SIZE - 1)) != 0) {
+ _rtld_error("%s: PT_LOAD segment %d not page-aligned",
+ path, nsegs);
+ goto error;
+ }
+ break;
+
+ case PT_PHDR:
+ phdr_vaddr = phdr->p_vaddr;
+ phsize = phdr->p_memsz;
+ break;
+
+ case PT_DYNAMIC:
+ phdyn = phdr;
+ break;
+
+ case PT_TLS:
+ phtls = phdr;
+ break;
+
+ case PT_GNU_STACK:
+ stack_flags = phdr->p_flags;
+ break;
+
+ case PT_GNU_RELRO:
+ relro_page = phdr->p_vaddr;
+ relro_size = phdr->p_memsz;
+ break;
+
+ case PT_NOTE:
+ if (phdr->p_offset > PAGE_SIZE ||
+ phdr->p_offset + phdr->p_filesz > PAGE_SIZE)
+ break;
+ note_start = (Elf_Addr)(char *)hdr + phdr->p_offset;
+ note_end = note_start + phdr->p_filesz;
+ break;
+ }
+
+ ++phdr;
+ }
+ if (phdyn == NULL) {
+ _rtld_error("%s: object is not dynamically-linked", path);
+ goto error;
+ }
+
+ if (nsegs < 0) {
+ _rtld_error("%s: too few PT_LOAD segments", path);
+ goto error;
+ }
+
+ /*
+ * Map the entire address space of the object, to stake out our
+ * contiguous region, and to establish the base address for relocation.
+ */
+ base_vaddr = trunc_page(segs[0]->p_vaddr);
+ base_vlimit = round_page(segs[nsegs]->p_vaddr + segs[nsegs]->p_memsz);
+ mapsize = base_vlimit - base_vaddr;
+ base_addr = (caddr_t) base_vaddr;
+ base_flags = MAP_PRIVATE | MAP_ANON | MAP_NOCORE;
+ if (npagesizes > 1 && round_page(segs[0]->p_filesz) >= pagesizes[1])
+ base_flags |= MAP_ALIGNED_SUPER;
+
+ mapbase = mmap(base_addr, mapsize, PROT_NONE, base_flags, -1, 0);
+ if (mapbase == (caddr_t) -1) {
+ _rtld_error("%s: mmap of entire address space failed: %s",
+ path, rtld_strerror(errno));
+ goto error;
+ }
+ if (base_addr != NULL && mapbase != base_addr) {
+ _rtld_error("%s: mmap returned wrong address: wanted %p, got %p",
+ path, base_addr, mapbase);
+ goto error1;
+ }
+
+ for (i = 0; i <= nsegs; i++) {
+ /* Overlay the segment onto the proper region. */
+ data_offset = trunc_page(segs[i]->p_offset);
+ data_vaddr = trunc_page(segs[i]->p_vaddr);
+ data_vlimit = round_page(segs[i]->p_vaddr + segs[i]->p_filesz);
+ data_addr = mapbase + (data_vaddr - base_vaddr);
+ data_prot = convert_prot(segs[i]->p_flags);
+ data_flags = convert_flags(segs[i]->p_flags) | MAP_FIXED;
+ if (mmap(data_addr, data_vlimit - data_vaddr, data_prot,
+ data_flags | MAP_PREFAULT_READ, fd, data_offset) == (caddr_t) -1) {
+ _rtld_error("%s: mmap of data failed: %s", path,
+ rtld_strerror(errno));
+ goto error1;
+ }
+
+ /* Do BSS setup */
+ if (segs[i]->p_filesz != segs[i]->p_memsz) {
+
+ /* Clear any BSS in the last page of the segment. */
+ clear_vaddr = segs[i]->p_vaddr + segs[i]->p_filesz;
+ clear_addr = mapbase + (clear_vaddr - base_vaddr);
+ clear_page = mapbase + (trunc_page(clear_vaddr) - base_vaddr);
+
+ if ((nclear = data_vlimit - clear_vaddr) > 0) {
+ /* Make sure the end of the segment is writable */
+ if ((data_prot & PROT_WRITE) == 0 && -1 ==
+ mprotect(clear_page, PAGE_SIZE, data_prot|PROT_WRITE)) {
+ _rtld_error("%s: mprotect failed: %s", path,
+ rtld_strerror(errno));
+ goto error1;
+ }
+
+ memset(clear_addr, 0, nclear);
+
+ /* Reset the data protection back */
+ if ((data_prot & PROT_WRITE) == 0)
+ mprotect(clear_page, PAGE_SIZE, data_prot);
+ }
+
+ /* Overlay the BSS segment onto the proper region. */
+ bss_vaddr = data_vlimit;
+ bss_vlimit = round_page(segs[i]->p_vaddr + segs[i]->p_memsz);
+ bss_addr = mapbase + (bss_vaddr - base_vaddr);
+ if (bss_vlimit > bss_vaddr) { /* There is something to do */
+ if (mmap(bss_addr, bss_vlimit - bss_vaddr, data_prot,
+ data_flags | MAP_ANON, -1, 0) == (caddr_t)-1) {
+ _rtld_error("%s: mmap of bss failed: %s", path,
+ rtld_strerror(errno));
+ goto error1;
+ }
+ }
+ }
+
+ if (phdr_vaddr == 0 && data_offset <= hdr->e_phoff &&
+ (data_vlimit - data_vaddr + data_offset) >=
+ (hdr->e_phoff + hdr->e_phnum * sizeof (Elf_Phdr))) {
+ phdr_vaddr = data_vaddr + hdr->e_phoff - data_offset;
+ }
+ }
+
+ obj = obj_new();
+ if (sb != NULL) {
+ obj->dev = sb->st_dev;
+ obj->ino = sb->st_ino;
+ }
+ obj->mapbase = mapbase;
+ obj->mapsize = mapsize;
+ obj->textsize = round_page(segs[0]->p_vaddr + segs[0]->p_memsz) -
+ base_vaddr;
+ obj->vaddrbase = base_vaddr;
+ obj->relocbase = mapbase - base_vaddr;
+ obj->dynamic = (const Elf_Dyn *) (obj->relocbase + phdyn->p_vaddr);
+ if (hdr->e_entry != 0)
+ obj->entry = (caddr_t) (obj->relocbase + hdr->e_entry);
+ if (phdr_vaddr != 0) {
+ obj->phdr = (const Elf_Phdr *) (obj->relocbase + phdr_vaddr);
+ } else {
+ obj->phdr = malloc(phsize);
+ if (obj->phdr == NULL) {
+ obj_free(obj);
+ _rtld_error("%s: cannot allocate program header", path);
+ goto error1;
+ }
+ memcpy((char *)obj->phdr, (char *)hdr + hdr->e_phoff, phsize);
+ obj->phdr_alloc = true;
+ }
+ obj->phsize = phsize;
+ if (phinterp != NULL)
+ obj->interp = (const char *) (obj->relocbase + phinterp->p_vaddr);
+ if (phtls != NULL) {
+ tls_dtv_generation++;
+ obj->tlsindex = ++tls_max_index;
+ obj->tlssize = phtls->p_memsz;
+ obj->tlsalign = phtls->p_align;
+ obj->tlsinitsize = phtls->p_filesz;
+ obj->tlsinit = mapbase + phtls->p_vaddr;
+ }
+ obj->stack_flags = stack_flags;
+ obj->relro_page = obj->relocbase + trunc_page(relro_page);
+ obj->relro_size = round_page(relro_size);
+ if (note_start < note_end)
+ digest_notes(obj, note_start, note_end);
+ munmap(hdr, PAGE_SIZE);
+ return (obj);
+
+error1:
+ munmap(mapbase, mapsize);
+error:
+ munmap(hdr, PAGE_SIZE);
+ return (NULL);
+}
+
+static Elf_Ehdr *
+get_elf_header(int fd, const char *path)
+{
+ Elf_Ehdr *hdr;
+
+ hdr = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE | MAP_PREFAULT_READ,
+ fd, 0);
+ if (hdr == (Elf_Ehdr *)MAP_FAILED) {
+ _rtld_error("%s: read error: %s", path, rtld_strerror(errno));
+ return (NULL);
+ }
+
+ /* Make sure the file is valid */
+ if (!IS_ELF(*hdr)) {
+ _rtld_error("%s: invalid file format", path);
+ goto error;
+ }
+ if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
+ hdr->e_ident[EI_DATA] != ELF_TARG_DATA) {
+ _rtld_error("%s: unsupported file layout", path);
+ goto error;
+ }
+ if (hdr->e_ident[EI_VERSION] != EV_CURRENT ||
+ hdr->e_version != EV_CURRENT) {
+ _rtld_error("%s: unsupported file version", path);
+ goto error;
+ }
+ if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) {
+ _rtld_error("%s: unsupported file type", path);
+ goto error;
+ }
+ if (hdr->e_machine != ELF_TARG_MACH) {
+ _rtld_error("%s: unsupported machine", path);
+ goto error;
+ }
+
+ /*
+ * We rely on the program header being in the first page. This is
+ * not strictly required by the ABI specification, but it seems to
+ * always true in practice. And, it simplifies things considerably.
+ */
+ if (hdr->e_phentsize != sizeof(Elf_Phdr)) {
+ _rtld_error(
+ "%s: invalid shared object: e_phentsize != sizeof(Elf_Phdr)", path);
+ goto error;
+ }
+ if (hdr->e_phoff + hdr->e_phnum * sizeof(Elf_Phdr) >
+ (size_t)PAGE_SIZE) {
+ _rtld_error("%s: program header too large", path);
+ goto error;
+ }
+ return (hdr);
+
+error:
+ munmap(hdr, PAGE_SIZE);
+ return (NULL);
+}
+
+void
+obj_free(Obj_Entry *obj)
+{
+ Objlist_Entry *elm;
+
+ if (obj->tls_done)
+ free_tls_offset(obj);
+ while (obj->needed != NULL) {
+ Needed_Entry *needed = obj->needed;
+ obj->needed = needed->next;
+ free(needed);
+ }
+ while (!STAILQ_EMPTY(&obj->names)) {
+ Name_Entry *entry = STAILQ_FIRST(&obj->names);
+ STAILQ_REMOVE_HEAD(&obj->names, link);
+ free(entry);
+ }
+ while (!STAILQ_EMPTY(&obj->dldags)) {
+ elm = STAILQ_FIRST(&obj->dldags);
+ STAILQ_REMOVE_HEAD(&obj->dldags, link);
+ free(elm);
+ }
+ while (!STAILQ_EMPTY(&obj->dagmembers)) {
+ elm = STAILQ_FIRST(&obj->dagmembers);
+ STAILQ_REMOVE_HEAD(&obj->dagmembers, link);
+ free(elm);
+ }
+ if (obj->vertab)
+ free(obj->vertab);
+ if (obj->origin_path)
+ free(obj->origin_path);
+ if (obj->z_origin)
+ free(obj->rpath);
+ if (obj->priv)
+ free(obj->priv);
+ if (obj->path)
+ free(obj->path);
+ if (obj->phdr_alloc)
+ free((void *)obj->phdr);
+ free(obj);
+}
+
+Obj_Entry *
+obj_new(void)
+{
+ Obj_Entry *obj;
+
+ obj = CNEW(Obj_Entry);
+ STAILQ_INIT(&obj->dldags);
+ STAILQ_INIT(&obj->dagmembers);
+ STAILQ_INIT(&obj->names);
+ return obj;
+}
+
+/*
+ * Given a set of ELF protection flags, return the corresponding protection
+ * flags for MMAP.
+ */
+static int
+convert_prot(int elfflags)
+{
+ int prot = 0;
+ if (elfflags & PF_R)
+ prot |= PROT_READ;
+ if (elfflags & PF_W)
+ prot |= PROT_WRITE;
+ if (elfflags & PF_X)
+ prot |= PROT_EXEC;
+ return prot;
+}
+
+static int
+convert_flags(int elfflags)
+{
+ int flags = MAP_PRIVATE; /* All mappings are private */
+
+ /*
+ * Readonly mappings are marked "MAP_NOCORE", because they can be
+ * reconstructed by a debugger.
+ */
+ if (!(elfflags & PF_W))
+ flags |= MAP_NOCORE;
+ return flags;
+}
diff --git a/libexec/rtld-elf/mips/reloc.c b/libexec/rtld-elf/mips/reloc.c
new file mode 100644
index 0000000..4e750d7
--- /dev/null
+++ b/libexec/rtld-elf/mips/reloc.c
@@ -0,0 +1,648 @@
+/* $NetBSD: mips_reloc.c,v 1.58 2010/01/14 11:57:06 skrll Exp $ */
+
+/*
+ * Copyright 1997 Michael L. Hitch <mhitch@montana.edu>
+ * Portions copyright 2002 Charles M. Hannum <root@ihack.net>
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/endian.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include <machine/sysarch.h>
+#include <machine/tls.h>
+
+#include "debug.h"
+#include "rtld.h"
+
+#ifdef __mips_n64
+#define GOT1_MASK 0x8000000000000000UL
+#else
+#define GOT1_MASK 0x80000000UL
+#endif
+
+void
+init_pltgot(Obj_Entry *obj)
+{
+ if (obj->pltgot != NULL) {
+ obj->pltgot[0] = (Elf_Addr) &_rtld_bind_start;
+ if (obj->pltgot[1] & 0x80000000)
+ obj->pltgot[1] = (Elf_Addr) obj | GOT1_MASK;
+ }
+}
+
+int
+do_copy_relocations(Obj_Entry *dstobj)
+{
+ /* Do nothing */
+ return 0;
+}
+
+void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr);
+
+/*
+ * It is possible for the compiler to emit relocations for unaligned data.
+ * We handle this situation with these inlines.
+ */
+#ifdef __mips_n64
+/*
+ * ELF64 MIPS encodes the relocs uniquely. The first 32-bits of info contain
+ * the symbol index. The top 32-bits contain three relocation types encoded
+ * in big-endian integer with first relocation in LSB. This means for little
+ * endian we have to byte swap that integer (r_type).
+ */
+#define Elf_Sxword Elf64_Sxword
+#define ELF_R_NXTTYPE_64_P(r_type) ((((r_type) >> 8) & 0xff) == R_TYPE(64))
+#if BYTE_ORDER == LITTLE_ENDIAN
+#undef ELF_R_SYM
+#undef ELF_R_TYPE
+#define ELF_R_SYM(r_info) ((r_info) & 0xffffffff)
+#define ELF_R_TYPE(r_info) bswap32((r_info) >> 32)
+#endif
+#else
+#define ELF_R_NXTTYPE_64_P(r_type) (0)
+#define Elf_Sxword Elf32_Sword
+#endif
+
+static __inline Elf_Sxword
+load_ptr(void *where, size_t len)
+{
+ Elf_Sxword val;
+
+ if (__predict_true(((uintptr_t)where & (len - 1)) == 0)) {
+#ifdef __mips_n64
+ if (len == sizeof(Elf_Sxword))
+ return *(Elf_Sxword *)where;
+#endif
+ return *(Elf_Sword *)where;
+ }
+
+ val = 0;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ (void)memcpy(&val, where, len);
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ (void)memcpy((uint8_t *)((&val)+1) - len, where, len);
+#endif
+ return (len == sizeof(Elf_Sxword)) ? val : (Elf_Sword)val;
+}
+
+static __inline void
+store_ptr(void *where, Elf_Sxword val, size_t len)
+{
+ if (__predict_true(((uintptr_t)where & (len - 1)) == 0)) {
+#ifdef __mips_n64
+ if (len == sizeof(Elf_Sxword)) {
+ *(Elf_Sxword *)where = val;
+ return;
+ }
+#endif
+ *(Elf_Sword *)where = val;
+ return;
+ }
+#if BYTE_ORDER == LITTLE_ENDIAN
+ (void)memcpy(where, &val, len);
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ (void)memcpy(where, (const uint8_t *)((&val)+1) - len, len);
+#endif
+}
+
+void
+_rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
+{
+ const Elf_Rel *rel = 0, *rellim;
+ Elf_Addr relsz = 0;
+ const Elf_Sym *symtab = NULL, *sym;
+ Elf_Addr *where;
+ Elf_Addr *got = NULL;
+ Elf_Word local_gotno = 0, symtabno = 0, gotsym = 0;
+ size_t i;
+
+ for (; dynp->d_tag != DT_NULL; dynp++) {
+ switch (dynp->d_tag) {
+ case DT_REL:
+ rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr);
+ break;
+ case DT_RELSZ:
+ relsz = dynp->d_un.d_val;
+ break;
+ case DT_SYMTAB:
+ symtab = (const Elf_Sym *)(relocbase + dynp->d_un.d_ptr);
+ break;
+ case DT_PLTGOT:
+ got = (Elf_Addr *)(relocbase + dynp->d_un.d_ptr);
+ break;
+ case DT_MIPS_LOCAL_GOTNO:
+ local_gotno = dynp->d_un.d_val;
+ break;
+ case DT_MIPS_SYMTABNO:
+ symtabno = dynp->d_un.d_val;
+ break;
+ case DT_MIPS_GOTSYM:
+ gotsym = dynp->d_un.d_val;
+ break;
+ }
+ }
+
+ i = (got[1] & GOT1_MASK) ? 2 : 1;
+ /* Relocate the local GOT entries */
+ got += i;
+ for (; i < local_gotno; i++) {
+ *got++ += relocbase;
+ }
+
+ sym = symtab + gotsym;
+ /* Now do the global GOT entries */
+ for (i = gotsym; i < symtabno; i++) {
+ *got = sym->st_value + relocbase;
+ ++sym;
+ ++got;
+ }
+
+ rellim = (const Elf_Rel *)((caddr_t)rel + relsz);
+ for (; rel < rellim; rel++) {
+ Elf_Word r_symndx, r_type;
+
+ where = (void *)(relocbase + rel->r_offset);
+
+ r_symndx = ELF_R_SYM(rel->r_info);
+ r_type = ELF_R_TYPE(rel->r_info);
+
+ switch (r_type & 0xff) {
+ case R_TYPE(REL32): {
+ const size_t rlen =
+ ELF_R_NXTTYPE_64_P(r_type)
+ ? sizeof(Elf_Sxword)
+ : sizeof(Elf_Sword);
+ Elf_Sxword old = load_ptr(where, rlen);
+ Elf_Sxword val = old;
+#ifdef __mips_n64
+ assert(r_type == R_TYPE(REL32)
+ || r_type == (R_TYPE(REL32)|(R_TYPE(64) << 8)));
+#endif
+ assert(r_symndx < gotsym);
+ sym = symtab + r_symndx;
+ assert(ELF_ST_BIND(sym->st_info) == STB_LOCAL);
+ val += relocbase;
+ store_ptr(where, val, sizeof(Elf_Sword));
+ dbg("REL32/L(%p) %p -> %p in <self>",
+ where, (void *)old, (void *)val);
+ store_ptr(where, val, rlen);
+ break;
+ }
+
+ case R_TYPE(GPREL32):
+ case R_TYPE(NONE):
+ break;
+
+
+ default:
+ abort();
+ break;
+ }
+ }
+}
+
+Elf_Addr
+_mips_rtld_bind(Obj_Entry *obj, Elf_Size reloff)
+{
+ Elf_Addr *got = obj->pltgot;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+ Elf_Addr target;
+
+ def = find_symdef(reloff, obj, &defobj, SYMLOOK_IN_PLT, NULL,
+ NULL);
+ if (def == NULL)
+ _rtld_error("bind failed no symbol");
+
+ target = (Elf_Addr)(defobj->relocbase + def->st_value);
+ dbg("bind now/fixup at %s sym # %jd in %s --> was=%p new=%p",
+ obj->path,
+ (intmax_t)reloff, defobj->strtab + def->st_name,
+ (void *)got[obj->local_gotno + reloff - obj->gotsym],
+ (void *)target);
+ got[obj->local_gotno + reloff - obj->gotsym] = target;
+ return (Elf_Addr)target;
+}
+
+int
+reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
+ RtldLockState *lockstate)
+{
+ const Elf_Rel *rel;
+ const Elf_Rel *rellim;
+ Elf_Addr *got = obj->pltgot;
+ const Elf_Sym *sym, *def;
+ const Obj_Entry *defobj;
+ Elf_Word i;
+#ifdef SUPPORT_OLD_BROKEN_LD
+ int broken;
+#endif
+
+ /* The relocation for the dynamic loader has already been done. */
+ if (obj == obj_rtld)
+ return (0);
+
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ /* XXX not implemented */
+ return (0);
+
+#ifdef SUPPORT_OLD_BROKEN_LD
+ broken = 0;
+ sym = obj->symtab;
+ for (i = 1; i < 12; i++)
+ if (sym[i].st_info == ELF_ST_INFO(STB_LOCAL, STT_NOTYPE))
+ broken = 1;
+ dbg("%s: broken=%d", obj->path, broken);
+#endif
+
+ i = (got[1] & GOT1_MASK) ? 2 : 1;
+
+ /* Relocate the local GOT entries */
+ got += i;
+ dbg("got:%p for %d entries adding %p",
+ got, obj->local_gotno, obj->relocbase);
+ for (; i < obj->local_gotno; i++) {
+ *got += (Elf_Addr)obj->relocbase;
+ got++;
+ }
+ sym = obj->symtab + obj->gotsym;
+
+ dbg("got:%p for %d entries",
+ got, obj->symtabno);
+ /* Now do the global GOT entries */
+ for (i = obj->gotsym; i < obj->symtabno; i++) {
+ dbg(" doing got %d sym %p (%s, %lx)", i - obj->gotsym, sym,
+ sym->st_name + obj->strtab, (u_long) *got);
+
+#ifdef SUPPORT_OLD_BROKEN_LD
+ if (ELF_ST_TYPE(sym->st_info) == STT_FUNC &&
+ broken && sym->st_shndx == SHN_UNDEF) {
+ /*
+ * XXX DANGER WILL ROBINSON!
+ * You might think this is stupid, as it intentionally
+ * defeats lazy binding -- and you'd be right.
+ * Unfortunately, for lazy binding to work right, we
+ * need to a way to force the GOT slots used for
+ * function pointers to be resolved immediately. This
+ * is supposed to be done automatically by the linker,
+ * by not outputting a PLT slot and setting st_value
+ * to 0 if there are non-PLT references, but older
+ * versions of GNU ld do not do this.
+ */
+ def = find_symdef(i, obj, &defobj, flags, NULL,
+ lockstate);
+ if (def == NULL)
+ return -1;
+ *got = def->st_value + (Elf_Addr)defobj->relocbase;
+ } else
+#endif
+ if (ELF_ST_TYPE(sym->st_info) == STT_FUNC &&
+ sym->st_value != 0 && sym->st_shndx == SHN_UNDEF) {
+ /*
+ * If there are non-PLT references to the function,
+ * st_value should be 0, forcing us to resolve the
+ * address immediately.
+ *
+ * XXX DANGER WILL ROBINSON!
+ * The linker is not outputting PLT slots for calls to
+ * functions that are defined in the same shared
+ * library. This is a bug, because it can screw up
+ * link ordering rules if the symbol is defined in
+ * more than one module. For now, if there is a
+ * definition, we fail the test above and force a full
+ * symbol lookup. This means that all intra-module
+ * calls are bound immediately. - mycroft, 2003/09/24
+ */
+ *got = sym->st_value + (Elf_Addr)obj->relocbase;
+ if ((Elf_Addr)(*got) == (Elf_Addr)obj->relocbase) {
+ dbg("Warning2, i:%d maps to relocbase address:%p",
+ i, obj->relocbase);
+ }
+
+ } else if (sym->st_info == ELF_ST_INFO(STB_GLOBAL, STT_SECTION)) {
+ /* Symbols with index SHN_ABS are not relocated. */
+ if (sym->st_shndx != SHN_ABS) {
+ *got = sym->st_value +
+ (Elf_Addr)obj->relocbase;
+ if ((Elf_Addr)(*got) == (Elf_Addr)obj->relocbase) {
+ dbg("Warning3, i:%d maps to relocbase address:%p",
+ i, obj->relocbase);
+ }
+ }
+ } else {
+ /* TODO: add cache here */
+ def = find_symdef(i, obj, &defobj, flags, NULL,
+ lockstate);
+ if (def == NULL) {
+ dbg("Warning4, can't find symbole %d", i);
+ return -1;
+ }
+ *got = def->st_value + (Elf_Addr)defobj->relocbase;
+ if ((Elf_Addr)(*got) == (Elf_Addr)obj->relocbase) {
+ dbg("Warning4, i:%d maps to relocbase address:%p",
+ i, obj->relocbase);
+ dbg("via first obj symbol %s",
+ obj->strtab + obj->symtab[i].st_name);
+ dbg("found in obj %p:%s",
+ defobj, defobj->path);
+ }
+ }
+
+ dbg(" --> now %lx", (u_long) *got);
+ ++sym;
+ ++got;
+ }
+
+ got = obj->pltgot;
+ rellim = (const Elf_Rel *)((caddr_t)obj->rel + obj->relsize);
+ for (rel = obj->rel; rel < rellim; rel++) {
+ Elf_Word r_symndx, r_type;
+ void *where;
+
+ where = obj->relocbase + rel->r_offset;
+ r_symndx = ELF_R_SYM(rel->r_info);
+ r_type = ELF_R_TYPE(rel->r_info);
+
+ switch (r_type & 0xff) {
+ case R_TYPE(NONE):
+ break;
+
+ case R_TYPE(REL32): {
+ /* 32-bit PC-relative reference */
+ const size_t rlen =
+ ELF_R_NXTTYPE_64_P(r_type)
+ ? sizeof(Elf_Sxword)
+ : sizeof(Elf_Sword);
+ Elf_Sxword old = load_ptr(where, rlen);
+ Elf_Sxword val = old;
+
+ def = obj->symtab + r_symndx;
+
+ if (r_symndx >= obj->gotsym) {
+ val += got[obj->local_gotno + r_symndx - obj->gotsym];
+ dbg("REL32/G(%p) %p --> %p (%s) in %s",
+ where, (void *)old, (void *)val,
+ obj->strtab + def->st_name,
+ obj->path);
+ } else {
+ /*
+ * XXX: ABI DIFFERENCE!
+ *
+ * Old NetBSD binutils would generate shared
+ * libs with section-relative relocations being
+ * already adjusted for the start address of
+ * the section.
+ *
+ * New binutils, OTOH, generate shared libs
+ * with the same relocations being based at
+ * zero, so we need to add in the start address
+ * of the section.
+ *
+ * --rkb, Oct 6, 2001
+ */
+
+ if (def->st_info ==
+ ELF_ST_INFO(STB_LOCAL, STT_SECTION)
+#ifdef SUPPORT_OLD_BROKEN_LD
+ && !broken
+#endif
+ )
+ val += (Elf_Addr)def->st_value;
+
+ val += (Elf_Addr)obj->relocbase;
+
+ dbg("REL32/L(%p) %p -> %p (%s) in %s",
+ where, (void *)old, (void *)val,
+ obj->strtab + def->st_name, obj->path);
+ }
+ store_ptr(where, val, rlen);
+ break;
+ }
+
+#ifdef __mips_n64
+ case R_TYPE(TLS_DTPMOD64):
+#else
+ case R_TYPE(TLS_DTPMOD32):
+#endif
+ {
+
+ const size_t rlen = sizeof(Elf_Addr);
+ Elf_Addr old = load_ptr(where, rlen);
+ Elf_Addr val = old;
+
+ def = find_symdef(r_symndx, obj, &defobj, flags, NULL,
+ lockstate);
+ if (def == NULL)
+ return -1;
+
+ val += (Elf_Addr)defobj->tlsindex;
+
+ store_ptr(where, val, rlen);
+ dbg("DTPMOD %s in %s %p --> %p in %s",
+ obj->strtab + obj->symtab[r_symndx].st_name,
+ obj->path, (void *)old, (void*)val, defobj->path);
+ break;
+ }
+
+#ifdef __mips_n64
+ case R_TYPE(TLS_DTPREL64):
+#else
+ case R_TYPE(TLS_DTPREL32):
+#endif
+ {
+ const size_t rlen = sizeof(Elf_Addr);
+ Elf_Addr old = load_ptr(where, rlen);
+ Elf_Addr val = old;
+
+ def = find_symdef(r_symndx, obj, &defobj, flags, NULL,
+ lockstate);
+ if (def == NULL)
+ return -1;
+
+ if (!defobj->tls_done && allocate_tls_offset(obj))
+ return -1;
+
+ val += (Elf_Addr)def->st_value - TLS_DTP_OFFSET;
+ store_ptr(where, val, rlen);
+
+ dbg("DTPREL %s in %s %p --> %p in %s",
+ obj->strtab + obj->symtab[r_symndx].st_name,
+ obj->path, (void*)old, (void *)val, defobj->path);
+ break;
+ }
+
+#ifdef __mips_n64
+ case R_TYPE(TLS_TPREL64):
+#else
+ case R_TYPE(TLS_TPREL32):
+#endif
+ {
+ const size_t rlen = sizeof(Elf_Addr);
+ Elf_Addr old = load_ptr(where, rlen);
+ Elf_Addr val = old;
+
+ def = find_symdef(r_symndx, obj, &defobj, flags, NULL,
+ lockstate);
+
+ if (def == NULL)
+ return -1;
+
+ if (!defobj->tls_done && allocate_tls_offset(obj))
+ return -1;
+
+ val += (Elf_Addr)(def->st_value + defobj->tlsoffset
+ - TLS_TP_OFFSET - TLS_TCB_SIZE);
+ store_ptr(where, val, rlen);
+
+ dbg("TPREL %s in %s %p --> %p in %s",
+ obj->strtab + obj->symtab[r_symndx].st_name,
+ obj->path, (void*)old, (void *)val, defobj->path);
+ break;
+ }
+
+
+
+ default:
+ dbg("sym = %lu, type = %lu, offset = %p, "
+ "contents = %p, symbol = %s",
+ (u_long)r_symndx, (u_long)ELF_R_TYPE(rel->r_info),
+ (void *)rel->r_offset,
+ (void *)load_ptr(where, sizeof(Elf_Sword)),
+ obj->strtab + obj->symtab[r_symndx].st_name);
+ _rtld_error("%s: Unsupported relocation type %ld "
+ "in non-PLT relocations",
+ obj->path, (u_long) ELF_R_TYPE(rel->r_info));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Process the PLT relocations.
+ */
+int
+reloc_plt(Obj_Entry *obj)
+{
+#if 0
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+
+ dbg("reloc_plt obj:%p pltrel:%p sz:%s", obj, obj->pltrel, (int)obj->pltrelsize);
+ dbg("gottable %p num syms:%s", obj->pltgot, obj->symtabno );
+ dbg("*****************************************************");
+ rellim = (const Elf_Rel *)((char *)obj->pltrel +
+ obj->pltrelsize);
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ Elf_Addr *where;
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ *where += (Elf_Addr )obj->relocbase;
+ }
+
+#endif
+ /* PLT fixups were done above in the GOT relocation. */
+ return (0);
+}
+
+/*
+ * LD_BIND_NOW was set - force relocation for all jump slots
+ */
+int
+reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ /* Do nothing */
+ obj->jmpslots_done = true;
+
+ return (0);
+}
+
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, int flags,
+ struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+Elf_Addr
+reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj,
+ const Obj_Entry *obj, const Elf_Rel *rel)
+{
+
+ /* Do nothing */
+
+ return target;
+}
+
+void
+allocate_initial_tls(Obj_Entry *objs)
+{
+ char *tls;
+
+ /*
+ * Fix the size of the static TLS block by using the maximum
+ * offset allocated so far and adding a bit for dynamic modules to
+ * use.
+ */
+ tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA;
+
+ tls = (char *) allocate_tls(objs, NULL, TLS_TCB_SIZE, 8);
+
+ sysarch(MIPS_SET_TLS, tls);
+}
+
+void *
+__tls_get_addr(tls_index* ti)
+{
+ Elf_Addr** tls;
+ char *p;
+
+ sysarch(MIPS_GET_TLS, &tls);
+
+ p = tls_get_addr_common(tls, ti->ti_module, ti->ti_offset + TLS_DTP_OFFSET);
+
+ return (p);
+}
diff --git a/libexec/rtld-elf/mips/rtld_machdep.h b/libexec/rtld-elf/mips/rtld_machdep.h
new file mode 100644
index 0000000..befbf13
--- /dev/null
+++ b/libexec/rtld-elf/mips/rtld_machdep.h
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 1999, 2000 John D. Polstra.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef RTLD_MACHDEP_H
+#define RTLD_MACHDEP_H 1
+
+#include <sys/types.h>
+#include <machine/atomic.h>
+#include <machine/tls.h>
+
+struct Struct_Obj_Entry;
+
+/* Return the address of the .dynamic section in the dynamic linker. */
+#define rtld_dynamic(obj) (&_DYNAMIC)
+
+Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
+ const struct Struct_Obj_Entry *defobj,
+ const struct Struct_Obj_Entry *obj,
+ const Elf_Rel *rel);
+
+#define make_function_pointer(def, defobj) \
+ ((defobj)->relocbase + (def)->st_value)
+
+#define call_initfini_pointer(obj, target) \
+ (((InitFunc)(target))())
+
+#define call_init_pointer(obj, target) \
+ (((InitArrFunc)(target))(main_argc, main_argv, environ))
+
+typedef struct {
+ unsigned long ti_module;
+ unsigned long ti_offset;
+} tls_index;
+
+#define round(size, align) \
+ (((size) + (align) - 1) & ~((align) - 1))
+#define calculate_first_tls_offset(size, align) \
+ round(TLS_TCB_SIZE, align)
+#define calculate_tls_offset(prev_offset, prev_size, size, align) \
+ round(prev_offset + prev_size, align)
+#define calculate_tls_end(off, size) ((off) + (size))
+
+/*
+ * Lazy binding entry point, called via PLT.
+ */
+void _rtld_bind_start(void);
+
+extern void *__tls_get_addr(tls_index *ti);
+
+#define RTLD_DEFAULT_STACK_PF_EXEC PF_X
+#define RTLD_DEFAULT_STACK_EXEC PROT_EXEC
+
+#endif
diff --git a/libexec/rtld-elf/mips/rtld_start.S b/libexec/rtld-elf/mips/rtld_start.S
new file mode 100644
index 0000000..a35fced
--- /dev/null
+++ b/libexec/rtld-elf/mips/rtld_start.S
@@ -0,0 +1,166 @@
+/* $NetBSD: rtld_start.S,v 1.10 2009/12/14 00:41:19 matt Exp $ */
+
+/*
+ * Copyright 1997 Michael L. Hitch <mhitch@montana.edu>
+ * Portions copyright 2002 Charles M. Hannum <root@ihack.net>
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/asm.h>
+
+.globl _C_LABEL(_rtld_relocate_nonplt_self)
+.globl _C_LABEL(_rtld)
+
+#define PTR_SIZE (1<<PTR_SCALESHIFT)
+
+/*
+ * a0 stack pointer
+ * a1 rtld cleanup (filled in by dynamic loader)
+ * a2 rtld object (filled in by dynamic loader)
+ * a3 ps_strings
+ */
+LEAF(rtld_start)
+ .frame sp, 4*PTR_SIZE, ra
+ .mask 0x10090000,-PTR_SIZE
+ .set noreorder
+ SETUP_GP
+ PTR_SUBU sp, 4*PTR_SIZE /* adjust stack pointer */
+ SETUP_GP64(s4, rtld_start)
+ SAVE_GP(0)
+ /* -> 1*PTR_SIZE(sp) for atexit */
+ /* -> 2*PTR_SIZE(sp) for obj_main */
+ move s0, a0 /* save stack pointer from a0 */
+ move s3, a3 /* save ps_strings pointer */
+
+ PTR_LA a1, 1f
+ bal 1f
+ PTR_LA t0, _C_LABEL(_rtld_relocate_nonplt_self)
+1: PTR_SUBU a1, ra, a1 /* relocbase */
+ PTR_LA a0, _DYNAMIC
+ PTR_ADDU t9, a1, t0
+ jalr t9 /* _rtld_relocate_nonplt_self(dynp, relocabase) */
+ PTR_ADDU a0, a1, a0 /* &_DYNAMIC */
+
+ move a0, s0 /* sp */
+ PTR_ADDU a1, sp, 2*PTR_SIZE /* &our atexit function */
+ PTR_ADDU a2, sp, 3*PTR_SIZE /* obj_main entry */
+ PTR_SUBU sp, 4*SZREG /* ABI requires to reserve memory for 4 regs */
+ PTR_LA t9, _C_LABEL(_rtld)
+ jalr t9 /* v0 = _rtld(sp, cleanup, objp) */
+ nop
+ PTR_ADDU sp, 4*SZREG
+
+ PTR_L a1, 2*PTR_SIZE(sp) /* our atexit function */
+ PTR_L a2, 3*PTR_SIZE(sp) /* obj_main entry */
+ PTR_ADDU sp, 4*PTR_SIZE /* readjust stack */
+ move a0, s0 /* stack pointer */
+ move t9, v0
+ PTR_SUBU sp, 4*SZREG /* ABI requires to reserve memory for 4 regs */
+ move ra,t9 /* RA == PC signals backtrace routine to stop */
+ j t9 /* _start(sp, cleanup, obj); */
+ move a3, s3 /* restore ps_strings */
+END(rtld_start)
+
+#define XCALLFRAME_SIZ (12*SZREG)
+#define XCALLFRAME_RA (10*SZREG)
+#define XCALLFRAME_GP (9*SZREG)
+#define XCALLFRAME_S0 (8*SZREG)
+#define XCALLFRAME_A3 (7*SZREG)
+#define XCALLFRAME_A2 (6*SZREG)
+#define XCALLFRAME_A1 (5*SZREG)
+#define XCALLFRAME_A0 (4*SZREG)
+#if defined(__mips_n32) || defined(__mips_n64)
+#define XCALLFRAME_A7 (3*SZREG)
+#define XCALLFRAME_A6 (2*SZREG)
+#define XCALLFRAME_A5 (1*SZREG)
+#define XCALLFRAME_A4 (0*SZREG)
+#endif
+
+ .globl _rtld_bind_start
+ .ent _rtld_bind_start
+_rtld_bind_start:
+ .frame sp, XCALLFRAME_SIZ, $15
+ move v1, gp /* save old GP */
+#if defined(__mips_o32) || defined(__mips_o64)
+ PTR_ADDU t9, 8 /* modify T9 to point at .cpload */
+#endif
+ SETUP_GP
+ PTR_SUBU sp, XCALLFRAME_SIZ /* save arguments and sp value in stack */
+ SETUP_GP64(XCALLFRAME_GP, _rtld_bind_start)
+ SAVE_GP(XCALLFRAME_GP)
+#if defined(__mips_n32) || defined(__mips_n64)
+ REG_S a4, XCALLFRAME_A4(sp)
+ REG_S a5, XCALLFRAME_A5(sp)
+ REG_S a6, XCALLFRAME_A6(sp)
+ REG_S a7, XCALLFRAME_A7(sp)
+#endif
+ REG_S a0, XCALLFRAME_A0(sp)
+ REG_S a1, XCALLFRAME_A1(sp)
+ REG_S a2, XCALLFRAME_A2(sp)
+ REG_S a3, XCALLFRAME_A3(sp)
+ REG_S $15, XCALLFRAME_RA(sp) /* ra is in t7/t3 */
+ REG_S s0, XCALLFRAME_S0(sp)
+ move s0, sp
+
+ move a0, v1 /* old GP */
+ PTR_SUBU a0, a0, 0x7ff0 /* The offset of $gp from the */
+ /* beginning of the .got section: */
+ /* $gp = .got + 0x7ff0, so */
+ /* .got = $gp - 0x7ff0 */
+ /* Simple math as you can see. */
+#if defined(__mips_n64)
+ ld a0, 8(a0) /* object = pltgot[1] */
+ and a0, a0, 0x7fffffffffffffff
+#else
+ lw a0, 4(a0) /* object = pltgot[1] & 0x7fffffff */
+ and a0, a0, 0x7fffffff
+#endif
+ move a1, t8 /* symbol index */
+
+ PTR_LA t9, _C_LABEL(_mips_rtld_bind)
+ jalr t9
+ nop
+
+ move sp, s0
+ REG_L ra, XCALLFRAME_RA(sp)
+ REG_L s0, XCALLFRAME_S0(sp)
+ REG_L a0, XCALLFRAME_A0(sp)
+ REG_L a1, XCALLFRAME_A1(sp)
+ REG_L a2, XCALLFRAME_A2(sp)
+ REG_L a3, XCALLFRAME_A3(sp)
+#if defined(__mips_n32) || defined(__mips_n64)
+ REG_L a4, XCALLFRAME_A4(sp)
+ REG_L a5, XCALLFRAME_A5(sp)
+ REG_L a6, XCALLFRAME_A6(sp)
+ REG_L a7, XCALLFRAME_A7(sp)
+#endif
+ RESTORE_GP64
+ PTR_ADDU sp, XCALLFRAME_SIZ
+ move t9, v0
+ jr t9
+ nop
+END(_rtld_bind_start)
diff --git a/libexec/rtld-elf/powerpc/Makefile.inc b/libexec/rtld-elf/powerpc/Makefile.inc
new file mode 100644
index 0000000..e8c0da7
--- /dev/null
+++ b/libexec/rtld-elf/powerpc/Makefile.inc
@@ -0,0 +1 @@
+# $FreeBSD$
diff --git a/libexec/rtld-elf/powerpc/reloc.c b/libexec/rtld-elf/powerpc/reloc.c
new file mode 100644
index 0000000..89e5536
--- /dev/null
+++ b/libexec/rtld-elf/powerpc/reloc.c
@@ -0,0 +1,656 @@
+/* $NetBSD: ppc_reloc.c,v 1.10 2001/09/10 06:09:41 mycroft Exp $ */
+
+/*-
+ * Copyright (C) 1998 Tsubai Masanari
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <machine/cpu.h>
+#include <machine/atomic.h>
+#include <machine/md_var.h>
+
+#include "debug.h"
+#include "rtld.h"
+
+#define _ppc_ha(x) ((((u_int32_t)(x) & 0x8000) ? \
+ ((u_int32_t)(x) + 0x10000) : (u_int32_t)(x)) >> 16)
+#define _ppc_la(x) ((u_int32_t)(x) & 0xffff)
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+#define PLT_EXTENDED_BEGIN (1 << 13)
+#define JMPTAB_BASE(N) (18 + N*2 + ((N > PLT_EXTENDED_BEGIN) ? \
+ (N - PLT_EXTENDED_BEGIN)*2 : 0))
+
+/*
+ * Process the R_PPC_COPY relocations
+ */
+int
+do_copy_relocations(Obj_Entry *dstobj)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ /*
+ * COPY relocs are invalid outside of the main program
+ */
+ assert(dstobj->mainprog);
+
+ relalim = (const Elf_Rela *) ((caddr_t) dstobj->rela +
+ dstobj->relasize);
+ for (rela = dstobj->rela; rela < relalim; rela++) {
+ void *dstaddr;
+ const Elf_Sym *dstsym;
+ const char *name;
+ size_t size;
+ const void *srcaddr;
+ const Elf_Sym *srcsym = NULL;
+ const Obj_Entry *srcobj, *defobj;
+ SymLook req;
+ int res;
+
+ if (ELF_R_TYPE(rela->r_info) != R_PPC_COPY) {
+ continue;
+ }
+
+ dstaddr = (void *) (dstobj->relocbase + rela->r_offset);
+ dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
+ name = dstobj->strtab + dstsym->st_name;
+ size = dstsym->st_size;
+ symlook_init(&req, name);
+ req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info));
+ req.flags = SYMLOOK_EARLY;
+
+ for (srcobj = dstobj->next; srcobj != NULL;
+ srcobj = srcobj->next) {
+ res = symlook_obj(&req, srcobj);
+ if (res == 0) {
+ srcsym = req.sym_out;
+ defobj = req.defobj_out;
+ break;
+ }
+ }
+
+ if (srcobj == NULL) {
+ _rtld_error("Undefined symbol \"%s\" "
+ " referenced from COPY"
+ " relocation in %s", name, dstobj->path);
+ return (-1);
+ }
+
+ srcaddr = (const void *) (defobj->relocbase+srcsym->st_value);
+ memcpy(dstaddr, srcaddr, size);
+ dbg("copy_reloc: src=%p,dst=%p,size=%d\n",srcaddr,dstaddr,size);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Perform early relocation of the run-time linker image
+ */
+void
+reloc_non_plt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
+{
+ const Elf_Rela *rela = 0, *relalim;
+ Elf_Addr relasz = 0;
+ Elf_Addr *where;
+
+ /*
+ * Extract the rela/relasz values from the dynamic section
+ */
+ for (; dynp->d_tag != DT_NULL; dynp++) {
+ switch (dynp->d_tag) {
+ case DT_RELA:
+ rela = (const Elf_Rela *)(relocbase+dynp->d_un.d_ptr);
+ break;
+ case DT_RELASZ:
+ relasz = dynp->d_un.d_val;
+ break;
+ }
+ }
+
+ /*
+ * Relocate these values
+ */
+ relalim = (const Elf_Rela *)((caddr_t)rela + relasz);
+ for (; rela < relalim; rela++) {
+ where = (Elf_Addr *)(relocbase + rela->r_offset);
+ *where = (Elf_Addr)(relocbase + rela->r_addend);
+ }
+}
+
+
+/*
+ * Relocate a non-PLT object with addend.
+ */
+static int
+reloc_nonplt_object(Obj_Entry *obj_rtld, Obj_Entry *obj, const Elf_Rela *rela,
+ SymCache *cache, int flags, RtldLockState *lockstate)
+{
+ Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+ Elf_Addr tmp;
+
+ switch (ELF_R_TYPE(rela->r_info)) {
+
+ case R_PPC_NONE:
+ break;
+
+ case R_PPC_ADDR32: /* word32 S + A */
+ case R_PPC_GLOB_DAT: /* word32 S + A */
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ flags, cache, lockstate);
+ if (def == NULL) {
+ return (-1);
+ }
+
+ tmp = (Elf_Addr)(defobj->relocbase + def->st_value +
+ rela->r_addend);
+
+ /* Don't issue write if unnecessary; avoid COW page fault */
+ if (*where != tmp) {
+ *where = tmp;
+ }
+ break;
+
+ case R_PPC_RELATIVE: /* word32 B + A */
+ tmp = (Elf_Addr)(obj->relocbase + rela->r_addend);
+
+ /* As above, don't issue write unnecessarily */
+ if (*where != tmp) {
+ *where = tmp;
+ }
+ break;
+
+ case R_PPC_COPY:
+ /*
+ * These are deferred until all other relocations
+ * have been done. All we do here is make sure
+ * that the COPY relocation is not in a shared
+ * library. They are allowed only in executable
+ * files.
+ */
+ if (!obj->mainprog) {
+ _rtld_error("%s: Unexpected R_COPY "
+ " relocation in shared library",
+ obj->path);
+ return (-1);
+ }
+ break;
+
+ case R_PPC_JMP_SLOT:
+ /*
+ * These will be handled by the plt/jmpslot routines
+ */
+ break;
+
+ case R_PPC_DTPMOD32:
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ flags, cache, lockstate);
+
+ if (def == NULL)
+ return (-1);
+
+ *where = (Elf_Addr) defobj->tlsindex;
+
+ break;
+
+ case R_PPC_TPREL32:
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ flags, cache, lockstate);
+
+ if (def == NULL)
+ return (-1);
+
+ /*
+ * We lazily allocate offsets for static TLS as we
+ * see the first relocation that references the
+ * TLS block. This allows us to support (small
+ * amounts of) static TLS in dynamically loaded
+ * modules. If we run out of space, we generate an
+ * error.
+ */
+ if (!defobj->tls_done) {
+ if (!allocate_tls_offset((Obj_Entry*) defobj)) {
+ _rtld_error("%s: No space available for static "
+ "Thread Local Storage", obj->path);
+ return (-1);
+ }
+ }
+
+ *(Elf_Addr **)where = *where * sizeof(Elf_Addr)
+ + (Elf_Addr *)(def->st_value + rela->r_addend
+ + defobj->tlsoffset - TLS_TP_OFFSET);
+
+ break;
+
+ case R_PPC_DTPREL32:
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ flags, cache, lockstate);
+
+ if (def == NULL)
+ return (-1);
+
+ *where += (Elf_Addr)(def->st_value + rela->r_addend
+ - TLS_DTV_OFFSET);
+
+ break;
+
+ default:
+ _rtld_error("%s: Unsupported relocation type %d"
+ " in non-PLT relocations\n", obj->path,
+ ELF_R_TYPE(rela->r_info));
+ return (-1);
+ }
+ return (0);
+}
+
+
+/*
+ * Process non-PLT relocations
+ */
+int
+reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
+ RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ SymCache *cache;
+ int r = -1;
+
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ /* XXX not implemented */
+ return (0);
+
+ /*
+ * The dynamic loader may be called from a thread, we have
+ * limited amounts of stack available so we cannot use alloca().
+ */
+ if (obj != obj_rtld) {
+ cache = calloc(obj->dynsymcount, sizeof(SymCache));
+ /* No need to check for NULL here */
+ } else
+ cache = NULL;
+
+ /*
+ * From the SVR4 PPC ABI:
+ * "The PowerPC family uses only the Elf32_Rela relocation
+ * entries with explicit addends."
+ */
+ relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize);
+ for (rela = obj->rela; rela < relalim; rela++) {
+ if (reloc_nonplt_object(obj_rtld, obj, rela, cache, flags,
+ lockstate) < 0)
+ goto done;
+ }
+ r = 0;
+done:
+ if (cache != NULL)
+ free(cache);
+
+ /* Synchronize icache for text seg in case we made any changes */
+ __syncicache(obj->mapbase, obj->textsize);
+
+ return (r);
+}
+
+/*
+ * Initialise a PLT slot to the resolving trampoline
+ */
+static int
+reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela)
+{
+ Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset);
+ Elf_Addr *pltresolve, *pltlongresolve, *jmptab;
+ Elf_Addr distance;
+ int N = obj->pltrelasize / sizeof(Elf_Rela);
+ int reloff;
+
+ reloff = rela - obj->pltrela;
+
+ if (reloff < 0)
+ return (-1);
+
+ pltlongresolve = obj->pltgot + 5;
+ pltresolve = pltlongresolve + 5;
+
+ distance = (Elf_Addr)pltresolve - (Elf_Addr)(where + 1);
+
+ dbg(" reloc_plt_object: where=%p,pltres=%p,reloff=%x,distance=%x",
+ (void *)where, (void *)pltresolve, reloff, distance);
+
+ if (reloff < PLT_EXTENDED_BEGIN) {
+ /* li r11,reloff */
+ /* b pltresolve */
+ where[0] = 0x39600000 | reloff;
+ where[1] = 0x48000000 | (distance & 0x03fffffc);
+ } else {
+ jmptab = obj->pltgot + JMPTAB_BASE(N);
+ jmptab[reloff] = (u_int)pltlongresolve;
+
+ /* lis r11,jmptab[reloff]@ha */
+ /* lwzu r12,jmptab[reloff]@l(r11) */
+ /* mtctr r12 */
+ /* bctr */
+ where[0] = 0x3d600000 | _ppc_ha(&jmptab[reloff]);
+ where[1] = 0x858b0000 | _ppc_la(&jmptab[reloff]);
+ where[2] = 0x7d8903a6;
+ where[3] = 0x4e800420;
+ }
+
+
+ /*
+ * The icache will be sync'd in reloc_plt, which is called
+ * after all the slots have been updated
+ */
+
+ return (0);
+}
+
+
+/*
+ * Process the PLT relocations.
+ */
+int
+reloc_plt(Obj_Entry *obj)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ int N = obj->pltrelasize / sizeof(Elf_Rela);
+
+ if (obj->pltrelasize != 0) {
+
+ relalim = (const Elf_Rela *)((char *)obj->pltrela +
+ obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT);
+
+ if (reloc_plt_object(obj, rela) < 0) {
+ return (-1);
+ }
+ }
+ }
+
+ /*
+ * Sync the icache for the byte range represented by the
+ * trampoline routines and call slots.
+ */
+ if (obj->pltgot != NULL)
+ __syncicache(obj->pltgot, JMPTAB_BASE(N)*4);
+
+ return (0);
+}
+
+
+/*
+ * LD_BIND_NOW was set - force relocation for all jump slots
+ */
+int
+reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ const Obj_Entry *defobj;
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ const Elf_Sym *def;
+ Elf_Addr *where;
+ Elf_Addr target;
+
+ relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT);
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL) {
+ dbg("reloc_jmpslots: sym not found");
+ return (-1);
+ }
+
+ target = (Elf_Addr)(defobj->relocbase + def->st_value);
+
+#if 0
+ /* PG XXX */
+ dbg("\"%s\" in \"%s\" --> %p in \"%s\"",
+ defobj->strtab + def->st_name, basename(obj->path),
+ (void *)target, basename(defobj->path));
+#endif
+
+ reloc_jmpslot(where, target, defobj, obj,
+ (const Elf_Rel *) rela);
+ }
+
+ obj->jmpslots_done = true;
+
+ return (0);
+}
+
+
+/*
+ * Update the value of a PLT jump slot. Branch directly to the target if
+ * it is within +/- 32Mb, otherwise go indirectly via the pltcall
+ * trampoline call and jump table.
+ */
+Elf_Addr
+reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *defobj,
+ const Obj_Entry *obj, const Elf_Rel *rel)
+{
+ Elf_Addr offset;
+ const Elf_Rela *rela = (const Elf_Rela *) rel;
+
+ dbg(" reloc_jmpslot: where=%p, target=%p",
+ (void *)wherep, (void *)target);
+
+ /*
+ * At the PLT entry pointed at by `wherep', construct
+ * a direct transfer to the now fully resolved function
+ * address.
+ */
+ offset = target - (Elf_Addr)wherep;
+
+ if (abs(offset) < 32*1024*1024) { /* inside 32MB? */
+ /* b value # branch directly */
+ *wherep = 0x48000000 | (offset & 0x03fffffc);
+ __syncicache(wherep, 4);
+ } else {
+ Elf_Addr *pltcall, *jmptab;
+ int distance;
+ int N = obj->pltrelasize / sizeof(Elf_Rela);
+ int reloff = rela - obj->pltrela;
+
+ if (reloff < 0)
+ return (-1);
+
+ pltcall = obj->pltgot;
+
+ dbg(" reloc_jmpslot: indir, reloff=%x, N=%x\n",
+ reloff, N);
+
+ jmptab = obj->pltgot + JMPTAB_BASE(N);
+ jmptab[reloff] = target;
+ mb(); /* Order jmptab update before next changes */
+
+ if (reloff < PLT_EXTENDED_BEGIN) {
+ /* for extended PLT entries, we keep the old code */
+
+ distance = (Elf_Addr)pltcall - (Elf_Addr)(wherep + 1);
+
+ /* li r11,reloff */
+ /* b pltcall # use indirect pltcall routine */
+
+ /* first instruction same as before */
+ wherep[1] = 0x48000000 | (distance & 0x03fffffc);
+ __syncicache(wherep, 8);
+ }
+ }
+
+ return (target);
+}
+
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, int flags,
+ struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+/*
+ * Setup the plt glue routines.
+ */
+#define PLTCALL_SIZE 20
+#define PLTLONGRESOLVE_SIZE 20
+#define PLTRESOLVE_SIZE 24
+
+void
+init_pltgot(Obj_Entry *obj)
+{
+ Elf_Word *pltcall, *pltresolve, *pltlongresolve;
+ Elf_Word *jmptab;
+ int N = obj->pltrelasize / sizeof(Elf_Rela);
+
+ pltcall = obj->pltgot;
+
+ if (pltcall == NULL) {
+ return;
+ }
+
+ /*
+ * From the SVR4 PPC ABI:
+ *
+ * 'The first 18 words (72 bytes) of the PLT are reserved for
+ * use by the dynamic linker.
+ * ...
+ * 'If the executable or shared object requires N procedure
+ * linkage table entries, the link editor shall reserve 3*N
+ * words (12*N bytes) following the 18 reserved words. The
+ * first 2*N of these words are the procedure linkage table
+ * entries themselves. The static linker directs calls to bytes
+ * (72 + (i-1)*8), for i between 1 and N inclusive. The remaining
+ * N words (4*N bytes) are reserved for use by the dynamic linker.'
+ */
+
+ /*
+ * Copy the absolute-call assembler stub into the first part of
+ * the reserved PLT area.
+ */
+ memcpy(pltcall, _rtld_powerpc_pltcall, PLTCALL_SIZE);
+
+ /*
+ * Determine the address of the jumptable, which is the dyn-linker
+ * reserved area after the call cells. Write the absolute address
+ * of the jumptable into the absolute-call assembler code so it
+ * can determine this address.
+ */
+ jmptab = obj->pltgot + JMPTAB_BASE(N);
+ pltcall[1] |= _ppc_ha(jmptab); /* addis 11,11,jmptab@ha */
+ pltcall[2] |= _ppc_la(jmptab); /* lwz 11,jmptab@l(11) */
+
+ /*
+ * Skip down 20 bytes into the initial reserved area and copy
+ * in the standard resolving assembler call. Into this assembler,
+ * insert the absolute address of the _rtld_bind_start routine
+ * and the address of the relocation object.
+ *
+ * We place pltlongresolve first, so it can fix up its arguments
+ * and then fall through to the regular PLT resolver.
+ */
+ pltlongresolve = obj->pltgot + 5;
+
+ memcpy(pltlongresolve, _rtld_powerpc_pltlongresolve,
+ PLTLONGRESOLVE_SIZE);
+ pltlongresolve[0] |= _ppc_ha(jmptab); /* lis 12,jmptab@ha */
+ pltlongresolve[1] |= _ppc_la(jmptab); /* addi 12,12,jmptab@l */
+
+ pltresolve = pltlongresolve + PLTLONGRESOLVE_SIZE/sizeof(uint32_t);
+ memcpy(pltresolve, _rtld_powerpc_pltresolve, PLTRESOLVE_SIZE);
+ pltresolve[0] |= _ppc_ha(_rtld_bind_start);
+ pltresolve[1] |= _ppc_la(_rtld_bind_start);
+ pltresolve[3] |= _ppc_ha(obj);
+ pltresolve[4] |= _ppc_la(obj);
+
+ /*
+ * The icache will be sync'd in reloc_plt, which is called
+ * after all the slots have been updated
+ */
+}
+
+void
+allocate_initial_tls(Obj_Entry *list)
+{
+ register Elf_Addr **tp __asm__("r2");
+ Elf_Addr **_tp;
+
+ /*
+ * Fix the size of the static TLS block by using the maximum
+ * offset allocated so far and adding a bit for dynamic modules to
+ * use.
+ */
+
+ tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA;
+
+ _tp = (Elf_Addr **) ((char *) allocate_tls(list, NULL, TLS_TCB_SIZE, 8)
+ + TLS_TP_OFFSET + TLS_TCB_SIZE);
+
+ /*
+ * XXX gcc seems to ignore 'tp = _tp;'
+ */
+
+ __asm __volatile("mr %0,%1" : "=r"(tp) : "r"(_tp));
+}
+
+void*
+__tls_get_addr(tls_index* ti)
+{
+ register Elf_Addr **tp __asm__("r2");
+ char *p;
+
+ p = tls_get_addr_common((Elf_Addr**)((Elf_Addr)tp - TLS_TP_OFFSET
+ - TLS_TCB_SIZE), ti->ti_module, ti->ti_offset);
+
+ return (p + TLS_DTV_OFFSET);
+}
diff --git a/libexec/rtld-elf/powerpc/rtld_machdep.h b/libexec/rtld-elf/powerpc/rtld_machdep.h
new file mode 100644
index 0000000..1ddf1bc
--- /dev/null
+++ b/libexec/rtld-elf/powerpc/rtld_machdep.h
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 1999, 2000 John D. Polstra.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef RTLD_MACHDEP_H
+#define RTLD_MACHDEP_H 1
+
+#include <sys/types.h>
+#include <machine/atomic.h>
+
+struct Struct_Obj_Entry;
+
+/* Return the address of the .dynamic section in the dynamic linker. */
+#define rtld_dynamic(obj) (&_DYNAMIC)
+
+Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
+ const struct Struct_Obj_Entry *defobj,
+ const struct Struct_Obj_Entry *obj,
+ const Elf_Rel *rel);
+
+#define make_function_pointer(def, defobj) \
+ ((defobj)->relocbase + (def)->st_value)
+
+#define call_initfini_pointer(obj, target) \
+ (((InitFunc)(target))())
+
+#define call_init_pointer(obj, target) \
+ (((InitArrFunc)(target))(main_argc, main_argv, environ))
+
+/*
+ * Lazy binding entry point, called via PLT.
+ */
+void _rtld_bind_start(void);
+
+/*
+ * PLT functions. Not really correct prototypes, but the
+ * symbol values are needed.
+ */
+void _rtld_powerpc_pltlongresolve(void);
+void _rtld_powerpc_pltresolve(void);
+void _rtld_powerpc_pltcall(void);
+
+/*
+ * TLS
+ */
+
+#define TLS_TP_OFFSET 0x7000
+#define TLS_DTV_OFFSET 0x8000
+#define TLS_TCB_SIZE 8
+
+#define round(size, align) \
+ (((size) + (align) - 1) & ~((align) - 1))
+#define calculate_first_tls_offset(size, align) \
+ round(8, align)
+#define calculate_tls_offset(prev_offset, prev_size, size, align) \
+ round(prev_offset + prev_size, align)
+#define calculate_tls_end(off, size) ((off) + (size))
+
+typedef struct {
+ unsigned long ti_module;
+ unsigned long ti_offset;
+} tls_index;
+
+extern void *__tls_get_addr(tls_index* ti);
+
+#define RTLD_DEFAULT_STACK_PF_EXEC PF_X
+#define RTLD_DEFAULT_STACK_EXEC PROT_EXEC
+
+#endif
diff --git a/libexec/rtld-elf/powerpc/rtld_start.S b/libexec/rtld-elf/powerpc/rtld_start.S
new file mode 100644
index 0000000..28ec19b
--- /dev/null
+++ b/libexec/rtld-elf/powerpc/rtld_start.S
@@ -0,0 +1,200 @@
+/* $NetBSD: rtld_start.S,v 1.4 2001/09/26 04:06:43 mycroft Exp $ */
+
+/*-
+ * Copyright (C) 1998 Tsubai Masanari
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/asm.h>
+
+.extern _GLOBAL_OFFSET_TABLE_
+.extern _DYNAMIC
+
+_ENTRY(.rtld_start)
+ stwu %r1,-48(%r1) /* 16-byte aligned stack for reg saves +
+ exit_proc & obj _rtld args +
+ backchain & lrsave stack frame */
+ stw %r3,16(%r1) /* argc */
+ stw %r4,20(%r1) /* argv */
+ stw %r5,24(%r1) /* envp */
+/* stw %r6,28(%r1) *//* obj (always 0) */
+/* stw %r7,32(%r1) *//* cleanup (always 0) */
+ stw %r8,36(%r1) /* ps_strings */
+
+ /*
+ * Perform initial relocation of ld-elf.so. Not as easy as it
+ * sounds.
+ * - perform small forward branch to put PC into link reg
+ * - use link-time constants to determine offset to the
+ * _DYNAMIC section and the GOT. Add these to the PC to
+ * convert to absolute addresses.
+ * - sync icache to allow execution of the SVR4 ABI-specified
+ * blrl instruction preceding the GOT
+ * - Use this instruction to determine the GOT absolute address
+ * - read GOT[0], which is the SVR4 ABI-specified link-time
+ * value of _DYNAMIC. Subtract this value from the absolute
+ * value to determine the load address
+ * - call reloc_non_plt_self() to fix up ld-elf.so's relocations
+ */
+ bl 1f
+ .long _DYNAMIC-.
+ .long _GLOBAL_OFFSET_TABLE_-. /* branch lr + 4 */
+1:
+ mflr %r3 /* PC value at .long */
+ lwz %r4,4(%r3)
+ add %r4,%r4,%r3 /* &_GLOBAL_OFFSET_TABLE-4, blrl insn. */
+ dcbst %r0,%r4 /* sync i-cache with d-cache */
+ sync
+ icbi %r0,%r4
+ isync
+
+ lwz %r4,0(%r3) /* offset to _DYNAMIC */
+ add %r3,%r4,%r3 /* r3 = &_DYNAMIC, absolute value */
+
+ bl _GLOBAL_OFFSET_TABLE_@local-4
+ mflr %r4 /* &_GLOBAL_OFFSET_TABLE_, absolute value */
+ lwz %r4,0(%r4) /* linker &_DYNAMIC, from got[0] */
+ subf %r4,%r4,%r3 /* subtract to calculate relocbase */
+
+ bl reloc_non_plt_self@plt /* reloc_non_plt_self(&_DYNAMIC,base) */
+
+ /*
+ * The _rtld() function likes to see a stack layout containing
+ * { argc, argv[0], argv[1] ... argv[N], 0, env[0], ... , env[N] }
+ * Since the PowerPC stack was 16-byte aligned at exec time, the
+ * original stack layout has to be found by moving back a word
+ * from the argv pointer.
+ */
+ lwz %r4,20(%r1) /* restore argv */
+ addi %r3,%r4,-4 /* locate argc ptr, &argv[-1] */
+
+ addi %r4,%r1,8 /* &exit_proc on stack */
+ addi %r5,%r1,12 /* &obj_main on stack */
+
+ bl _rtld@plt /* &_start = _rtld(sp, &exit_proc, &obj_main)*/
+ mtlr %r3
+
+ /*
+ * Restore args, with new obj/exit proc
+ */
+ lwz %r3,16(%r1) /* argc */
+ lwz %r4,20(%r1) /* argv */
+ lwz %r5,24(%r1) /* envp */
+ lwz %r6,12(%r1) /* obj */
+ lwz %r7,8(%r1) /* exit proc */
+ lwz %r8,36(%r1) /* ps_strings */
+ addi %r1,%r1,48 /* restore original stackptr */
+
+ blrl /* _start(argc, argv, envp, obj, cleanup, ps_strings) */
+
+ li %r0,1 /* _exit() */
+ sc
+
+/*
+ * _rtld_bind_start()
+ *
+ * Call into the MI binder. This routine is reached via the PLT call cell,
+ * and then _rtld_powerpc_pltresolve().
+ * On entry, %r11 contains the index of the PLT cell, and %r12 contains
+ * a pointer to the ELF object for the file.
+ * Save all registers, call into the binder to resolve and fixup the external
+ * routine, and then transfer to the external routine on return.
+ */
+ .globl _rtld_bind
+
+_ENTRY(_rtld_bind_start)
+ stwu %r1,-160(%r1) # stack space for 29 regs + r0/lr/cr
+ stw %r0,20(%r1) # save r0
+ mflr %r0
+ stw %r0,16(%r1) # save lr
+ mfcr %r0
+ stw %r0,12(%r1) # save cr
+ stmw %r3,24(%r1) # save r3-r31
+
+ mr %r3,%r12 # obj
+ mulli %r4,%r11,12 # rela index * sizeof(Elf_Rela)
+ bl _rtld_bind@PLT # target addr = _rtld_bind(obj, reloff)
+ mtctr %r3 # move absolute target addr into ctr
+
+ lmw %r3,24(%r1) # restore r3-r31
+ lwz %r0,12(%r1) # restore cr
+ mtcr %r0
+ lwz %r0,16(%r1) # restore lr
+ mtlr %r0
+ lwz %r0,20(%r1) # restore r0
+
+ addi %r1,%r1,160 # restore stack
+ bctr # jump to target
+
+
+/*
+ * _rtld_powerpc_pltresolve()
+ *
+ * This routine is copied into the latter part of the 72-byte reserved
+ * area at the start of the PLT. The absolute address of the _rtld_bind_start
+ * routine, and the ELF object for the loaded file, are inserted into
+ * the code by the reloc.c:init_pltgot() routine.
+ * The first time an external routine is called, the PLT slot will
+ * set up %r11 to the offset of the slot, and will jump to this routine.
+ * The ELF object is shifted into %r11, and _rtld_bind_start is called
+ * to complete the binding.
+ */
+_ENTRY(_rtld_powerpc_pltlongresolve)
+ lis %r12,0 # lis 12,jmptab@ha
+ addi %r12,%r12,0 # addi 12,12,jmptab@l
+ subf %r11,%r12,%r11 # reloff
+ li %r12,2
+ srw %r11,%r11,%r12 # index = reloff/sizeof(Elf_Addr)
+_ENTRY(_rtld_powerpc_pltresolve)
+ lis %r12,0 # lis 12,_rtld_bind_start@ha
+ addi %r12,%r12,0 # addi 12,12,_rtld_bind_start@l
+ mtctr %r12
+ lis %r12,0 # lis 12,obj@ha
+ addi %r12,%r12,0 # addi 12,12,obj@l
+ bctr
+
+/*
+ * _rtld_powerpc_pltcall()
+ *
+ * This routine is copied into the 72-byte reserved area at the
+ * start of the PLT. The reloc.c:init_pltgot() routine inserts
+ * the absolute address of the jumptable.
+ * Control is transferred to this routine when the binder has
+ * located the external routine, but determined that it is > 32Mb
+ * from the PLT slot. Code is inserted into the PLT slot to set up
+ * %r11 with the jumptable index, and jump to here, where the
+ * absolute address of the external routine is loaded from the
+ * jumptable and transferred to
+ */
+_ENTRY(_rtld_powerpc_pltcall)
+ slwi %r11,%r11,2 # jmptab offset = index * 4
+ addis %r11,%r11,0 # addis 11,11,jmptab@ha
+ lwz %r11,0(%r11) # lwz 11,jmptab@l(11)
+ mtctr %r11
+ bctr # (*jmptab[index])()
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/libexec/rtld-elf/powerpc64/Makefile.inc b/libexec/rtld-elf/powerpc64/Makefile.inc
new file mode 100644
index 0000000..e8c0da7
--- /dev/null
+++ b/libexec/rtld-elf/powerpc64/Makefile.inc
@@ -0,0 +1 @@
+# $FreeBSD$
diff --git a/libexec/rtld-elf/powerpc64/reloc.c b/libexec/rtld-elf/powerpc64/reloc.c
new file mode 100644
index 0000000..65db28f
--- /dev/null
+++ b/libexec/rtld-elf/powerpc64/reloc.c
@@ -0,0 +1,520 @@
+/* $NetBSD: ppc_reloc.c,v 1.10 2001/09/10 06:09:41 mycroft Exp $ */
+
+/*-
+ * Copyright (C) 1998 Tsubai Masanari
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <machine/cpu.h>
+#include <machine/md_var.h>
+
+#include "debug.h"
+#include "rtld.h"
+
+struct funcdesc {
+ Elf_Addr addr;
+ Elf_Addr toc;
+ Elf_Addr env;
+};
+
+/*
+ * Process the R_PPC_COPY relocations
+ */
+int
+do_copy_relocations(Obj_Entry *dstobj)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ /*
+ * COPY relocs are invalid outside of the main program
+ */
+ assert(dstobj->mainprog);
+
+ relalim = (const Elf_Rela *) ((caddr_t) dstobj->rela +
+ dstobj->relasize);
+ for (rela = dstobj->rela; rela < relalim; rela++) {
+ void *dstaddr;
+ const Elf_Sym *dstsym;
+ const char *name;
+ size_t size;
+ const void *srcaddr;
+ const Elf_Sym *srcsym = NULL;
+ const Obj_Entry *srcobj, *defobj;
+ SymLook req;
+ int res;
+
+ if (ELF_R_TYPE(rela->r_info) != R_PPC_COPY) {
+ continue;
+ }
+
+ dstaddr = (void *) (dstobj->relocbase + rela->r_offset);
+ dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
+ name = dstobj->strtab + dstsym->st_name;
+ size = dstsym->st_size;
+ symlook_init(&req, name);
+ req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info));
+ req.flags = SYMLOOK_EARLY;
+
+ for (srcobj = dstobj->next; srcobj != NULL;
+ srcobj = srcobj->next) {
+ res = symlook_obj(&req, srcobj);
+ if (res == 0) {
+ srcsym = req.sym_out;
+ defobj = req.defobj_out;
+ break;
+ }
+ }
+
+ if (srcobj == NULL) {
+ _rtld_error("Undefined symbol \"%s\" "
+ " referenced from COPY"
+ " relocation in %s", name, dstobj->path);
+ return (-1);
+ }
+
+ srcaddr = (const void *) (defobj->relocbase+srcsym->st_value);
+ memcpy(dstaddr, srcaddr, size);
+ dbg("copy_reloc: src=%p,dst=%p,size=%zd\n",srcaddr,dstaddr,size);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Perform early relocation of the run-time linker image
+ */
+void
+reloc_non_plt_self(Elf_Dyn *dynp, Elf_Addr relocbase)
+{
+ const Elf_Rela *rela = 0, *relalim;
+ Elf_Addr relasz = 0;
+ Elf_Addr *where;
+
+ /*
+ * Extract the rela/relasz values from the dynamic section
+ */
+ for (; dynp->d_tag != DT_NULL; dynp++) {
+ switch (dynp->d_tag) {
+ case DT_RELA:
+ rela = (const Elf_Rela *)(relocbase+dynp->d_un.d_ptr);
+ break;
+ case DT_RELASZ:
+ relasz = dynp->d_un.d_val;
+ break;
+ }
+ }
+
+ /*
+ * Relocate these values
+ */
+ relalim = (const Elf_Rela *)((caddr_t)rela + relasz);
+ for (; rela < relalim; rela++) {
+ where = (Elf_Addr *)(relocbase + rela->r_offset);
+ *where = (Elf_Addr)(relocbase + rela->r_addend);
+ }
+}
+
+
+/*
+ * Relocate a non-PLT object with addend.
+ */
+static int
+reloc_nonplt_object(Obj_Entry *obj_rtld, Obj_Entry *obj, const Elf_Rela *rela,
+ SymCache *cache, int flags, RtldLockState *lockstate)
+{
+ Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+ Elf_Addr tmp;
+
+ switch (ELF_R_TYPE(rela->r_info)) {
+
+ case R_PPC_NONE:
+ break;
+
+ case R_PPC64_UADDR64: /* doubleword64 S + A */
+ case R_PPC64_ADDR64:
+ case R_PPC_GLOB_DAT:
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ flags, cache, lockstate);
+ if (def == NULL) {
+ return (-1);
+ }
+
+ tmp = (Elf_Addr)(defobj->relocbase + def->st_value +
+ rela->r_addend);
+
+ /* Don't issue write if unnecessary; avoid COW page fault */
+ if (*where != tmp) {
+ *where = tmp;
+ }
+ break;
+
+ case R_PPC_RELATIVE: /* doubleword64 B + A */
+ tmp = (Elf_Addr)(obj->relocbase + rela->r_addend);
+
+ /* As above, don't issue write unnecessarily */
+ if (*where != tmp) {
+ *where = tmp;
+ }
+ break;
+
+ case R_PPC_COPY:
+ /*
+ * These are deferred until all other relocations
+ * have been done. All we do here is make sure
+ * that the COPY relocation is not in a shared
+ * library. They are allowed only in executable
+ * files.
+ */
+ if (!obj->mainprog) {
+ _rtld_error("%s: Unexpected R_COPY "
+ " relocation in shared library",
+ obj->path);
+ return (-1);
+ }
+ break;
+
+ case R_PPC_JMP_SLOT:
+ /*
+ * These will be handled by the plt/jmpslot routines
+ */
+ break;
+
+ case R_PPC64_DTPMOD64:
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ flags, cache, lockstate);
+
+ if (def == NULL)
+ return (-1);
+
+ *where = (Elf_Addr) defobj->tlsindex;
+
+ break;
+
+ case R_PPC64_TPREL64:
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ flags, cache, lockstate);
+
+ if (def == NULL)
+ return (-1);
+
+ /*
+ * We lazily allocate offsets for static TLS as we
+ * see the first relocation that references the
+ * TLS block. This allows us to support (small
+ * amounts of) static TLS in dynamically loaded
+ * modules. If we run out of space, we generate an
+ * error.
+ */
+ if (!defobj->tls_done) {
+ if (!allocate_tls_offset((Obj_Entry*) defobj)) {
+ _rtld_error("%s: No space available for static "
+ "Thread Local Storage", obj->path);
+ return (-1);
+ }
+ }
+
+ *(Elf_Addr **)where = *where * sizeof(Elf_Addr)
+ + (Elf_Addr *)(def->st_value + rela->r_addend
+ + defobj->tlsoffset - TLS_TP_OFFSET);
+
+ break;
+
+ case R_PPC64_DTPREL64:
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ flags, cache, lockstate);
+
+ if (def == NULL)
+ return (-1);
+
+ *where += (Elf_Addr)(def->st_value + rela->r_addend
+ - TLS_DTV_OFFSET);
+
+ break;
+
+ default:
+ _rtld_error("%s: Unsupported relocation type %ld"
+ " in non-PLT relocations\n", obj->path,
+ ELF_R_TYPE(rela->r_info));
+ return (-1);
+ }
+ return (0);
+}
+
+
+/*
+ * Process non-PLT relocations
+ */
+int
+reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
+ RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ SymCache *cache;
+ int bytes = obj->dynsymcount * sizeof(SymCache);
+ int r = -1;
+
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ /* XXX not implemented */
+ return (0);
+
+ /*
+ * The dynamic loader may be called from a thread, we have
+ * limited amounts of stack available so we cannot use alloca().
+ */
+ if (obj != obj_rtld) {
+ cache = mmap(NULL, bytes, PROT_READ|PROT_WRITE, MAP_ANON,
+ -1, 0);
+ if (cache == MAP_FAILED)
+ cache = NULL;
+ } else
+ cache = NULL;
+
+ /*
+ * From the SVR4 PPC ABI:
+ * "The PowerPC family uses only the Elf32_Rela relocation
+ * entries with explicit addends."
+ */
+ relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize);
+ for (rela = obj->rela; rela < relalim; rela++) {
+ if (reloc_nonplt_object(obj_rtld, obj, rela, cache, flags,
+ lockstate) < 0)
+ goto done;
+ }
+ r = 0;
+done:
+ if (cache)
+ munmap(cache, bytes);
+
+ /* Synchronize icache for text seg in case we made any changes */
+ __syncicache(obj->mapbase, obj->textsize);
+
+ return (r);
+}
+
+
+/*
+ * Initialise a PLT slot to the resolving trampoline
+ */
+static int
+reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela)
+{
+ Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ Elf_Addr *glink;
+ long reloff;
+
+ reloff = rela - obj->pltrela;
+
+ if (obj->priv == NULL)
+ obj->priv = xmalloc(obj->pltrelasize);
+ glink = obj->priv + reloff*sizeof(Elf_Addr)*2;
+
+ dbg(" reloc_plt_object: where=%p,reloff=%lx,glink=%p", (void *)where, reloff, glink);
+
+ memcpy(where, _rtld_bind_start, sizeof(struct funcdesc));
+ ((struct funcdesc *)(where))->env = (Elf_Addr)glink;
+ *(glink++) = (Elf_Addr)obj;
+ *(glink++) = reloff*sizeof(Elf_Rela);
+
+ return (0);
+}
+
+
+/*
+ * Process the PLT relocations.
+ */
+int
+reloc_plt(Obj_Entry *obj)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ if (obj->pltrelasize != 0) {
+ relalim = (const Elf_Rela *)((char *)obj->pltrela +
+ obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT);
+
+ if (reloc_plt_object(obj, rela) < 0) {
+ return (-1);
+ }
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * LD_BIND_NOW was set - force relocation for all jump slots
+ */
+int
+reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ const Obj_Entry *defobj;
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ const Elf_Sym *def;
+ Elf_Addr *where;
+ Elf_Addr target;
+
+ relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT);
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL) {
+ dbg("reloc_jmpslots: sym not found");
+ return (-1);
+ }
+
+ target = (Elf_Addr)(defobj->relocbase + def->st_value);
+
+#if 0
+ /* PG XXX */
+ dbg("\"%s\" in \"%s\" --> %p in \"%s\"",
+ defobj->strtab + def->st_name, basename(obj->path),
+ (void *)target, basename(defobj->path));
+#endif
+
+ if (def == &sym_zero) {
+ /* Zero undefined weak symbols */
+ bzero(where, sizeof(struct funcdesc));
+ } else {
+ reloc_jmpslot(where, target, defobj, obj,
+ (const Elf_Rel *) rela);
+ }
+ }
+
+ obj->jmpslots_done = true;
+
+ return (0);
+}
+
+
+/*
+ * Update the value of a PLT jump slot.
+ */
+Elf_Addr
+reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *defobj,
+ const Obj_Entry *obj, const Elf_Rel *rel)
+{
+ dbg(" reloc_jmpslot: where=%p, target=%p (%#lx + %#lx)",
+ (void *)wherep, (void *)target, *(Elf_Addr *)target,
+ (Elf_Addr)defobj->relocbase);
+
+ /*
+ * At the PLT entry pointed at by `wherep', construct
+ * a direct transfer to the now fully resolved function
+ * address.
+ */
+
+ memcpy(wherep, (void *)target, sizeof(struct funcdesc));
+ if (((struct funcdesc *)(wherep))->addr < (Elf_Addr)defobj->relocbase) {
+ /*
+ * XXX: It is possible (e.g. LD_BIND_NOW) that the function
+ * descriptor we are copying has not yet been relocated.
+ * If this happens, fix it.
+ */
+
+ ((struct funcdesc *)(wherep))->addr +=
+ (Elf_Addr)defobj->relocbase;
+ ((struct funcdesc *)(wherep))->toc +=
+ (Elf_Addr)defobj->relocbase;
+ }
+
+ __asm __volatile("dcbst 0,%0; sync" :: "r"(wherep) : "memory");
+
+ return (target);
+}
+
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, int flags,
+ struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+void
+init_pltgot(Obj_Entry *obj)
+{
+}
+
+void
+allocate_initial_tls(Obj_Entry *list)
+{
+ Elf_Addr **tp;
+
+ /*
+ * Fix the size of the static TLS block by using the maximum
+ * offset allocated so far and adding a bit for dynamic modules to
+ * use.
+ */
+
+ tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA;
+
+ tp = (Elf_Addr **) ((char *)allocate_tls(list, NULL, TLS_TCB_SIZE, 16)
+ + TLS_TP_OFFSET + TLS_TCB_SIZE);
+
+ __asm __volatile("mr 13,%0" :: "r"(tp));
+}
+
+void*
+__tls_get_addr(tls_index* ti)
+{
+ Elf_Addr **tp;
+ char *p;
+
+ __asm __volatile("mr %0,13" : "=r"(tp));
+ p = tls_get_addr_common((Elf_Addr**)((Elf_Addr)tp - TLS_TP_OFFSET
+ - TLS_TCB_SIZE), ti->ti_module, ti->ti_offset);
+
+ return (p + TLS_DTV_OFFSET);
+}
diff --git a/libexec/rtld-elf/powerpc64/rtld_machdep.h b/libexec/rtld-elf/powerpc64/rtld_machdep.h
new file mode 100644
index 0000000..b88ed9d
--- /dev/null
+++ b/libexec/rtld-elf/powerpc64/rtld_machdep.h
@@ -0,0 +1,85 @@
+/*-
+ * Copyright (c) 1999, 2000 John D. Polstra.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef RTLD_MACHDEP_H
+#define RTLD_MACHDEP_H 1
+
+#include <sys/types.h>
+#include <machine/atomic.h>
+
+struct Struct_Obj_Entry;
+
+/* Return the address of the .dynamic section in the dynamic linker. */
+#define rtld_dynamic(obj) (&_DYNAMIC)
+
+Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target,
+ const struct Struct_Obj_Entry *defobj,
+ const struct Struct_Obj_Entry *obj,
+ const Elf_Rel *rel);
+
+#define make_function_pointer(def, defobj) \
+ ((defobj)->relocbase + (def)->st_value)
+
+#define call_initfini_pointer(obj, target) \
+ (((InitFunc)(target))())
+
+#define call_init_pointer(obj, target) \
+ (((InitArrFunc)(target))(main_argc, main_argv, environ))
+
+/*
+ * Lazy binding entry point, called via PLT.
+ */
+void _rtld_bind_start(void);
+
+/*
+ * TLS
+ */
+
+#define TLS_TP_OFFSET 0x7000
+#define TLS_DTV_OFFSET 0x8000
+#define TLS_TCB_SIZE 16
+
+#define round(size, align) \
+ (((size) + (align) - 1) & ~((align) - 1))
+#define calculate_first_tls_offset(size, align) \
+ round(16, align)
+#define calculate_tls_offset(prev_offset, prev_size, size, align) \
+ round(prev_offset + prev_size, align)
+#define calculate_tls_end(off, size) ((off) + (size))
+
+typedef struct {
+ unsigned long ti_module;
+ unsigned long ti_offset;
+} tls_index;
+
+extern void *__tls_get_addr(tls_index* ti);
+
+#define RTLD_DEFAULT_STACK_PF_EXEC PF_X
+#define RTLD_DEFAULT_STACK_EXEC PROT_EXEC
+
+#endif
diff --git a/libexec/rtld-elf/powerpc64/rtld_start.S b/libexec/rtld-elf/powerpc64/rtld_start.S
new file mode 100644
index 0000000..186e16e
--- /dev/null
+++ b/libexec/rtld-elf/powerpc64/rtld_start.S
@@ -0,0 +1,162 @@
+/* $NetBSD: rtld_start.S,v 1.4 2001/09/26 04:06:43 mycroft Exp $ */
+
+/*-
+ * Copyright (C) 1998 Tsubai Masanari
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/asm.h>
+
+.extern _GLOBAL_OFFSET_TABLE_
+.extern _DYNAMIC
+
+_ENTRY(_rtld_start)
+ stdu %r1,-144(%r1) /* 16-byte aligned stack for reg saves +
+ exit_proc & obj _rtld args +
+ backchain & lrsave stack frame */
+ std %r3,96(%r1) /* argc */
+ std %r4,104(%r1) /* argv */
+ std %r5,112(%r1) /* envp */
+/* std %r6,120(%r1) *//* obj (always 0) */
+/* std %r7,128(%r1) *//* cleanup (always 0) */
+ std %r8,136(%r1) /* ps_strings */
+
+ /*
+ * Perform initial relocation of ld-elf.so. Not as easy as it
+ * sounds.
+ * - perform small forward branch to put PC into link reg
+ * - use link-time constants to determine offset to the
+ * _DYNAMIC section and the GOT. Add these to the PC to
+ * convert to absolute addresses.
+ * - call reloc_non_plt_self() to fix up ld-elf.so's relocations
+ */
+
+ bl 1f
+ .llong _DYNAMIC-.
+1:
+ mflr %r3 /* PC value at .llong */
+ ld %r4,0(%r3) /* offset to _DYNAMIC */
+ add %r3,%r4,%r3 /* r3 = &_DYNAMIC, absolute value */
+
+ ld %r4,-0x8000(%r2) /* First TOC entry is TOC base */
+ subf %r4,%r4,%r2 /* Subtract from real TOC base to get base */
+
+ bl reloc_non_plt_self /* reloc_non_plt_self(&_DYNAMIC,base) */
+ nop
+
+ /*
+ * The _rtld() function likes to see a stack layout containing
+ * { argc, argv[0], argv[1] ... argv[N], 0, env[0], ... , env[N] }
+ * Since the PowerPC stack was 16-byte aligned at exec time, the
+ * original stack layout has to be found by moving back a word
+ * from the argv pointer.
+ */
+ ld %r4,104(%r1)
+ addi %r3,%r4,-8 /* locate argc ptr, &argv[-1] */
+ addi %r4,%r1,128 /* &exit_proc on stack */
+ addi %r5,%r1,120 /* &obj_main on stack */
+
+ bl _rtld /* &_start = _rtld(sp, &exit_proc, &obj_main)*/
+ nop
+ ld %r2,8(%r3)
+ ld %r11,16(%r3)
+ ld %r3,0(%r3)
+ mtlr %r3
+
+ /*
+ * Restore args, with new obj/exit proc
+ */
+ ld %r3,96(%r1) /* argc */
+ ld %r4,104(%r1) /* argv */
+ ld %r5,112(%r1) /* envp */
+ ld %r6,120(%r1) /* obj */
+ ld %r7,128(%r1) /* exit proc */
+ ld %r8,136(%r1) /* ps_strings */
+
+ blrl /* _start(argc, argv, envp, obj, cleanup, ps_strings) */
+
+ li %r0,1 /* _exit() */
+ sc
+
+/*
+ * _rtld_bind_start()
+ *
+ * Call into the MI binder. This routine is reached via the PLT call cell
+ * On entry, %r11 contains a pointer to the (object, relocation) tuple.
+ *
+ * Save all registers, call into the binder to resolve and fixup the external
+ * routine, and then transfer to the external routine on return.
+ */
+ .globl _rtld_bind
+
+_ENTRY(_rtld_bind_start)
+ mflr %r0
+ std %r0,16(%r1) # save lr
+ mfcr %r0
+ std %r0,8(%r1) # save cr
+
+ stdu %r1,-48-12*8(%r1) # stack space for 8 regs + header
+ # + 2 save regs
+ std %r3,64+0*8(%r1) # save r3-r31
+ std %r4,64+1*8(%r1)
+ std %r5,64+2*8(%r1)
+ std %r6,64+3*8(%r1)
+ std %r7,64+4*8(%r1)
+ std %r8,64+5*8(%r1)
+ std %r9,64+6*8(%r1)
+ std %r10,64+7*8(%r1)
+ std %r12,64+8*8(%r1)
+
+ ld %r3,0(%r11)
+ ld %r4,8(%r11)
+ bl _rtld_bind # target addr = _rtld_bind(obj, reloff)
+ nop
+
+ ld %r2,8(%r3)
+ ld %r11,16(%r3)
+ ld %r3,0(%r3)
+ mtctr %r3 # move absolute target addr into ctr
+
+ ld %r3,64+0*8(%r1) # restore r3-r31
+ ld %r4,64+1*8(%r1)
+ ld %r5,64+2*8(%r1)
+ ld %r6,64+3*8(%r1)
+ ld %r7,64+4*8(%r1)
+ ld %r8,64+5*8(%r1)
+ ld %r9,64+6*8(%r1)
+ ld %r10,64+7*8(%r1)
+ ld %r12,64+8*8(%r1)
+
+ ld %r1,0(%r1) # restore stack
+ ld %r0,8(%r1) # restore cr
+ mtcr %r0
+ ld %r0,16(%r1) # restore lr
+ mtlr %r0
+
+ bctr # jump to target
+
+ .section .note.GNU-stack,"",%progbits
diff --git a/libexec/rtld-elf/rtld.1 b/libexec/rtld-elf/rtld.1
new file mode 100644
index 0000000..a8e9d68
--- /dev/null
+++ b/libexec/rtld-elf/rtld.1
@@ -0,0 +1,295 @@
+.\" 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 acknowledgment:
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 20, 2014
+.Dt RTLD 1
+.Os
+.Sh NAME
+.Nm ld-elf.so.1 ,
+.Nm ld.so ,
+.Nm rtld
+.Nd run-time link-editor
+.Sh DESCRIPTION
+The
+.Nm
+utility is a self-contained shared object providing run-time
+support for loading and link-editing shared objects into a process'
+address space.
+It is also commonly known as the dynamic linker.
+It uses the data structures
+contained within dynamically linked programs to determine which shared
+libraries are needed and loads them using the
+.Xr mmap 2
+system call.
+.Pp
+After all shared libraries have been successfully loaded,
+.Nm
+proceeds to resolve external references from both the main program and
+all objects loaded.
+A mechanism is provided for initialization routines
+to be called on a per-object basis, giving a shared object an opportunity
+to perform any extra set-up before execution of the program proper begins.
+This is useful for C++ libraries that contain static constructors.
+.Pp
+When resolving dependencies for the loaded objects,
+.Nm
+may be allowed to translate dynamic token strings in rpath and soname
+by setting
+.Fl "z origin"
+option of the static linker
+.Xr ld 1 .
+The following strings are recognized now:
+.Bl -tag -width ".Pa $PLATFORM"
+.It Pa $ORIGIN
+Translated to the full path of the loaded object.
+.It Pa $OSNAME
+Translated to the name of the operating system implementation.
+.It Pa $OSREL
+Translated to the release level of the operating system.
+.It Pa $PLATFORM
+Translated to the machine hardware platform.
+.El
+.Pp
+The
+.Nm
+utility itself is loaded by the kernel together with any dynamically-linked
+program that is to be executed.
+The kernel transfers control to the
+dynamic linker.
+After the dynamic linker has finished loading,
+relocating, and initializing the program and its required shared
+objects, it transfers control to the entry point of the program.
+The following search order is used to locate required shared objects:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+.Dv DT_RPATH
+of the referencing object unless that object also contains a
+.Dv DT_RUNPATH
+tag
+.It
+.Dv DT_RPATH
+of the program unless the referencing object contains a
+.Dv DT_RUNPATH
+tag
+.It
+Path indicated by
+.Ev LD_LIBRARY_PATH
+environment variable
+.It
+.Dv DT_RUNPATH
+of the referencing object
+.It
+Hints file produced by the
+.Xr ldconfig 8
+utility
+.It
+The
+.Pa /lib
+and
+.Pa /usr/lib
+directories, unless the referencing object was linked using the
+.Dq Fl z Ar nodefaultlib
+option
+.El
+.Pp
+The
+.Nm
+utility
+recognizes a number of environment variables that can be used to modify
+its behaviour.
+On 64-bit architectures, the linker for 32-bit objects recognizes
+all the environment variables listed below, but is being prefixed with
+.Ev LD_32_ ,
+for example:
+.Ev LD_32_TRACE_LOADED_OBJECTS .
+.Bl -tag -width ".Ev LD_LIBMAP_DISABLE"
+.It Ev LD_DUMP_REL_POST
+If set,
+.Nm
+will print a table containing all relocations after symbol
+binding and relocation.
+.It Ev LD_DUMP_REL_PRE
+If set,
+.Nm
+will print a table containing all relocations before symbol
+binding and relocation.
+.It Ev LD_LIBMAP
+A library replacement list in the same format as
+.Xr libmap.conf 5 .
+For convenience, the characters
+.Ql =
+and
+.Ql \&,
+can be used instead of a space and a newline.
+This variable is parsed after
+.Xr libmap.conf 5 ,
+and will override its entries.
+This variable is unset for set-user-ID and set-group-ID programs.
+.It Ev LD_LIBMAP_DISABLE
+If set, disables the use of
+.Xr libmap.conf 5
+and
+.Ev LD_LIBMAP .
+This variable is unset for set-user-ID and set-group-ID programs.
+.It Ev LD_ELF_HINTS_PATH
+This variable will override the default location of
+.Dq hints
+file.
+This variable is unset for set-user-ID and set-group-ID programs.
+.It Ev LD_LIBRARY_PATH
+A colon separated list of directories, overriding the default search path
+for shared libraries.
+This variable is unset for set-user-ID and set-group-ID programs.
+.It Ev LD_LIBRARY_PATH_RPATH
+If the variable is specified and has a value starting with
+any of \'y\', \'Y\' or \'1\' symbols, the path specified by
+.Ev LD_LIBRARY_PATH
+variable is allowed to override the path from
+.Dv DT_RPATH
+for binaries which does not contain
+.Dv DT_RUNPATH
+tag.
+For such binaries, when the variable
+.Ev LD_LIBRARY_PATH_RPATH
+is set,
+.Dq Fl z Ar nodefaultlib
+link-time option is ignored as well.
+.It Ev LD_PRELOAD
+A list of shared libraries, separated by colons and/or white space,
+to be linked in before any
+other shared libraries.
+If the directory is not specified then
+the directories specified by
+.Ev LD_LIBRARY_PATH
+will be searched first
+followed by the set of built-in standard directories.
+This variable is unset for set-user-ID and set-group-ID programs.
+.Ev LD_LIBRARY_PATH_FDS
+A colon separated list of file descriptor numbers for library directories.
+This is intended for use within
+.Xr capsicum 4
+sandboxes, when global namespaces such as the filesystem are unavailable.
+It is consulted just after LD_LIBRARY_PATH.
+This variable is unset for set-user-ID and set-group-ID programs.
+.It Ev LD_BIND_NOW
+When set to a nonempty string, causes
+.Nm
+to relocate all external function calls before starting execution of the
+program.
+Normally, function calls are bound lazily, at the first call
+of each function.
+.Ev LD_BIND_NOW
+increases the start-up time of a program, but it avoids run-time
+surprises caused by unexpectedly undefined functions.
+.It Ev LD_TRACE_LOADED_OBJECTS
+When set to a nonempty string, 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_TRACE_LOADED_OBJECTS_ALL
+When set to a nonempty string, causes
+.Nm
+to expand the summary to indicate which objects caused each object to
+be loaded.
+.It Ev LD_TRACE_LOADED_OBJECTS_FMT1
+.It Ev LD_TRACE_LOADED_OBJECTS_FMT2
+When set, these variables are interpreted as format strings a la
+.Xr printf 3
+to customize the trace output and are used by
+.Xr ldd 1 Ns 's
+.Fl f
+option and allows
+.Xr ldd 1
+to be operated as a filter more conveniently.
+If the dependency name starts with string
+.Pa lib ,
+.Ev LD_TRACE_LOADED_OBJECTS_FMT1
+is used, otherwise
+.Ev LD_TRACE_LOADED_OBJECTS_FMT2
+is used.
+The following conversions can be used:
+.Bl -tag -width 4n
+.It Li %a
+The main program's name
+(also known as
+.Dq __progname ) .
+.It Li \&%A
+The value of the environment variable
+.Ev LD_TRACE_LOADED_OBJECTS_PROGNAME .
+Typically used to print both the names of programs and shared libraries
+being inspected using
+.Xr ldd 1 .
+.It Li %o
+The library name.
+.It Li %p
+The full pathname as determined by
+.Nm rtld Ns 's
+library search rules.
+.It Li %x
+The library's load address.
+.El
+.Pp
+Additionally,
+.Ql \en
+and
+.Ql \et
+are recognized and have their usual meaning.
+.It Ev LD_UTRACE
+If set,
+.Nm
+will log events such as the loading and unloading of shared objects via
+.Xr utrace 2 .
+.It Ev LD_LOADFLTR
+If set,
+.Nm
+will process the filtee dependencies of the loaded objects immediately,
+instead of postponing it until required.
+Normally, the filtees are opened at the time of the first symbol resolution
+from the filter object.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /var/run/ld-elf32.so.hints" -compact
+.It Pa /var/run/ld-elf.so.hints
+Hints file.
+.It Pa /var/run/ld-elf32.so.hints
+Hints file for 32-bit binaries on 64-bit system.
+.It Pa /etc/libmap.conf
+The libmap configuration file.
+.It Pa /etc/libmap32.conf
+The libmap configuration file for 32-bit binaries on 64-bit system.
+.El
+.Sh SEE ALSO
+.Xr ld 1 ,
+.Xr ldd 1 ,
+.Xr capsicum 4 ,
+.Xr elf 5 ,
+.Xr libmap.conf 5 ,
+.Xr ldconfig 8
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
new file mode 100644
index 0000000..cc7afda
--- /dev/null
+++ b/libexec/rtld-elf/rtld.c
@@ -0,0 +1,5021 @@
+/*-
+ * Copyright 1996, 1997, 1998, 1999, 2000 John D. Polstra.
+ * Copyright 2003 Alexander Kabaev <kan@FreeBSD.ORG>.
+ * Copyright 2009-2012 Konstantin Belousov <kib@FreeBSD.ORG>.
+ * Copyright 2012 John Marino <draco@marino.st>.
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Dynamic linker for ELF.
+ *
+ * John Polstra <jdp@polstra.com>.
+ */
+
+#ifndef __GNUC__
+#error "GCC is needed to compile this file"
+#endif
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+#include <sys/ktrace.h>
+
+#include <dlfcn.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+#include "rtld.h"
+#include "libmap.h"
+#include "rtld_tls.h"
+#include "rtld_printf.h"
+#include "notes.h"
+
+#ifndef COMPAT_32BIT
+#define PATH_RTLD "/libexec/ld-elf.so.1"
+#else
+#define PATH_RTLD "/libexec/ld-elf32.so.1"
+#endif
+
+/* Types. */
+typedef void (*func_ptr_type)();
+typedef void * (*path_enum_proc) (const char *path, size_t len, void *arg);
+
+/*
+ * Function declarations.
+ */
+static const char *basename(const char *);
+static void die(void) __dead2;
+static void digest_dynamic1(Obj_Entry *, int, const Elf_Dyn **,
+ const Elf_Dyn **, const Elf_Dyn **);
+static void digest_dynamic2(Obj_Entry *, const Elf_Dyn *, const Elf_Dyn *,
+ const Elf_Dyn *);
+static void digest_dynamic(Obj_Entry *, int);
+static Obj_Entry *digest_phdr(const Elf_Phdr *, int, caddr_t, const char *);
+static Obj_Entry *dlcheck(void *);
+static Obj_Entry *dlopen_object(const char *name, int fd, Obj_Entry *refobj,
+ int lo_flags, int mode, RtldLockState *lockstate);
+static Obj_Entry *do_load_object(int, const char *, char *, struct stat *, int);
+static int do_search_info(const Obj_Entry *obj, int, struct dl_serinfo *);
+static bool donelist_check(DoneList *, const Obj_Entry *);
+static void errmsg_restore(char *);
+static char *errmsg_save(void);
+static void *fill_search_info(const char *, size_t, void *);
+static char *find_library(const char *, const Obj_Entry *, int *);
+static const char *gethints(bool);
+static void init_dag(Obj_Entry *);
+static void init_pagesizes(Elf_Auxinfo **aux_info);
+static void init_rtld(caddr_t, Elf_Auxinfo **);
+static void initlist_add_neededs(Needed_Entry *, Objlist *);
+static void initlist_add_objects(Obj_Entry *, Obj_Entry **, Objlist *);
+static void linkmap_add(Obj_Entry *);
+static void linkmap_delete(Obj_Entry *);
+static void load_filtees(Obj_Entry *, int flags, RtldLockState *);
+static void unload_filtees(Obj_Entry *);
+static int load_needed_objects(Obj_Entry *, int);
+static int load_preload_objects(void);
+static Obj_Entry *load_object(const char *, int fd, const Obj_Entry *, int);
+static void map_stacks_exec(RtldLockState *);
+static Obj_Entry *obj_from_addr(const void *);
+static void objlist_call_fini(Objlist *, Obj_Entry *, RtldLockState *);
+static void objlist_call_init(Objlist *, RtldLockState *);
+static void objlist_clear(Objlist *);
+static Objlist_Entry *objlist_find(Objlist *, const Obj_Entry *);
+static void objlist_init(Objlist *);
+static void objlist_push_head(Objlist *, Obj_Entry *);
+static void objlist_push_tail(Objlist *, Obj_Entry *);
+static void objlist_put_after(Objlist *, Obj_Entry *, Obj_Entry *);
+static void objlist_remove(Objlist *, Obj_Entry *);
+static int parse_libdir(const char *);
+static void *path_enumerate(const char *, path_enum_proc, void *);
+static int relocate_object_dag(Obj_Entry *root, bool bind_now,
+ Obj_Entry *rtldobj, int flags, RtldLockState *lockstate);
+static int relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj,
+ int flags, RtldLockState *lockstate);
+static int relocate_objects(Obj_Entry *, bool, Obj_Entry *, int,
+ RtldLockState *);
+static int resolve_objects_ifunc(Obj_Entry *first, bool bind_now,
+ int flags, RtldLockState *lockstate);
+static int rtld_dirname(const char *, char *);
+static int rtld_dirname_abs(const char *, char *);
+static void *rtld_dlopen(const char *name, int fd, int mode);
+static void rtld_exit(void);
+static char *search_library_path(const char *, const char *);
+static char *search_library_pathfds(const char *, const char *, int *);
+static const void **get_program_var_addr(const char *, RtldLockState *);
+static void set_program_var(const char *, const void *);
+static int symlook_default(SymLook *, const Obj_Entry *refobj);
+static int symlook_global(SymLook *, DoneList *);
+static void symlook_init_from_req(SymLook *, const SymLook *);
+static int symlook_list(SymLook *, const Objlist *, DoneList *);
+static int symlook_needed(SymLook *, const Needed_Entry *, DoneList *);
+static int symlook_obj1_sysv(SymLook *, const Obj_Entry *);
+static int symlook_obj1_gnu(SymLook *, const Obj_Entry *);
+static void trace_loaded_objects(Obj_Entry *);
+static void unlink_object(Obj_Entry *);
+static void unload_object(Obj_Entry *);
+static void unref_dag(Obj_Entry *);
+static void ref_dag(Obj_Entry *);
+static char *origin_subst_one(char *, const char *, const char *, bool);
+static char *origin_subst(char *, const char *);
+static void preinit_main(void);
+static int rtld_verify_versions(const Objlist *);
+static int rtld_verify_object_versions(Obj_Entry *);
+static void object_add_name(Obj_Entry *, const char *);
+static int object_match_name(const Obj_Entry *, const char *);
+static void ld_utrace_log(int, void *, void *, size_t, int, const char *);
+static void rtld_fill_dl_phdr_info(const Obj_Entry *obj,
+ struct dl_phdr_info *phdr_info);
+static uint32_t gnu_hash(const char *);
+static bool matched_symbol(SymLook *, const Obj_Entry *, Sym_Match_Result *,
+ const unsigned long);
+
+void r_debug_state(struct r_debug *, struct link_map *) __noinline;
+void _r_debug_postinit(struct link_map *) __noinline;
+
+/*
+ * Data declarations.
+ */
+static char *error_message; /* Message for dlerror(), or NULL */
+struct r_debug r_debug; /* for GDB; */
+static bool libmap_disable; /* Disable libmap */
+static bool ld_loadfltr; /* Immediate filters processing */
+static char *libmap_override; /* Maps to use in addition to libmap.conf */
+static bool trust; /* False for setuid and setgid programs */
+static bool dangerous_ld_env; /* True if environment variables have been
+ used to affect the libraries loaded */
+static char *ld_bind_now; /* Environment variable for immediate binding */
+static char *ld_debug; /* Environment variable for debugging */
+static char *ld_library_path; /* Environment variable for search path */
+static char *ld_library_dirs; /* Environment variable for library descriptors */
+static char *ld_preload; /* Environment variable for libraries to
+ load first */
+static char *ld_elf_hints_path; /* Environment variable for alternative hints path */
+static char *ld_tracing; /* Called from ldd to print libs */
+static char *ld_utrace; /* Use utrace() to log events. */
+static Obj_Entry *obj_list; /* Head of linked list of shared objects */
+static Obj_Entry **obj_tail; /* Link field of last object in list */
+static Obj_Entry *obj_main; /* The main program shared object */
+static Obj_Entry obj_rtld; /* The dynamic linker shared object */
+static unsigned int obj_count; /* Number of objects in obj_list */
+static unsigned int obj_loads; /* Number of objects in obj_list */
+
+static Objlist list_global = /* Objects dlopened with RTLD_GLOBAL */
+ STAILQ_HEAD_INITIALIZER(list_global);
+static Objlist list_main = /* Objects loaded at program startup */
+ STAILQ_HEAD_INITIALIZER(list_main);
+static Objlist list_fini = /* Objects needing fini() calls */
+ STAILQ_HEAD_INITIALIZER(list_fini);
+
+Elf_Sym sym_zero; /* For resolving undefined weak refs. */
+
+#define GDB_STATE(s,m) r_debug.r_state = s; r_debug_state(&r_debug,m);
+
+extern Elf_Dyn _DYNAMIC;
+#pragma weak _DYNAMIC
+#ifndef RTLD_IS_DYNAMIC
+#define RTLD_IS_DYNAMIC() (&_DYNAMIC != NULL)
+#endif
+
+int npagesizes, osreldate;
+size_t *pagesizes;
+
+long __stack_chk_guard[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+static int stack_prot = PROT_READ | PROT_WRITE | RTLD_DEFAULT_STACK_EXEC;
+static int max_stack_flags;
+
+/*
+ * Global declarations normally provided by crt1. The dynamic linker is
+ * not built with crt1, so we have to provide them ourselves.
+ */
+char *__progname;
+char **environ;
+
+/*
+ * Used to pass argc, argv to init functions.
+ */
+int main_argc;
+char **main_argv;
+
+/*
+ * Globals to control TLS allocation.
+ */
+size_t tls_last_offset; /* Static TLS offset of last module */
+size_t tls_last_size; /* Static TLS size of last module */
+size_t tls_static_space; /* Static TLS space allocated */
+size_t tls_static_max_align;
+int tls_dtv_generation = 1; /* Used to detect when dtv size changes */
+int tls_max_index = 1; /* Largest module index allocated */
+
+bool ld_library_path_rpath = false;
+
+/*
+ * Fill in a DoneList with an allocation large enough to hold all of
+ * the currently-loaded objects. Keep this as a macro since it calls
+ * alloca and we want that to occur within the scope of the caller.
+ */
+#define donelist_init(dlp) \
+ ((dlp)->objs = alloca(obj_count * sizeof (dlp)->objs[0]), \
+ assert((dlp)->objs != NULL), \
+ (dlp)->num_alloc = obj_count, \
+ (dlp)->num_used = 0)
+
+#define UTRACE_DLOPEN_START 1
+#define UTRACE_DLOPEN_STOP 2
+#define UTRACE_DLCLOSE_START 3
+#define UTRACE_DLCLOSE_STOP 4
+#define UTRACE_LOAD_OBJECT 5
+#define UTRACE_UNLOAD_OBJECT 6
+#define UTRACE_ADD_RUNDEP 7
+#define UTRACE_PRELOAD_FINISHED 8
+#define UTRACE_INIT_CALL 9
+#define UTRACE_FINI_CALL 10
+
+struct utrace_rtld {
+ char sig[4]; /* 'RTLD' */
+ int event;
+ void *handle;
+ void *mapbase; /* Used for 'parent' and 'init/fini' */
+ size_t mapsize;
+ int refcnt; /* Used for 'mode' */
+ char name[MAXPATHLEN];
+};
+
+#define LD_UTRACE(e, h, mb, ms, r, n) do { \
+ if (ld_utrace != NULL) \
+ ld_utrace_log(e, h, mb, ms, r, n); \
+} while (0)
+
+static void
+ld_utrace_log(int event, void *handle, void *mapbase, size_t mapsize,
+ int refcnt, const char *name)
+{
+ struct utrace_rtld ut;
+
+ ut.sig[0] = 'R';
+ ut.sig[1] = 'T';
+ ut.sig[2] = 'L';
+ ut.sig[3] = 'D';
+ ut.event = event;
+ ut.handle = handle;
+ ut.mapbase = mapbase;
+ ut.mapsize = mapsize;
+ ut.refcnt = refcnt;
+ bzero(ut.name, sizeof(ut.name));
+ if (name)
+ strlcpy(ut.name, name, sizeof(ut.name));
+ utrace(&ut, sizeof(ut));
+}
+
+/*
+ * Main entry point for dynamic linking. The first argument is the
+ * stack pointer. The stack is expected to be laid out as described
+ * in the SVR4 ABI specification, Intel 386 Processor Supplement.
+ * Specifically, the stack pointer points to a word containing
+ * ARGC. Following that in the stack is a null-terminated sequence
+ * of pointers to argument strings. Then comes a null-terminated
+ * sequence of pointers to environment strings. Finally, there is a
+ * sequence of "auxiliary vector" entries.
+ *
+ * The second argument points to a place to store the dynamic linker's
+ * exit procedure pointer and the third to a place to store the main
+ * program's object.
+ *
+ * The return value is the main program's entry point.
+ */
+func_ptr_type
+_rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
+{
+ Elf_Auxinfo *aux_info[AT_COUNT];
+ int i;
+ int argc;
+ char **argv;
+ char **env;
+ Elf_Auxinfo *aux;
+ Elf_Auxinfo *auxp;
+ const char *argv0;
+ Objlist_Entry *entry;
+ Obj_Entry *obj;
+ Obj_Entry **preload_tail;
+ Obj_Entry *last_interposer;
+ Objlist initlist;
+ RtldLockState lockstate;
+ char *library_path_rpath;
+ int mib[2];
+ size_t len;
+
+ /*
+ * On entry, the dynamic linker itself has not been relocated yet.
+ * Be very careful not to reference any global data until after
+ * init_rtld has returned. It is OK to reference file-scope statics
+ * and string constants, and to call static and global functions.
+ */
+
+ /* Find the auxiliary vector on the stack. */
+ argc = *sp++;
+ argv = (char **) sp;
+ sp += argc + 1; /* Skip over arguments and NULL terminator */
+ env = (char **) sp;
+ while (*sp++ != 0) /* Skip over environment, and NULL terminator */
+ ;
+ aux = (Elf_Auxinfo *) sp;
+
+ /* Digest the auxiliary vector. */
+ for (i = 0; i < AT_COUNT; i++)
+ aux_info[i] = NULL;
+ for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {
+ if (auxp->a_type < AT_COUNT)
+ aux_info[auxp->a_type] = auxp;
+ }
+
+ /* Initialize and relocate ourselves. */
+ assert(aux_info[AT_BASE] != NULL);
+ init_rtld((caddr_t) aux_info[AT_BASE]->a_un.a_ptr, aux_info);
+
+ __progname = obj_rtld.path;
+ argv0 = argv[0] != NULL ? argv[0] : "(null)";
+ environ = env;
+ main_argc = argc;
+ main_argv = argv;
+
+ if (aux_info[AT_CANARY] != NULL &&
+ aux_info[AT_CANARY]->a_un.a_ptr != NULL) {
+ i = aux_info[AT_CANARYLEN]->a_un.a_val;
+ if (i > sizeof(__stack_chk_guard))
+ i = sizeof(__stack_chk_guard);
+ memcpy(__stack_chk_guard, aux_info[AT_CANARY]->a_un.a_ptr, i);
+ } else {
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_ARND;
+
+ len = sizeof(__stack_chk_guard);
+ if (sysctl(mib, 2, __stack_chk_guard, &len, NULL, 0) == -1 ||
+ len != sizeof(__stack_chk_guard)) {
+ /* If sysctl was unsuccessful, use the "terminator canary". */
+ ((unsigned char *)(void *)__stack_chk_guard)[0] = 0;
+ ((unsigned char *)(void *)__stack_chk_guard)[1] = 0;
+ ((unsigned char *)(void *)__stack_chk_guard)[2] = '\n';
+ ((unsigned char *)(void *)__stack_chk_guard)[3] = 255;
+ }
+ }
+
+ trust = !issetugid();
+
+ ld_bind_now = getenv(LD_ "BIND_NOW");
+ /*
+ * If the process is tainted, then we un-set the dangerous environment
+ * variables. The process will be marked as tainted until setuid(2)
+ * is called. If any child process calls setuid(2) we do not want any
+ * future processes to honor the potentially un-safe variables.
+ */
+ if (!trust) {
+ if (unsetenv(LD_ "PRELOAD") || unsetenv(LD_ "LIBMAP") ||
+ unsetenv(LD_ "LIBRARY_PATH") || unsetenv(LD_ "LIBRARY_PATH_FDS") ||
+ unsetenv(LD_ "LIBMAP_DISABLE") ||
+ unsetenv(LD_ "DEBUG") || unsetenv(LD_ "ELF_HINTS_PATH") ||
+ unsetenv(LD_ "LOADFLTR") || unsetenv(LD_ "LIBRARY_PATH_RPATH")) {
+ _rtld_error("environment corrupt; aborting");
+ die();
+ }
+ }
+ ld_debug = getenv(LD_ "DEBUG");
+ libmap_disable = getenv(LD_ "LIBMAP_DISABLE") != NULL;
+ libmap_override = getenv(LD_ "LIBMAP");
+ ld_library_path = getenv(LD_ "LIBRARY_PATH");
+ ld_library_dirs = getenv(LD_ "LIBRARY_PATH_FDS");
+ ld_preload = getenv(LD_ "PRELOAD");
+ ld_elf_hints_path = getenv(LD_ "ELF_HINTS_PATH");
+ ld_loadfltr = getenv(LD_ "LOADFLTR") != NULL;
+ library_path_rpath = getenv(LD_ "LIBRARY_PATH_RPATH");
+ if (library_path_rpath != NULL) {
+ if (library_path_rpath[0] == 'y' ||
+ library_path_rpath[0] == 'Y' ||
+ library_path_rpath[0] == '1')
+ ld_library_path_rpath = true;
+ else
+ ld_library_path_rpath = false;
+ }
+ dangerous_ld_env = libmap_disable || (libmap_override != NULL) ||
+ (ld_library_path != NULL) || (ld_preload != NULL) ||
+ (ld_elf_hints_path != NULL) || ld_loadfltr;
+ ld_tracing = getenv(LD_ "TRACE_LOADED_OBJECTS");
+ ld_utrace = getenv(LD_ "UTRACE");
+
+ if ((ld_elf_hints_path == NULL) || strlen(ld_elf_hints_path) == 0)
+ ld_elf_hints_path = _PATH_ELF_HINTS;
+
+ if (ld_debug != NULL && *ld_debug != '\0')
+ debug = 1;
+ dbg("%s is initialized, base address = %p", __progname,
+ (caddr_t) aux_info[AT_BASE]->a_un.a_ptr);
+ dbg("RTLD dynamic = %p", obj_rtld.dynamic);
+ dbg("RTLD pltgot = %p", obj_rtld.pltgot);
+
+ dbg("initializing thread locks");
+ lockdflt_init();
+
+ /*
+ * Load the main program, or process its program header if it is
+ * already loaded.
+ */
+ if (aux_info[AT_EXECFD] != NULL) { /* Load the main program. */
+ int fd = aux_info[AT_EXECFD]->a_un.a_val;
+ dbg("loading main program");
+ obj_main = map_object(fd, argv0, NULL);
+ close(fd);
+ if (obj_main == NULL)
+ die();
+ max_stack_flags = obj->stack_flags;
+ } else { /* Main program already loaded. */
+ const Elf_Phdr *phdr;
+ int phnum;
+ caddr_t entry;
+
+ dbg("processing main program's program header");
+ assert(aux_info[AT_PHDR] != NULL);
+ phdr = (const Elf_Phdr *) aux_info[AT_PHDR]->a_un.a_ptr;
+ assert(aux_info[AT_PHNUM] != NULL);
+ phnum = aux_info[AT_PHNUM]->a_un.a_val;
+ assert(aux_info[AT_PHENT] != NULL);
+ assert(aux_info[AT_PHENT]->a_un.a_val == sizeof(Elf_Phdr));
+ assert(aux_info[AT_ENTRY] != NULL);
+ entry = (caddr_t) aux_info[AT_ENTRY]->a_un.a_ptr;
+ if ((obj_main = digest_phdr(phdr, phnum, entry, argv0)) == NULL)
+ die();
+ }
+
+ if (aux_info[AT_EXECPATH] != 0) {
+ char *kexecpath;
+ char buf[MAXPATHLEN];
+
+ kexecpath = aux_info[AT_EXECPATH]->a_un.a_ptr;
+ dbg("AT_EXECPATH %p %s", kexecpath, kexecpath);
+ if (kexecpath[0] == '/')
+ obj_main->path = kexecpath;
+ else if (getcwd(buf, sizeof(buf)) == NULL ||
+ strlcat(buf, "/", sizeof(buf)) >= sizeof(buf) ||
+ strlcat(buf, kexecpath, sizeof(buf)) >= sizeof(buf))
+ obj_main->path = xstrdup(argv0);
+ else
+ obj_main->path = xstrdup(buf);
+ } else {
+ dbg("No AT_EXECPATH");
+ obj_main->path = xstrdup(argv0);
+ }
+ dbg("obj_main path %s", obj_main->path);
+ obj_main->mainprog = true;
+
+ if (aux_info[AT_STACKPROT] != NULL &&
+ aux_info[AT_STACKPROT]->a_un.a_val != 0)
+ stack_prot = aux_info[AT_STACKPROT]->a_un.a_val;
+
+ /*
+ * Get the actual dynamic linker pathname from the executable if
+ * possible. (It should always be possible.) That ensures that
+ * gdb will find the right dynamic linker even if a non-standard
+ * one is being used.
+ */
+ if (obj_main->interp != NULL &&
+ strcmp(obj_main->interp, obj_rtld.path) != 0) {
+ free(obj_rtld.path);
+ obj_rtld.path = xstrdup(obj_main->interp);
+ __progname = obj_rtld.path;
+ }
+
+ digest_dynamic(obj_main, 0);
+ dbg("%s valid_hash_sysv %d valid_hash_gnu %d dynsymcount %d",
+ obj_main->path, obj_main->valid_hash_sysv, obj_main->valid_hash_gnu,
+ obj_main->dynsymcount);
+
+ linkmap_add(obj_main);
+ linkmap_add(&obj_rtld);
+
+ /* Link the main program into the list of objects. */
+ *obj_tail = obj_main;
+ obj_tail = &obj_main->next;
+ obj_count++;
+ obj_loads++;
+
+ /* Initialize a fake symbol for resolving undefined weak references. */
+ sym_zero.st_info = ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE);
+ sym_zero.st_shndx = SHN_UNDEF;
+ sym_zero.st_value = -(uintptr_t)obj_main->relocbase;
+
+ if (!libmap_disable)
+ libmap_disable = (bool)lm_init(libmap_override);
+
+ dbg("loading LD_PRELOAD libraries");
+ if (load_preload_objects() == -1)
+ die();
+ preload_tail = obj_tail;
+
+ dbg("loading needed objects");
+ if (load_needed_objects(obj_main, 0) == -1)
+ die();
+
+ /* Make a list of all objects loaded at startup. */
+ last_interposer = obj_main;
+ for (obj = obj_list; obj != NULL; obj = obj->next) {
+ if (obj->z_interpose && obj != obj_main) {
+ objlist_put_after(&list_main, last_interposer, obj);
+ last_interposer = obj;
+ } else {
+ objlist_push_tail(&list_main, obj);
+ }
+ obj->refcount++;
+ }
+
+ dbg("checking for required versions");
+ if (rtld_verify_versions(&list_main) == -1 && !ld_tracing)
+ die();
+
+ if (ld_tracing) { /* We're done */
+ trace_loaded_objects(obj_main);
+ exit(0);
+ }
+
+ if (getenv(LD_ "DUMP_REL_PRE") != NULL) {
+ dump_relocations(obj_main);
+ exit (0);
+ }
+
+ /*
+ * Processing tls relocations requires having the tls offsets
+ * initialized. Prepare offsets before starting initial
+ * relocation processing.
+ */
+ dbg("initializing initial thread local storage offsets");
+ STAILQ_FOREACH(entry, &list_main, link) {
+ /*
+ * Allocate all the initial objects out of the static TLS
+ * block even if they didn't ask for it.
+ */
+ allocate_tls_offset(entry->obj);
+ }
+
+ if (relocate_objects(obj_main,
+ ld_bind_now != NULL && *ld_bind_now != '\0',
+ &obj_rtld, SYMLOOK_EARLY, NULL) == -1)
+ die();
+
+ dbg("doing copy relocations");
+ if (do_copy_relocations(obj_main) == -1)
+ die();
+
+ if (getenv(LD_ "DUMP_REL_POST") != NULL) {
+ dump_relocations(obj_main);
+ exit (0);
+ }
+
+ /*
+ * Setup TLS for main thread. This must be done after the
+ * relocations are processed, since tls initialization section
+ * might be the subject for relocations.
+ */
+ dbg("initializing initial thread local storage");
+ allocate_initial_tls(obj_list);
+
+ dbg("initializing key program variables");
+ set_program_var("__progname", argv[0] != NULL ? basename(argv[0]) : "");
+ set_program_var("environ", env);
+ set_program_var("__elf_aux_vector", aux);
+
+ /* Make a list of init functions to call. */
+ objlist_init(&initlist);
+ initlist_add_objects(obj_list, preload_tail, &initlist);
+
+ r_debug_state(NULL, &obj_main->linkmap); /* say hello to gdb! */
+
+ map_stacks_exec(NULL);
+
+ dbg("resolving ifuncs");
+ if (resolve_objects_ifunc(obj_main,
+ ld_bind_now != NULL && *ld_bind_now != '\0', SYMLOOK_EARLY,
+ NULL) == -1)
+ die();
+
+ if (!obj_main->crt_no_init) {
+ /*
+ * Make sure we don't call the main program's init and fini
+ * functions for binaries linked with old crt1 which calls
+ * _init itself.
+ */
+ obj_main->init = obj_main->fini = (Elf_Addr)NULL;
+ obj_main->preinit_array = obj_main->init_array =
+ obj_main->fini_array = (Elf_Addr)NULL;
+ }
+
+ wlock_acquire(rtld_bind_lock, &lockstate);
+ if (obj_main->crt_no_init)
+ preinit_main();
+ objlist_call_init(&initlist, &lockstate);
+ _r_debug_postinit(&obj_main->linkmap);
+ objlist_clear(&initlist);
+ dbg("loading filtees");
+ for (obj = obj_list->next; obj != NULL; obj = obj->next) {
+ if (ld_loadfltr || obj->z_loadfltr)
+ load_filtees(obj, 0, &lockstate);
+ }
+ lock_release(rtld_bind_lock, &lockstate);
+
+ dbg("transferring control to program entry point = %p", obj_main->entry);
+
+ /* Return the exit procedure and the program entry point. */
+ *exit_proc = rtld_exit;
+ *objp = obj_main;
+ return (func_ptr_type) obj_main->entry;
+}
+
+void *
+rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def)
+{
+ void *ptr;
+ Elf_Addr target;
+
+ ptr = (void *)make_function_pointer(def, obj);
+ target = ((Elf_Addr (*)(void))ptr)();
+ return ((void *)target);
+}
+
+Elf_Addr
+_rtld_bind(Obj_Entry *obj, Elf_Size reloff)
+{
+ const Elf_Rel *rel;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+ Elf_Addr *where;
+ Elf_Addr target;
+ RtldLockState lockstate;
+
+ rlock_acquire(rtld_bind_lock, &lockstate);
+ if (sigsetjmp(lockstate.env, 0) != 0)
+ lock_upgrade(rtld_bind_lock, &lockstate);
+ if (obj->pltrel)
+ rel = (const Elf_Rel *) ((caddr_t) obj->pltrel + reloff);
+ else
+ rel = (const Elf_Rel *) ((caddr_t) obj->pltrela + reloff);
+
+ where = (Elf_Addr *) (obj->relocbase + rel->r_offset);
+ def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL,
+ &lockstate);
+ if (def == NULL)
+ die();
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC)
+ target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
+ else
+ target = (Elf_Addr)(defobj->relocbase + def->st_value);
+
+ dbg("\"%s\" in \"%s\" ==> %p in \"%s\"",
+ defobj->strtab + def->st_name, basename(obj->path),
+ (void *)target, basename(defobj->path));
+
+ /*
+ * Write the new contents for the jmpslot. Note that depending on
+ * architecture, the value which we need to return back to the
+ * lazy binding trampoline may or may not be the target
+ * address. The value returned from reloc_jmpslot() is the value
+ * that the trampoline needs.
+ */
+ target = reloc_jmpslot(where, target, defobj, obj, rel);
+ lock_release(rtld_bind_lock, &lockstate);
+ return target;
+}
+
+/*
+ * Error reporting function. Use it like printf. If formats the message
+ * into a buffer, and sets things up so that the next call to dlerror()
+ * will return the message.
+ */
+void
+_rtld_error(const char *fmt, ...)
+{
+ static char buf[512];
+ va_list ap;
+
+ va_start(ap, fmt);
+ rtld_vsnprintf(buf, sizeof buf, fmt, ap);
+ error_message = buf;
+ va_end(ap);
+}
+
+/*
+ * Return a dynamically-allocated copy of the current error message, if any.
+ */
+static char *
+errmsg_save(void)
+{
+ return error_message == NULL ? NULL : xstrdup(error_message);
+}
+
+/*
+ * Restore the current error message from a copy which was previously saved
+ * by errmsg_save(). The copy is freed.
+ */
+static void
+errmsg_restore(char *saved_msg)
+{
+ if (saved_msg == NULL)
+ error_message = NULL;
+ else {
+ _rtld_error("%s", saved_msg);
+ free(saved_msg);
+ }
+}
+
+static const char *
+basename(const char *name)
+{
+ const char *p = strrchr(name, '/');
+ return p != NULL ? p + 1 : name;
+}
+
+static struct utsname uts;
+
+static char *
+origin_subst_one(char *real, const char *kw, const char *subst,
+ bool may_free)
+{
+ char *p, *p1, *res, *resp;
+ int subst_len, kw_len, subst_count, old_len, new_len;
+
+ kw_len = strlen(kw);
+
+ /*
+ * First, count the number of the keyword occurences, to
+ * preallocate the final string.
+ */
+ for (p = real, subst_count = 0;; p = p1 + kw_len, subst_count++) {
+ p1 = strstr(p, kw);
+ if (p1 == NULL)
+ break;
+ }
+
+ /*
+ * If the keyword is not found, just return.
+ */
+ if (subst_count == 0)
+ return (may_free ? real : xstrdup(real));
+
+ /*
+ * There is indeed something to substitute. Calculate the
+ * length of the resulting string, and allocate it.
+ */
+ subst_len = strlen(subst);
+ old_len = strlen(real);
+ new_len = old_len + (subst_len - kw_len) * subst_count;
+ res = xmalloc(new_len + 1);
+
+ /*
+ * Now, execute the substitution loop.
+ */
+ for (p = real, resp = res, *resp = '\0';;) {
+ p1 = strstr(p, kw);
+ if (p1 != NULL) {
+ /* Copy the prefix before keyword. */
+ memcpy(resp, p, p1 - p);
+ resp += p1 - p;
+ /* Keyword replacement. */
+ memcpy(resp, subst, subst_len);
+ resp += subst_len;
+ *resp = '\0';
+ p = p1 + kw_len;
+ } else
+ break;
+ }
+
+ /* Copy to the end of string and finish. */
+ strcat(resp, p);
+ if (may_free)
+ free(real);
+ return (res);
+}
+
+static char *
+origin_subst(char *real, const char *origin_path)
+{
+ char *res1, *res2, *res3, *res4;
+
+ if (uts.sysname[0] == '\0') {
+ if (uname(&uts) != 0) {
+ _rtld_error("utsname failed: %d", errno);
+ return (NULL);
+ }
+ }
+ res1 = origin_subst_one(real, "$ORIGIN", origin_path, false);
+ res2 = origin_subst_one(res1, "$OSNAME", uts.sysname, true);
+ res3 = origin_subst_one(res2, "$OSREL", uts.release, true);
+ res4 = origin_subst_one(res3, "$PLATFORM", uts.machine, true);
+ return (res4);
+}
+
+static void
+die(void)
+{
+ const char *msg = dlerror();
+
+ if (msg == NULL)
+ msg = "Fatal error";
+ rtld_fdputstr(STDERR_FILENO, msg);
+ rtld_fdputchar(STDERR_FILENO, '\n');
+ _exit(1);
+}
+
+/*
+ * Process a shared object's DYNAMIC section, and save the important
+ * information in its Obj_Entry structure.
+ */
+static void
+digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath,
+ const Elf_Dyn **dyn_soname, const Elf_Dyn **dyn_runpath)
+{
+ const Elf_Dyn *dynp;
+ Needed_Entry **needed_tail = &obj->needed;
+ Needed_Entry **needed_filtees_tail = &obj->needed_filtees;
+ Needed_Entry **needed_aux_filtees_tail = &obj->needed_aux_filtees;
+ const Elf_Hashelt *hashtab;
+ const Elf32_Word *hashval;
+ Elf32_Word bkt, nmaskwords;
+ int bloom_size32;
+ bool nmw_power2;
+ int plttype = DT_REL;
+
+ *dyn_rpath = NULL;
+ *dyn_soname = NULL;
+ *dyn_runpath = NULL;
+
+ obj->bind_now = false;
+ for (dynp = obj->dynamic; dynp->d_tag != DT_NULL; dynp++) {
+ switch (dynp->d_tag) {
+
+ case DT_REL:
+ obj->rel = (const Elf_Rel *) (obj->relocbase + dynp->d_un.d_ptr);
+ break;
+
+ case DT_RELSZ:
+ obj->relsize = dynp->d_un.d_val;
+ break;
+
+ case DT_RELENT:
+ assert(dynp->d_un.d_val == sizeof(Elf_Rel));
+ break;
+
+ case DT_JMPREL:
+ obj->pltrel = (const Elf_Rel *)
+ (obj->relocbase + dynp->d_un.d_ptr);
+ break;
+
+ case DT_PLTRELSZ:
+ obj->pltrelsize = dynp->d_un.d_val;
+ break;
+
+ case DT_RELA:
+ obj->rela = (const Elf_Rela *) (obj->relocbase + dynp->d_un.d_ptr);
+ break;
+
+ case DT_RELASZ:
+ obj->relasize = dynp->d_un.d_val;
+ break;
+
+ case DT_RELAENT:
+ assert(dynp->d_un.d_val == sizeof(Elf_Rela));
+ break;
+
+ case DT_PLTREL:
+ plttype = dynp->d_un.d_val;
+ assert(dynp->d_un.d_val == DT_REL || plttype == DT_RELA);
+ break;
+
+ case DT_SYMTAB:
+ obj->symtab = (const Elf_Sym *)
+ (obj->relocbase + dynp->d_un.d_ptr);
+ break;
+
+ case DT_SYMENT:
+ assert(dynp->d_un.d_val == sizeof(Elf_Sym));
+ break;
+
+ case DT_STRTAB:
+ obj->strtab = (const char *) (obj->relocbase + dynp->d_un.d_ptr);
+ break;
+
+ case DT_STRSZ:
+ obj->strsize = dynp->d_un.d_val;
+ break;
+
+ case DT_VERNEED:
+ obj->verneed = (const Elf_Verneed *) (obj->relocbase +
+ dynp->d_un.d_val);
+ break;
+
+ case DT_VERNEEDNUM:
+ obj->verneednum = dynp->d_un.d_val;
+ break;
+
+ case DT_VERDEF:
+ obj->verdef = (const Elf_Verdef *) (obj->relocbase +
+ dynp->d_un.d_val);
+ break;
+
+ case DT_VERDEFNUM:
+ obj->verdefnum = dynp->d_un.d_val;
+ break;
+
+ case DT_VERSYM:
+ obj->versyms = (const Elf_Versym *)(obj->relocbase +
+ dynp->d_un.d_val);
+ break;
+
+ case DT_HASH:
+ {
+ hashtab = (const Elf_Hashelt *)(obj->relocbase +
+ dynp->d_un.d_ptr);
+ obj->nbuckets = hashtab[0];
+ obj->nchains = hashtab[1];
+ obj->buckets = hashtab + 2;
+ obj->chains = obj->buckets + obj->nbuckets;
+ obj->valid_hash_sysv = obj->nbuckets > 0 && obj->nchains > 0 &&
+ obj->buckets != NULL;
+ }
+ break;
+
+ case DT_GNU_HASH:
+ {
+ hashtab = (const Elf_Hashelt *)(obj->relocbase +
+ dynp->d_un.d_ptr);
+ obj->nbuckets_gnu = hashtab[0];
+ obj->symndx_gnu = hashtab[1];
+ nmaskwords = hashtab[2];
+ bloom_size32 = (__ELF_WORD_SIZE / 32) * nmaskwords;
+ /* Number of bitmask words is required to be power of 2 */
+ nmw_power2 = ((nmaskwords & (nmaskwords - 1)) == 0);
+ obj->maskwords_bm_gnu = nmaskwords - 1;
+ obj->shift2_gnu = hashtab[3];
+ obj->bloom_gnu = (Elf_Addr *) (hashtab + 4);
+ obj->buckets_gnu = hashtab + 4 + bloom_size32;
+ obj->chain_zero_gnu = obj->buckets_gnu + obj->nbuckets_gnu -
+ obj->symndx_gnu;
+ obj->valid_hash_gnu = nmw_power2 && obj->nbuckets_gnu > 0 &&
+ obj->buckets_gnu != NULL;
+ }
+ break;
+
+ case DT_NEEDED:
+ if (!obj->rtld) {
+ Needed_Entry *nep = NEW(Needed_Entry);
+ nep->name = dynp->d_un.d_val;
+ nep->obj = NULL;
+ nep->next = NULL;
+
+ *needed_tail = nep;
+ needed_tail = &nep->next;
+ }
+ break;
+
+ case DT_FILTER:
+ if (!obj->rtld) {
+ Needed_Entry *nep = NEW(Needed_Entry);
+ nep->name = dynp->d_un.d_val;
+ nep->obj = NULL;
+ nep->next = NULL;
+
+ *needed_filtees_tail = nep;
+ needed_filtees_tail = &nep->next;
+ }
+ break;
+
+ case DT_AUXILIARY:
+ if (!obj->rtld) {
+ Needed_Entry *nep = NEW(Needed_Entry);
+ nep->name = dynp->d_un.d_val;
+ nep->obj = NULL;
+ nep->next = NULL;
+
+ *needed_aux_filtees_tail = nep;
+ needed_aux_filtees_tail = &nep->next;
+ }
+ break;
+
+ case DT_PLTGOT:
+ obj->pltgot = (Elf_Addr *) (obj->relocbase + dynp->d_un.d_ptr);
+ break;
+
+ case DT_TEXTREL:
+ obj->textrel = true;
+ break;
+
+ case DT_SYMBOLIC:
+ obj->symbolic = true;
+ break;
+
+ case DT_RPATH:
+ /*
+ * We have to wait until later to process this, because we
+ * might not have gotten the address of the string table yet.
+ */
+ *dyn_rpath = dynp;
+ break;
+
+ case DT_SONAME:
+ *dyn_soname = dynp;
+ break;
+
+ case DT_RUNPATH:
+ *dyn_runpath = dynp;
+ break;
+
+ case DT_INIT:
+ obj->init = (Elf_Addr) (obj->relocbase + dynp->d_un.d_ptr);
+ break;
+
+ case DT_PREINIT_ARRAY:
+ obj->preinit_array = (Elf_Addr)(obj->relocbase + dynp->d_un.d_ptr);
+ break;
+
+ case DT_PREINIT_ARRAYSZ:
+ obj->preinit_array_num = dynp->d_un.d_val / sizeof(Elf_Addr);
+ break;
+
+ case DT_INIT_ARRAY:
+ obj->init_array = (Elf_Addr)(obj->relocbase + dynp->d_un.d_ptr);
+ break;
+
+ case DT_INIT_ARRAYSZ:
+ obj->init_array_num = dynp->d_un.d_val / sizeof(Elf_Addr);
+ break;
+
+ case DT_FINI:
+ obj->fini = (Elf_Addr) (obj->relocbase + dynp->d_un.d_ptr);
+ break;
+
+ case DT_FINI_ARRAY:
+ obj->fini_array = (Elf_Addr)(obj->relocbase + dynp->d_un.d_ptr);
+ break;
+
+ case DT_FINI_ARRAYSZ:
+ obj->fini_array_num = dynp->d_un.d_val / sizeof(Elf_Addr);
+ break;
+
+ /*
+ * Don't process DT_DEBUG on MIPS as the dynamic section
+ * is mapped read-only. DT_MIPS_RLD_MAP is used instead.
+ */
+
+#ifndef __mips__
+ case DT_DEBUG:
+ /* XXX - not implemented yet */
+ if (!early)
+ dbg("Filling in DT_DEBUG entry");
+ ((Elf_Dyn*)dynp)->d_un.d_ptr = (Elf_Addr) &r_debug;
+ break;
+#endif
+
+ case DT_FLAGS:
+ if ((dynp->d_un.d_val & DF_ORIGIN) && trust)
+ obj->z_origin = true;
+ if (dynp->d_un.d_val & DF_SYMBOLIC)
+ obj->symbolic = true;
+ if (dynp->d_un.d_val & DF_TEXTREL)
+ obj->textrel = true;
+ if (dynp->d_un.d_val & DF_BIND_NOW)
+ obj->bind_now = true;
+ /*if (dynp->d_un.d_val & DF_STATIC_TLS)
+ ;*/
+ break;
+#ifdef __mips__
+ case DT_MIPS_LOCAL_GOTNO:
+ obj->local_gotno = dynp->d_un.d_val;
+ break;
+
+ case DT_MIPS_SYMTABNO:
+ obj->symtabno = dynp->d_un.d_val;
+ break;
+
+ case DT_MIPS_GOTSYM:
+ obj->gotsym = dynp->d_un.d_val;
+ break;
+
+ case DT_MIPS_RLD_MAP:
+ *((Elf_Addr *)(dynp->d_un.d_ptr)) = (Elf_Addr) &r_debug;
+ break;
+#endif
+
+ case DT_FLAGS_1:
+ if (dynp->d_un.d_val & DF_1_NOOPEN)
+ obj->z_noopen = true;
+ if ((dynp->d_un.d_val & DF_1_ORIGIN) && trust)
+ obj->z_origin = true;
+ /*if (dynp->d_un.d_val & DF_1_GLOBAL)
+ XXX ;*/
+ if (dynp->d_un.d_val & DF_1_BIND_NOW)
+ obj->bind_now = true;
+ if (dynp->d_un.d_val & DF_1_NODELETE)
+ obj->z_nodelete = true;
+ if (dynp->d_un.d_val & DF_1_LOADFLTR)
+ obj->z_loadfltr = true;
+ if (dynp->d_un.d_val & DF_1_INTERPOSE)
+ obj->z_interpose = true;
+ if (dynp->d_un.d_val & DF_1_NODEFLIB)
+ obj->z_nodeflib = true;
+ break;
+
+ default:
+ if (!early) {
+ dbg("Ignoring d_tag %ld = %#lx", (long)dynp->d_tag,
+ (long)dynp->d_tag);
+ }
+ break;
+ }
+ }
+
+ obj->traced = false;
+
+ if (plttype == DT_RELA) {
+ obj->pltrela = (const Elf_Rela *) obj->pltrel;
+ obj->pltrel = NULL;
+ obj->pltrelasize = obj->pltrelsize;
+ obj->pltrelsize = 0;
+ }
+
+ /* Determine size of dynsym table (equal to nchains of sysv hash) */
+ if (obj->valid_hash_sysv)
+ obj->dynsymcount = obj->nchains;
+ else if (obj->valid_hash_gnu) {
+ obj->dynsymcount = 0;
+ for (bkt = 0; bkt < obj->nbuckets_gnu; bkt++) {
+ if (obj->buckets_gnu[bkt] == 0)
+ continue;
+ hashval = &obj->chain_zero_gnu[obj->buckets_gnu[bkt]];
+ do
+ obj->dynsymcount++;
+ while ((*hashval++ & 1u) == 0);
+ }
+ obj->dynsymcount += obj->symndx_gnu;
+ }
+}
+
+static void
+digest_dynamic2(Obj_Entry *obj, const Elf_Dyn *dyn_rpath,
+ const Elf_Dyn *dyn_soname, const Elf_Dyn *dyn_runpath)
+{
+
+ if (obj->z_origin && obj->origin_path == NULL) {
+ obj->origin_path = xmalloc(PATH_MAX);
+ if (rtld_dirname_abs(obj->path, obj->origin_path) == -1)
+ die();
+ }
+
+ if (dyn_runpath != NULL) {
+ obj->runpath = (char *)obj->strtab + dyn_runpath->d_un.d_val;
+ if (obj->z_origin)
+ obj->runpath = origin_subst(obj->runpath, obj->origin_path);
+ }
+ else if (dyn_rpath != NULL) {
+ obj->rpath = (char *)obj->strtab + dyn_rpath->d_un.d_val;
+ if (obj->z_origin)
+ obj->rpath = origin_subst(obj->rpath, obj->origin_path);
+ }
+
+ if (dyn_soname != NULL)
+ object_add_name(obj, obj->strtab + dyn_soname->d_un.d_val);
+}
+
+static void
+digest_dynamic(Obj_Entry *obj, int early)
+{
+ const Elf_Dyn *dyn_rpath;
+ const Elf_Dyn *dyn_soname;
+ const Elf_Dyn *dyn_runpath;
+
+ digest_dynamic1(obj, early, &dyn_rpath, &dyn_soname, &dyn_runpath);
+ digest_dynamic2(obj, dyn_rpath, dyn_soname, dyn_runpath);
+}
+
+/*
+ * Process a shared object's program header. This is used only for the
+ * main program, when the kernel has already loaded the main program
+ * into memory before calling the dynamic linker. It creates and
+ * returns an Obj_Entry structure.
+ */
+static Obj_Entry *
+digest_phdr(const Elf_Phdr *phdr, int phnum, caddr_t entry, const char *path)
+{
+ Obj_Entry *obj;
+ const Elf_Phdr *phlimit = phdr + phnum;
+ const Elf_Phdr *ph;
+ Elf_Addr note_start, note_end;
+ int nsegs = 0;
+
+ obj = obj_new();
+ for (ph = phdr; ph < phlimit; ph++) {
+ if (ph->p_type != PT_PHDR)
+ continue;
+
+ obj->phdr = phdr;
+ obj->phsize = ph->p_memsz;
+ obj->relocbase = (caddr_t)phdr - ph->p_vaddr;
+ break;
+ }
+
+ obj->stack_flags = PF_X | PF_R | PF_W;
+
+ for (ph = phdr; ph < phlimit; ph++) {
+ switch (ph->p_type) {
+
+ case PT_INTERP:
+ obj->interp = (const char *)(ph->p_vaddr + obj->relocbase);
+ break;
+
+ case PT_LOAD:
+ if (nsegs == 0) { /* First load segment */
+ obj->vaddrbase = trunc_page(ph->p_vaddr);
+ obj->mapbase = obj->vaddrbase + obj->relocbase;
+ obj->textsize = round_page(ph->p_vaddr + ph->p_memsz) -
+ obj->vaddrbase;
+ } else { /* Last load segment */
+ obj->mapsize = round_page(ph->p_vaddr + ph->p_memsz) -
+ obj->vaddrbase;
+ }
+ nsegs++;
+ break;
+
+ case PT_DYNAMIC:
+ obj->dynamic = (const Elf_Dyn *)(ph->p_vaddr + obj->relocbase);
+ break;
+
+ case PT_TLS:
+ obj->tlsindex = 1;
+ obj->tlssize = ph->p_memsz;
+ obj->tlsalign = ph->p_align;
+ obj->tlsinitsize = ph->p_filesz;
+ obj->tlsinit = (void*)(ph->p_vaddr + obj->relocbase);
+ break;
+
+ case PT_GNU_STACK:
+ obj->stack_flags = ph->p_flags;
+ break;
+
+ case PT_GNU_RELRO:
+ obj->relro_page = obj->relocbase + trunc_page(ph->p_vaddr);
+ obj->relro_size = round_page(ph->p_memsz);
+ break;
+
+ case PT_NOTE:
+ note_start = (Elf_Addr)obj->relocbase + ph->p_vaddr;
+ note_end = note_start + ph->p_filesz;
+ digest_notes(obj, note_start, note_end);
+ break;
+ }
+ }
+ if (nsegs < 1) {
+ _rtld_error("%s: too few PT_LOAD segments", path);
+ return NULL;
+ }
+
+ obj->entry = entry;
+ return obj;
+}
+
+void
+digest_notes(Obj_Entry *obj, Elf_Addr note_start, Elf_Addr note_end)
+{
+ const Elf_Note *note;
+ const char *note_name;
+ uintptr_t p;
+
+ for (note = (const Elf_Note *)note_start; (Elf_Addr)note < note_end;
+ note = (const Elf_Note *)((const char *)(note + 1) +
+ roundup2(note->n_namesz, sizeof(Elf32_Addr)) +
+ roundup2(note->n_descsz, sizeof(Elf32_Addr)))) {
+ if (note->n_namesz != sizeof(NOTE_FREEBSD_VENDOR) ||
+ note->n_descsz != sizeof(int32_t))
+ continue;
+ if (note->n_type != ABI_NOTETYPE &&
+ note->n_type != CRT_NOINIT_NOTETYPE)
+ continue;
+ note_name = (const char *)(note + 1);
+ if (strncmp(NOTE_FREEBSD_VENDOR, note_name,
+ sizeof(NOTE_FREEBSD_VENDOR)) != 0)
+ continue;
+ switch (note->n_type) {
+ case ABI_NOTETYPE:
+ /* FreeBSD osrel note */
+ p = (uintptr_t)(note + 1);
+ p += roundup2(note->n_namesz, sizeof(Elf32_Addr));
+ obj->osrel = *(const int32_t *)(p);
+ dbg("note osrel %d", obj->osrel);
+ break;
+ case CRT_NOINIT_NOTETYPE:
+ /* FreeBSD 'crt does not call init' note */
+ obj->crt_no_init = true;
+ dbg("note crt_no_init");
+ break;
+ }
+ }
+}
+
+static Obj_Entry *
+dlcheck(void *handle)
+{
+ Obj_Entry *obj;
+
+ for (obj = obj_list; obj != NULL; obj = obj->next)
+ if (obj == (Obj_Entry *) handle)
+ break;
+
+ if (obj == NULL || obj->refcount == 0 || obj->dl_refcount == 0) {
+ _rtld_error("Invalid shared object handle %p", handle);
+ return NULL;
+ }
+ return obj;
+}
+
+/*
+ * If the given object is already in the donelist, return true. Otherwise
+ * add the object to the list and return false.
+ */
+static bool
+donelist_check(DoneList *dlp, const Obj_Entry *obj)
+{
+ unsigned int i;
+
+ for (i = 0; i < dlp->num_used; i++)
+ if (dlp->objs[i] == obj)
+ return true;
+ /*
+ * Our donelist allocation should always be sufficient. But if
+ * our threads locking isn't working properly, more shared objects
+ * could have been loaded since we allocated the list. That should
+ * never happen, but we'll handle it properly just in case it does.
+ */
+ if (dlp->num_used < dlp->num_alloc)
+ dlp->objs[dlp->num_used++] = obj;
+ return false;
+}
+
+/*
+ * Hash function for symbol table lookup. Don't even think about changing
+ * this. It is specified by the System V ABI.
+ */
+unsigned long
+elf_hash(const char *name)
+{
+ const unsigned char *p = (const unsigned char *) name;
+ unsigned long h = 0;
+ unsigned long g;
+
+ while (*p != '\0') {
+ h = (h << 4) + *p++;
+ if ((g = h & 0xf0000000) != 0)
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ return h;
+}
+
+/*
+ * The GNU hash function is the Daniel J. Bernstein hash clipped to 32 bits
+ * unsigned in case it's implemented with a wider type.
+ */
+static uint32_t
+gnu_hash(const char *s)
+{
+ uint32_t h;
+ unsigned char c;
+
+ h = 5381;
+ for (c = *s; c != '\0'; c = *++s)
+ h = h * 33 + c;
+ return (h & 0xffffffff);
+}
+
+
+/*
+ * Find the library with the given name, and return its full pathname.
+ * The returned string is dynamically allocated. Generates an error
+ * message and returns NULL if the library cannot be found.
+ *
+ * If the second argument is non-NULL, then it refers to an already-
+ * loaded shared object, whose library search path will be searched.
+ *
+ * If a library is successfully located via LD_LIBRARY_PATH_FDS, its
+ * descriptor (which is close-on-exec) will be passed out via the third
+ * argument.
+ *
+ * The search order is:
+ * DT_RPATH in the referencing file _unless_ DT_RUNPATH is present (1)
+ * DT_RPATH of the main object if DSO without defined DT_RUNPATH (1)
+ * LD_LIBRARY_PATH
+ * DT_RUNPATH in the referencing file
+ * ldconfig hints (if -z nodefaultlib, filter out default library directories
+ * from list)
+ * /lib:/usr/lib _unless_ the referencing file is linked with -z nodefaultlib
+ *
+ * (1) Handled in digest_dynamic2 - rpath left NULL if runpath defined.
+ */
+static char *
+find_library(const char *xname, const Obj_Entry *refobj, int *fdp)
+{
+ char *pathname;
+ char *name;
+ bool nodeflib, objgiven;
+
+ objgiven = refobj != NULL;
+ if (strchr(xname, '/') != NULL) { /* Hard coded pathname */
+ if (xname[0] != '/' && !trust) {
+ _rtld_error("Absolute pathname required for shared object \"%s\"",
+ xname);
+ return NULL;
+ }
+ if (objgiven && refobj->z_origin) {
+ return (origin_subst(__DECONST(char *, xname),
+ refobj->origin_path));
+ } else {
+ return (xstrdup(xname));
+ }
+ }
+
+ if (libmap_disable || !objgiven ||
+ (name = lm_find(refobj->path, xname)) == NULL)
+ name = (char *)xname;
+
+ dbg(" Searching for \"%s\"", name);
+
+ /*
+ * If refobj->rpath != NULL, then refobj->runpath is NULL. Fall
+ * back to pre-conforming behaviour if user requested so with
+ * LD_LIBRARY_PATH_RPATH environment variable and ignore -z
+ * nodeflib.
+ */
+ if (objgiven && refobj->rpath != NULL && ld_library_path_rpath) {
+ if ((pathname = search_library_path(name, ld_library_path)) != NULL ||
+ (refobj != NULL &&
+ (pathname = search_library_path(name, refobj->rpath)) != NULL) ||
+ (pathname = search_library_pathfds(name, ld_library_dirs, fdp)) != NULL ||
+ (pathname = search_library_path(name, gethints(false))) != NULL ||
+ (pathname = search_library_path(name, STANDARD_LIBRARY_PATH)) != NULL)
+ return (pathname);
+ } else {
+ nodeflib = objgiven ? refobj->z_nodeflib : false;
+ if ((objgiven &&
+ (pathname = search_library_path(name, refobj->rpath)) != NULL) ||
+ (objgiven && refobj->runpath == NULL && refobj != obj_main &&
+ (pathname = search_library_path(name, obj_main->rpath)) != NULL) ||
+ (pathname = search_library_path(name, ld_library_path)) != NULL ||
+ (objgiven &&
+ (pathname = search_library_path(name, refobj->runpath)) != NULL) ||
+ (pathname = search_library_pathfds(name, ld_library_dirs, fdp)) != NULL ||
+ (pathname = search_library_path(name, gethints(nodeflib))) != NULL ||
+ (objgiven && !nodeflib &&
+ (pathname = search_library_path(name, STANDARD_LIBRARY_PATH)) != NULL))
+ return (pathname);
+ }
+
+ if (objgiven && refobj->path != NULL) {
+ _rtld_error("Shared object \"%s\" not found, required by \"%s\"",
+ name, basename(refobj->path));
+ } else {
+ _rtld_error("Shared object \"%s\" not found", name);
+ }
+ return NULL;
+}
+
+/*
+ * Given a symbol number in a referencing object, find the corresponding
+ * definition of the symbol. Returns a pointer to the symbol, or NULL if
+ * no definition was found. Returns a pointer to the Obj_Entry of the
+ * defining object via the reference parameter DEFOBJ_OUT.
+ */
+const Elf_Sym *
+find_symdef(unsigned long symnum, const Obj_Entry *refobj,
+ const Obj_Entry **defobj_out, int flags, SymCache *cache,
+ RtldLockState *lockstate)
+{
+ const Elf_Sym *ref;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+ SymLook req;
+ const char *name;
+ int res;
+
+ /*
+ * If we have already found this symbol, get the information from
+ * the cache.
+ */
+ if (symnum >= refobj->dynsymcount)
+ return NULL; /* Bad object */
+ if (cache != NULL && cache[symnum].sym != NULL) {
+ *defobj_out = cache[symnum].obj;
+ return cache[symnum].sym;
+ }
+
+ ref = refobj->symtab + symnum;
+ name = refobj->strtab + ref->st_name;
+ def = NULL;
+ defobj = NULL;
+
+ /*
+ * We don't have to do a full scale lookup if the symbol is local.
+ * We know it will bind to the instance in this load module; to
+ * which we already have a pointer (ie ref). By not doing a lookup,
+ * we not only improve performance, but it also avoids unresolvable
+ * symbols when local symbols are not in the hash table. This has
+ * been seen with the ia64 toolchain.
+ */
+ if (ELF_ST_BIND(ref->st_info) != STB_LOCAL) {
+ if (ELF_ST_TYPE(ref->st_info) == STT_SECTION) {
+ _rtld_error("%s: Bogus symbol table entry %lu", refobj->path,
+ symnum);
+ }
+ symlook_init(&req, name);
+ req.flags = flags;
+ req.ventry = fetch_ventry(refobj, symnum);
+ req.lockstate = lockstate;
+ res = symlook_default(&req, refobj);
+ if (res == 0) {
+ def = req.sym_out;
+ defobj = req.defobj_out;
+ }
+ } else {
+ def = ref;
+ defobj = refobj;
+ }
+
+ /*
+ * If we found no definition and the reference is weak, treat the
+ * symbol as having the value zero.
+ */
+ if (def == NULL && ELF_ST_BIND(ref->st_info) == STB_WEAK) {
+ def = &sym_zero;
+ defobj = obj_main;
+ }
+
+ if (def != NULL) {
+ *defobj_out = defobj;
+ /* Record the information in the cache to avoid subsequent lookups. */
+ if (cache != NULL) {
+ cache[symnum].sym = def;
+ cache[symnum].obj = defobj;
+ }
+ } else {
+ if (refobj != &obj_rtld)
+ _rtld_error("%s: Undefined symbol \"%s\"", refobj->path, name);
+ }
+ return def;
+}
+
+/*
+ * Return the search path from the ldconfig hints file, reading it if
+ * necessary. If nostdlib is true, then the default search paths are
+ * not added to result.
+ *
+ * Returns NULL if there are problems with the hints file,
+ * or if the search path there is empty.
+ */
+static const char *
+gethints(bool nostdlib)
+{
+ static char *hints, *filtered_path;
+ struct elfhints_hdr hdr;
+ struct fill_search_info_args sargs, hargs;
+ struct dl_serinfo smeta, hmeta, *SLPinfo, *hintinfo;
+ struct dl_serpath *SLPpath, *hintpath;
+ char *p;
+ unsigned int SLPndx, hintndx, fndx, fcount;
+ int fd;
+ size_t flen;
+ bool skip;
+
+ /* First call, read the hints file */
+ if (hints == NULL) {
+ /* Keep from trying again in case the hints file is bad. */
+ hints = "";
+
+ if ((fd = open(ld_elf_hints_path, O_RDONLY | O_CLOEXEC)) == -1)
+ return (NULL);
+ if (read(fd, &hdr, sizeof hdr) != sizeof hdr ||
+ hdr.magic != ELFHINTS_MAGIC ||
+ hdr.version != 1) {
+ close(fd);
+ return (NULL);
+ }
+ p = xmalloc(hdr.dirlistlen + 1);
+ if (lseek(fd, hdr.strtab + hdr.dirlist, SEEK_SET) == -1 ||
+ read(fd, p, hdr.dirlistlen + 1) !=
+ (ssize_t)hdr.dirlistlen + 1) {
+ free(p);
+ close(fd);
+ return (NULL);
+ }
+ hints = p;
+ close(fd);
+ }
+
+ /*
+ * If caller agreed to receive list which includes the default
+ * paths, we are done. Otherwise, if we still did not
+ * calculated filtered result, do it now.
+ */
+ if (!nostdlib)
+ return (hints[0] != '\0' ? hints : NULL);
+ if (filtered_path != NULL)
+ goto filt_ret;
+
+ /*
+ * Obtain the list of all configured search paths, and the
+ * list of the default paths.
+ *
+ * First estimate the size of the results.
+ */
+ smeta.dls_size = __offsetof(struct dl_serinfo, dls_serpath);
+ smeta.dls_cnt = 0;
+ hmeta.dls_size = __offsetof(struct dl_serinfo, dls_serpath);
+ hmeta.dls_cnt = 0;
+
+ sargs.request = RTLD_DI_SERINFOSIZE;
+ sargs.serinfo = &smeta;
+ hargs.request = RTLD_DI_SERINFOSIZE;
+ hargs.serinfo = &hmeta;
+
+ path_enumerate(STANDARD_LIBRARY_PATH, fill_search_info, &sargs);
+ path_enumerate(p, fill_search_info, &hargs);
+
+ SLPinfo = xmalloc(smeta.dls_size);
+ hintinfo = xmalloc(hmeta.dls_size);
+
+ /*
+ * Next fetch both sets of paths.
+ */
+ sargs.request = RTLD_DI_SERINFO;
+ sargs.serinfo = SLPinfo;
+ sargs.serpath = &SLPinfo->dls_serpath[0];
+ sargs.strspace = (char *)&SLPinfo->dls_serpath[smeta.dls_cnt];
+
+ hargs.request = RTLD_DI_SERINFO;
+ hargs.serinfo = hintinfo;
+ hargs.serpath = &hintinfo->dls_serpath[0];
+ hargs.strspace = (char *)&hintinfo->dls_serpath[hmeta.dls_cnt];
+
+ path_enumerate(STANDARD_LIBRARY_PATH, fill_search_info, &sargs);
+ path_enumerate(p, fill_search_info, &hargs);
+
+ /*
+ * Now calculate the difference between two sets, by excluding
+ * standard paths from the full set.
+ */
+ fndx = 0;
+ fcount = 0;
+ filtered_path = xmalloc(hdr.dirlistlen + 1);
+ hintpath = &hintinfo->dls_serpath[0];
+ for (hintndx = 0; hintndx < hmeta.dls_cnt; hintndx++, hintpath++) {
+ skip = false;
+ SLPpath = &SLPinfo->dls_serpath[0];
+ /*
+ * Check each standard path against current.
+ */
+ for (SLPndx = 0; SLPndx < smeta.dls_cnt; SLPndx++, SLPpath++) {
+ /* matched, skip the path */
+ if (!strcmp(hintpath->dls_name, SLPpath->dls_name)) {
+ skip = true;
+ break;
+ }
+ }
+ if (skip)
+ continue;
+ /*
+ * Not matched against any standard path, add the path
+ * to result. Separate consequtive paths with ':'.
+ */
+ if (fcount > 0) {
+ filtered_path[fndx] = ':';
+ fndx++;
+ }
+ fcount++;
+ flen = strlen(hintpath->dls_name);
+ strncpy((filtered_path + fndx), hintpath->dls_name, flen);
+ fndx += flen;
+ }
+ filtered_path[fndx] = '\0';
+
+ free(SLPinfo);
+ free(hintinfo);
+
+filt_ret:
+ return (filtered_path[0] != '\0' ? filtered_path : NULL);
+}
+
+static void
+init_dag(Obj_Entry *root)
+{
+ const Needed_Entry *needed;
+ const Objlist_Entry *elm;
+ DoneList donelist;
+
+ if (root->dag_inited)
+ return;
+ donelist_init(&donelist);
+
+ /* Root object belongs to own DAG. */
+ objlist_push_tail(&root->dldags, root);
+ objlist_push_tail(&root->dagmembers, root);
+ donelist_check(&donelist, root);
+
+ /*
+ * Add dependencies of root object to DAG in breadth order
+ * by exploiting the fact that each new object get added
+ * to the tail of the dagmembers list.
+ */
+ STAILQ_FOREACH(elm, &root->dagmembers, link) {
+ for (needed = elm->obj->needed; needed != NULL; needed = needed->next) {
+ if (needed->obj == NULL || donelist_check(&donelist, needed->obj))
+ continue;
+ objlist_push_tail(&needed->obj->dldags, root);
+ objlist_push_tail(&root->dagmembers, needed->obj);
+ }
+ }
+ root->dag_inited = true;
+}
+
+static void
+process_nodelete(Obj_Entry *root)
+{
+ const Objlist_Entry *elm;
+
+ /*
+ * Walk over object DAG and process every dependent object that
+ * is marked as DF_1_NODELETE. They need to grow their own DAG,
+ * which then should have its reference upped separately.
+ */
+ STAILQ_FOREACH(elm, &root->dagmembers, link) {
+ if (elm->obj != NULL && elm->obj->z_nodelete &&
+ !elm->obj->ref_nodel) {
+ dbg("obj %s nodelete", elm->obj->path);
+ init_dag(elm->obj);
+ ref_dag(elm->obj);
+ elm->obj->ref_nodel = true;
+ }
+ }
+}
+/*
+ * Initialize the dynamic linker. The argument is the address at which
+ * the dynamic linker has been mapped into memory. The primary task of
+ * this function is to relocate the dynamic linker.
+ */
+static void
+init_rtld(caddr_t mapbase, Elf_Auxinfo **aux_info)
+{
+ Obj_Entry objtmp; /* Temporary rtld object */
+ const Elf_Dyn *dyn_rpath;
+ const Elf_Dyn *dyn_soname;
+ const Elf_Dyn *dyn_runpath;
+
+#ifdef RTLD_INIT_PAGESIZES_EARLY
+ /* The page size is required by the dynamic memory allocator. */
+ init_pagesizes(aux_info);
+#endif
+
+ /*
+ * Conjure up an Obj_Entry structure for the dynamic linker.
+ *
+ * The "path" member can't be initialized yet because string constants
+ * cannot yet be accessed. Below we will set it correctly.
+ */
+ memset(&objtmp, 0, sizeof(objtmp));
+ objtmp.path = NULL;
+ objtmp.rtld = true;
+ objtmp.mapbase = mapbase;
+#ifdef PIC
+ objtmp.relocbase = mapbase;
+#endif
+ if (RTLD_IS_DYNAMIC()) {
+ objtmp.dynamic = rtld_dynamic(&objtmp);
+ digest_dynamic1(&objtmp, 1, &dyn_rpath, &dyn_soname, &dyn_runpath);
+ assert(objtmp.needed == NULL);
+#if !defined(__mips__)
+ /* MIPS has a bogus DT_TEXTREL. */
+ assert(!objtmp.textrel);
+#endif
+
+ /*
+ * Temporarily put the dynamic linker entry into the object list, so
+ * that symbols can be found.
+ */
+
+ relocate_objects(&objtmp, true, &objtmp, 0, NULL);
+ }
+
+ /* Initialize the object list. */
+ obj_tail = &obj_list;
+
+ /* Now that non-local variables can be accesses, copy out obj_rtld. */
+ memcpy(&obj_rtld, &objtmp, sizeof(obj_rtld));
+
+#ifndef RTLD_INIT_PAGESIZES_EARLY
+ /* The page size is required by the dynamic memory allocator. */
+ init_pagesizes(aux_info);
+#endif
+
+ if (aux_info[AT_OSRELDATE] != NULL)
+ osreldate = aux_info[AT_OSRELDATE]->a_un.a_val;
+
+ digest_dynamic2(&obj_rtld, dyn_rpath, dyn_soname, dyn_runpath);
+
+ /* Replace the path with a dynamically allocated copy. */
+ obj_rtld.path = xstrdup(PATH_RTLD);
+
+ r_debug.r_brk = r_debug_state;
+ r_debug.r_state = RT_CONSISTENT;
+}
+
+/*
+ * Retrieve the array of supported page sizes. The kernel provides the page
+ * sizes in increasing order.
+ */
+static void
+init_pagesizes(Elf_Auxinfo **aux_info)
+{
+ static size_t psa[MAXPAGESIZES];
+ int mib[2];
+ size_t len, size;
+
+ if (aux_info[AT_PAGESIZES] != NULL && aux_info[AT_PAGESIZESLEN] !=
+ NULL) {
+ size = aux_info[AT_PAGESIZESLEN]->a_un.a_val;
+ pagesizes = aux_info[AT_PAGESIZES]->a_un.a_ptr;
+ } else {
+ len = 2;
+ if (sysctlnametomib("hw.pagesizes", mib, &len) == 0)
+ size = sizeof(psa);
+ else {
+ /* As a fallback, retrieve the base page size. */
+ size = sizeof(psa[0]);
+ if (aux_info[AT_PAGESZ] != NULL) {
+ psa[0] = aux_info[AT_PAGESZ]->a_un.a_val;
+ goto psa_filled;
+ } else {
+ mib[0] = CTL_HW;
+ mib[1] = HW_PAGESIZE;
+ len = 2;
+ }
+ }
+ if (sysctl(mib, len, psa, &size, NULL, 0) == -1) {
+ _rtld_error("sysctl for hw.pagesize(s) failed");
+ die();
+ }
+psa_filled:
+ pagesizes = psa;
+ }
+ npagesizes = size / sizeof(pagesizes[0]);
+ /* Discard any invalid entries at the end of the array. */
+ while (npagesizes > 0 && pagesizes[npagesizes - 1] == 0)
+ npagesizes--;
+}
+
+/*
+ * Add the init functions from a needed object list (and its recursive
+ * needed objects) to "list". This is not used directly; it is a helper
+ * function for initlist_add_objects(). The write lock must be held
+ * when this function is called.
+ */
+static void
+initlist_add_neededs(Needed_Entry *needed, Objlist *list)
+{
+ /* Recursively process the successor needed objects. */
+ if (needed->next != NULL)
+ initlist_add_neededs(needed->next, list);
+
+ /* Process the current needed object. */
+ if (needed->obj != NULL)
+ initlist_add_objects(needed->obj, &needed->obj->next, list);
+}
+
+/*
+ * Scan all of the DAGs rooted in the range of objects from "obj" to
+ * "tail" and add their init functions to "list". This recurses over
+ * the DAGs and ensure the proper init ordering such that each object's
+ * needed libraries are initialized before the object itself. At the
+ * same time, this function adds the objects to the global finalization
+ * list "list_fini" in the opposite order. The write lock must be
+ * held when this function is called.
+ */
+static void
+initlist_add_objects(Obj_Entry *obj, Obj_Entry **tail, Objlist *list)
+{
+
+ if (obj->init_scanned || obj->init_done)
+ return;
+ obj->init_scanned = true;
+
+ /* Recursively process the successor objects. */
+ if (&obj->next != tail)
+ initlist_add_objects(obj->next, tail, list);
+
+ /* Recursively process the needed objects. */
+ if (obj->needed != NULL)
+ initlist_add_neededs(obj->needed, list);
+ if (obj->needed_filtees != NULL)
+ initlist_add_neededs(obj->needed_filtees, list);
+ if (obj->needed_aux_filtees != NULL)
+ initlist_add_neededs(obj->needed_aux_filtees, list);
+
+ /* Add the object to the init list. */
+ if (obj->preinit_array != (Elf_Addr)NULL || obj->init != (Elf_Addr)NULL ||
+ obj->init_array != (Elf_Addr)NULL)
+ objlist_push_tail(list, obj);
+
+ /* Add the object to the global fini list in the reverse order. */
+ if ((obj->fini != (Elf_Addr)NULL || obj->fini_array != (Elf_Addr)NULL)
+ && !obj->on_fini_list) {
+ objlist_push_head(&list_fini, obj);
+ obj->on_fini_list = true;
+ }
+}
+
+#ifndef FPTR_TARGET
+#define FPTR_TARGET(f) ((Elf_Addr) (f))
+#endif
+
+static void
+free_needed_filtees(Needed_Entry *n)
+{
+ Needed_Entry *needed, *needed1;
+
+ for (needed = n; needed != NULL; needed = needed->next) {
+ if (needed->obj != NULL) {
+ dlclose(needed->obj);
+ needed->obj = NULL;
+ }
+ }
+ for (needed = n; needed != NULL; needed = needed1) {
+ needed1 = needed->next;
+ free(needed);
+ }
+}
+
+static void
+unload_filtees(Obj_Entry *obj)
+{
+
+ free_needed_filtees(obj->needed_filtees);
+ obj->needed_filtees = NULL;
+ free_needed_filtees(obj->needed_aux_filtees);
+ obj->needed_aux_filtees = NULL;
+ obj->filtees_loaded = false;
+}
+
+static void
+load_filtee1(Obj_Entry *obj, Needed_Entry *needed, int flags,
+ RtldLockState *lockstate)
+{
+
+ for (; needed != NULL; needed = needed->next) {
+ needed->obj = dlopen_object(obj->strtab + needed->name, -1, obj,
+ flags, ((ld_loadfltr || obj->z_loadfltr) ? RTLD_NOW : RTLD_LAZY) |
+ RTLD_LOCAL, lockstate);
+ }
+}
+
+static void
+load_filtees(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+
+ lock_restart_for_upgrade(lockstate);
+ if (!obj->filtees_loaded) {
+ load_filtee1(obj, obj->needed_filtees, flags, lockstate);
+ load_filtee1(obj, obj->needed_aux_filtees, flags, lockstate);
+ obj->filtees_loaded = true;
+ }
+}
+
+static int
+process_needed(Obj_Entry *obj, Needed_Entry *needed, int flags)
+{
+ Obj_Entry *obj1;
+
+ for (; needed != NULL; needed = needed->next) {
+ obj1 = needed->obj = load_object(obj->strtab + needed->name, -1, obj,
+ flags & ~RTLD_LO_NOLOAD);
+ if (obj1 == NULL && !ld_tracing && (flags & RTLD_LO_FILTEES) == 0)
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Given a shared object, traverse its list of needed objects, and load
+ * each of them. Returns 0 on success. Generates an error message and
+ * returns -1 on failure.
+ */
+static int
+load_needed_objects(Obj_Entry *first, int flags)
+{
+ Obj_Entry *obj;
+
+ for (obj = first; obj != NULL; obj = obj->next) {
+ if (process_needed(obj, obj->needed, flags) == -1)
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+load_preload_objects(void)
+{
+ char *p = ld_preload;
+ Obj_Entry *obj;
+ static const char delim[] = " \t:;";
+
+ if (p == NULL)
+ return 0;
+
+ p += strspn(p, delim);
+ while (*p != '\0') {
+ size_t len = strcspn(p, delim);
+ char savech;
+
+ savech = p[len];
+ p[len] = '\0';
+ obj = load_object(p, -1, NULL, 0);
+ if (obj == NULL)
+ return -1; /* XXX - cleanup */
+ obj->z_interpose = true;
+ p[len] = savech;
+ p += len;
+ p += strspn(p, delim);
+ }
+ LD_UTRACE(UTRACE_PRELOAD_FINISHED, NULL, NULL, 0, 0, NULL);
+ return 0;
+}
+
+static const char *
+printable_path(const char *path)
+{
+
+ return (path == NULL ? "<unknown>" : path);
+}
+
+/*
+ * Load a shared object into memory, if it is not already loaded. The
+ * object may be specified by name or by user-supplied file descriptor
+ * fd_u. In the later case, the fd_u descriptor is not closed, but its
+ * duplicate is.
+ *
+ * Returns a pointer to the Obj_Entry for the object. Returns NULL
+ * on failure.
+ */
+static Obj_Entry *
+load_object(const char *name, int fd_u, const Obj_Entry *refobj, int flags)
+{
+ Obj_Entry *obj;
+ int fd;
+ struct stat sb;
+ char *path;
+
+ fd = -1;
+ if (name != NULL) {
+ for (obj = obj_list->next; obj != NULL; obj = obj->next) {
+ if (object_match_name(obj, name))
+ return (obj);
+ }
+
+ path = find_library(name, refobj, &fd);
+ if (path == NULL)
+ return (NULL);
+ } else
+ path = NULL;
+
+ if (fd >= 0) {
+ /*
+ * search_library_pathfds() opens a fresh file descriptor for the
+ * library, so there is no need to dup().
+ */
+ } else if (fd_u == -1) {
+ /*
+ * If we didn't find a match by pathname, or the name is not
+ * supplied, open the file and check again by device and inode.
+ * This avoids false mismatches caused by multiple links or ".."
+ * in pathnames.
+ *
+ * To avoid a race, we open the file and use fstat() rather than
+ * using stat().
+ */
+ if ((fd = open(path, O_RDONLY | O_CLOEXEC)) == -1) {
+ _rtld_error("Cannot open \"%s\"", path);
+ free(path);
+ return (NULL);
+ }
+ } else {
+ fd = fcntl(fd_u, F_DUPFD_CLOEXEC, 0);
+ if (fd == -1) {
+ _rtld_error("Cannot dup fd");
+ free(path);
+ return (NULL);
+ }
+ }
+ if (fstat(fd, &sb) == -1) {
+ _rtld_error("Cannot fstat \"%s\"", printable_path(path));
+ close(fd);
+ free(path);
+ return NULL;
+ }
+ for (obj = obj_list->next; obj != NULL; obj = obj->next)
+ if (obj->ino == sb.st_ino && obj->dev == sb.st_dev)
+ break;
+ if (obj != NULL && name != NULL) {
+ object_add_name(obj, name);
+ free(path);
+ close(fd);
+ return obj;
+ }
+ if (flags & RTLD_LO_NOLOAD) {
+ free(path);
+ close(fd);
+ return (NULL);
+ }
+
+ /* First use of this object, so we must map it in */
+ obj = do_load_object(fd, name, path, &sb, flags);
+ if (obj == NULL)
+ free(path);
+ close(fd);
+
+ return obj;
+}
+
+static Obj_Entry *
+do_load_object(int fd, const char *name, char *path, struct stat *sbp,
+ int flags)
+{
+ Obj_Entry *obj;
+ struct statfs fs;
+
+ /*
+ * but first, make sure that environment variables haven't been
+ * used to circumvent the noexec flag on a filesystem.
+ */
+ if (dangerous_ld_env) {
+ if (fstatfs(fd, &fs) != 0) {
+ _rtld_error("Cannot fstatfs \"%s\"", printable_path(path));
+ return NULL;
+ }
+ if (fs.f_flags & MNT_NOEXEC) {
+ _rtld_error("Cannot execute objects on %s\n", fs.f_mntonname);
+ return NULL;
+ }
+ }
+ dbg("loading \"%s\"", printable_path(path));
+ obj = map_object(fd, printable_path(path), sbp);
+ if (obj == NULL)
+ return NULL;
+
+ /*
+ * If DT_SONAME is present in the object, digest_dynamic2 already
+ * added it to the object names.
+ */
+ if (name != NULL)
+ object_add_name(obj, name);
+ obj->path = path;
+ digest_dynamic(obj, 0);
+ dbg("%s valid_hash_sysv %d valid_hash_gnu %d dynsymcount %d", obj->path,
+ obj->valid_hash_sysv, obj->valid_hash_gnu, obj->dynsymcount);
+ if (obj->z_noopen && (flags & (RTLD_LO_DLOPEN | RTLD_LO_TRACE)) ==
+ RTLD_LO_DLOPEN) {
+ dbg("refusing to load non-loadable \"%s\"", obj->path);
+ _rtld_error("Cannot dlopen non-loadable %s", obj->path);
+ munmap(obj->mapbase, obj->mapsize);
+ obj_free(obj);
+ return (NULL);
+ }
+
+ *obj_tail = obj;
+ obj_tail = &obj->next;
+ obj_count++;
+ obj_loads++;
+ linkmap_add(obj); /* for GDB & dlinfo() */
+ max_stack_flags |= obj->stack_flags;
+
+ dbg(" %p .. %p: %s", obj->mapbase,
+ obj->mapbase + obj->mapsize - 1, obj->path);
+ if (obj->textrel)
+ dbg(" WARNING: %s has impure text", obj->path);
+ LD_UTRACE(UTRACE_LOAD_OBJECT, obj, obj->mapbase, obj->mapsize, 0,
+ obj->path);
+
+ return obj;
+}
+
+static Obj_Entry *
+obj_from_addr(const void *addr)
+{
+ Obj_Entry *obj;
+
+ for (obj = obj_list; obj != NULL; obj = obj->next) {
+ if (addr < (void *) obj->mapbase)
+ continue;
+ if (addr < (void *) (obj->mapbase + obj->mapsize))
+ return obj;
+ }
+ return NULL;
+}
+
+static void
+preinit_main(void)
+{
+ Elf_Addr *preinit_addr;
+ int index;
+
+ preinit_addr = (Elf_Addr *)obj_main->preinit_array;
+ if (preinit_addr == NULL)
+ return;
+
+ for (index = 0; index < obj_main->preinit_array_num; index++) {
+ if (preinit_addr[index] != 0 && preinit_addr[index] != 1) {
+ dbg("calling preinit function for %s at %p", obj_main->path,
+ (void *)preinit_addr[index]);
+ LD_UTRACE(UTRACE_INIT_CALL, obj_main, (void *)preinit_addr[index],
+ 0, 0, obj_main->path);
+ call_init_pointer(obj_main, preinit_addr[index]);
+ }
+ }
+}
+
+/*
+ * Call the finalization functions for each of the objects in "list"
+ * belonging to the DAG of "root" and referenced once. If NULL "root"
+ * is specified, every finalization function will be called regardless
+ * of the reference count and the list elements won't be freed. All of
+ * the objects are expected to have non-NULL fini functions.
+ */
+static void
+objlist_call_fini(Objlist *list, Obj_Entry *root, RtldLockState *lockstate)
+{
+ Objlist_Entry *elm;
+ char *saved_msg;
+ Elf_Addr *fini_addr;
+ int index;
+
+ assert(root == NULL || root->refcount == 1);
+
+ /*
+ * Preserve the current error message since a fini function might
+ * call into the dynamic linker and overwrite it.
+ */
+ saved_msg = errmsg_save();
+ do {
+ STAILQ_FOREACH(elm, list, link) {
+ if (root != NULL && (elm->obj->refcount != 1 ||
+ objlist_find(&root->dagmembers, elm->obj) == NULL))
+ continue;
+ /* Remove object from fini list to prevent recursive invocation. */
+ STAILQ_REMOVE(list, elm, Struct_Objlist_Entry, link);
+ /*
+ * XXX: If a dlopen() call references an object while the
+ * fini function is in progress, we might end up trying to
+ * unload the referenced object in dlclose() or the object
+ * won't be unloaded although its fini function has been
+ * called.
+ */
+ lock_release(rtld_bind_lock, lockstate);
+
+ /*
+ * It is legal to have both DT_FINI and DT_FINI_ARRAY defined.
+ * When this happens, DT_FINI_ARRAY is processed first.
+ */
+ fini_addr = (Elf_Addr *)elm->obj->fini_array;
+ if (fini_addr != NULL && elm->obj->fini_array_num > 0) {
+ for (index = elm->obj->fini_array_num - 1; index >= 0;
+ index--) {
+ if (fini_addr[index] != 0 && fini_addr[index] != 1) {
+ dbg("calling fini function for %s at %p",
+ elm->obj->path, (void *)fini_addr[index]);
+ LD_UTRACE(UTRACE_FINI_CALL, elm->obj,
+ (void *)fini_addr[index], 0, 0, elm->obj->path);
+ call_initfini_pointer(elm->obj, fini_addr[index]);
+ }
+ }
+ }
+ if (elm->obj->fini != (Elf_Addr)NULL) {
+ dbg("calling fini function for %s at %p", elm->obj->path,
+ (void *)elm->obj->fini);
+ LD_UTRACE(UTRACE_FINI_CALL, elm->obj, (void *)elm->obj->fini,
+ 0, 0, elm->obj->path);
+ call_initfini_pointer(elm->obj, elm->obj->fini);
+ }
+ wlock_acquire(rtld_bind_lock, lockstate);
+ /* No need to free anything if process is going down. */
+ if (root != NULL)
+ free(elm);
+ /*
+ * We must restart the list traversal after every fini call
+ * because a dlclose() call from the fini function or from
+ * another thread might have modified the reference counts.
+ */
+ break;
+ }
+ } while (elm != NULL);
+ errmsg_restore(saved_msg);
+}
+
+/*
+ * Call the initialization functions for each of the objects in
+ * "list". All of the objects are expected to have non-NULL init
+ * functions.
+ */
+static void
+objlist_call_init(Objlist *list, RtldLockState *lockstate)
+{
+ Objlist_Entry *elm;
+ Obj_Entry *obj;
+ char *saved_msg;
+ Elf_Addr *init_addr;
+ int index;
+
+ /*
+ * Clean init_scanned flag so that objects can be rechecked and
+ * possibly initialized earlier if any of vectors called below
+ * cause the change by using dlopen.
+ */
+ for (obj = obj_list; obj != NULL; obj = obj->next)
+ obj->init_scanned = false;
+
+ /*
+ * Preserve the current error message since an init function might
+ * call into the dynamic linker and overwrite it.
+ */
+ saved_msg = errmsg_save();
+ STAILQ_FOREACH(elm, list, link) {
+ if (elm->obj->init_done) /* Initialized early. */
+ continue;
+ /*
+ * Race: other thread might try to use this object before current
+ * one completes the initilization. Not much can be done here
+ * without better locking.
+ */
+ elm->obj->init_done = true;
+ lock_release(rtld_bind_lock, lockstate);
+
+ /*
+ * It is legal to have both DT_INIT and DT_INIT_ARRAY defined.
+ * When this happens, DT_INIT is processed first.
+ */
+ if (elm->obj->init != (Elf_Addr)NULL) {
+ dbg("calling init function for %s at %p", elm->obj->path,
+ (void *)elm->obj->init);
+ LD_UTRACE(UTRACE_INIT_CALL, elm->obj, (void *)elm->obj->init,
+ 0, 0, elm->obj->path);
+ call_initfini_pointer(elm->obj, elm->obj->init);
+ }
+ init_addr = (Elf_Addr *)elm->obj->init_array;
+ if (init_addr != NULL) {
+ for (index = 0; index < elm->obj->init_array_num; index++) {
+ if (init_addr[index] != 0 && init_addr[index] != 1) {
+ dbg("calling init function for %s at %p", elm->obj->path,
+ (void *)init_addr[index]);
+ LD_UTRACE(UTRACE_INIT_CALL, elm->obj,
+ (void *)init_addr[index], 0, 0, elm->obj->path);
+ call_init_pointer(elm->obj, init_addr[index]);
+ }
+ }
+ }
+ wlock_acquire(rtld_bind_lock, lockstate);
+ }
+ errmsg_restore(saved_msg);
+}
+
+static void
+objlist_clear(Objlist *list)
+{
+ Objlist_Entry *elm;
+
+ while (!STAILQ_EMPTY(list)) {
+ elm = STAILQ_FIRST(list);
+ STAILQ_REMOVE_HEAD(list, link);
+ free(elm);
+ }
+}
+
+static Objlist_Entry *
+objlist_find(Objlist *list, const Obj_Entry *obj)
+{
+ Objlist_Entry *elm;
+
+ STAILQ_FOREACH(elm, list, link)
+ if (elm->obj == obj)
+ return elm;
+ return NULL;
+}
+
+static void
+objlist_init(Objlist *list)
+{
+ STAILQ_INIT(list);
+}
+
+static void
+objlist_push_head(Objlist *list, Obj_Entry *obj)
+{
+ Objlist_Entry *elm;
+
+ elm = NEW(Objlist_Entry);
+ elm->obj = obj;
+ STAILQ_INSERT_HEAD(list, elm, link);
+}
+
+static void
+objlist_push_tail(Objlist *list, Obj_Entry *obj)
+{
+ Objlist_Entry *elm;
+
+ elm = NEW(Objlist_Entry);
+ elm->obj = obj;
+ STAILQ_INSERT_TAIL(list, elm, link);
+}
+
+static void
+objlist_put_after(Objlist *list, Obj_Entry *listobj, Obj_Entry *obj)
+{
+ Objlist_Entry *elm, *listelm;
+
+ STAILQ_FOREACH(listelm, list, link) {
+ if (listelm->obj == listobj)
+ break;
+ }
+ elm = NEW(Objlist_Entry);
+ elm->obj = obj;
+ if (listelm != NULL)
+ STAILQ_INSERT_AFTER(list, listelm, elm, link);
+ else
+ STAILQ_INSERT_TAIL(list, elm, link);
+}
+
+static void
+objlist_remove(Objlist *list, Obj_Entry *obj)
+{
+ Objlist_Entry *elm;
+
+ if ((elm = objlist_find(list, obj)) != NULL) {
+ STAILQ_REMOVE(list, elm, Struct_Objlist_Entry, link);
+ free(elm);
+ }
+}
+
+/*
+ * Relocate dag rooted in the specified object.
+ * Returns 0 on success, or -1 on failure.
+ */
+
+static int
+relocate_object_dag(Obj_Entry *root, bool bind_now, Obj_Entry *rtldobj,
+ int flags, RtldLockState *lockstate)
+{
+ Objlist_Entry *elm;
+ int error;
+
+ error = 0;
+ STAILQ_FOREACH(elm, &root->dagmembers, link) {
+ error = relocate_object(elm->obj, bind_now, rtldobj, flags,
+ lockstate);
+ if (error == -1)
+ break;
+ }
+ return (error);
+}
+
+/*
+ * Relocate single object.
+ * Returns 0 on success, or -1 on failure.
+ */
+static int
+relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj,
+ int flags, RtldLockState *lockstate)
+{
+
+ if (obj->relocated)
+ return (0);
+ obj->relocated = true;
+ if (obj != rtldobj)
+ dbg("relocating \"%s\"", obj->path);
+
+ if (obj->symtab == NULL || obj->strtab == NULL ||
+ !(obj->valid_hash_sysv || obj->valid_hash_gnu)) {
+ _rtld_error("%s: Shared object has no run-time symbol table",
+ obj->path);
+ return (-1);
+ }
+
+ if (obj->textrel) {
+ /* There are relocations to the write-protected text segment. */
+ if (mprotect(obj->mapbase, obj->textsize,
+ PROT_READ|PROT_WRITE|PROT_EXEC) == -1) {
+ _rtld_error("%s: Cannot write-enable text segment: %s",
+ obj->path, rtld_strerror(errno));
+ return (-1);
+ }
+ }
+
+ /* Process the non-PLT non-IFUNC relocations. */
+ if (reloc_non_plt(obj, rtldobj, flags, lockstate))
+ return (-1);
+
+ if (obj->textrel) { /* Re-protected the text segment. */
+ if (mprotect(obj->mapbase, obj->textsize,
+ PROT_READ|PROT_EXEC) == -1) {
+ _rtld_error("%s: Cannot write-protect text segment: %s",
+ obj->path, rtld_strerror(errno));
+ return (-1);
+ }
+ }
+
+ /* Set the special PLT or GOT entries. */
+ init_pltgot(obj);
+
+ /* Process the PLT relocations. */
+ if (reloc_plt(obj) == -1)
+ return (-1);
+ /* Relocate the jump slots if we are doing immediate binding. */
+ if (obj->bind_now || bind_now)
+ if (reloc_jmpslots(obj, flags, lockstate) == -1)
+ return (-1);
+
+ /*
+ * Process the non-PLT IFUNC relocations. The relocations are
+ * processed in two phases, because IFUNC resolvers may
+ * reference other symbols, which must be readily processed
+ * before resolvers are called.
+ */
+ if (obj->non_plt_gnu_ifunc &&
+ reloc_non_plt(obj, rtldobj, flags | SYMLOOK_IFUNC, lockstate))
+ return (-1);
+
+ if (obj->relro_size > 0) {
+ if (mprotect(obj->relro_page, obj->relro_size,
+ PROT_READ) == -1) {
+ _rtld_error("%s: Cannot enforce relro protection: %s",
+ obj->path, rtld_strerror(errno));
+ return (-1);
+ }
+ }
+
+ /*
+ * Set up the magic number and version in the Obj_Entry. These
+ * were checked in the crt1.o from the original ElfKit, so we
+ * set them for backward compatibility.
+ */
+ obj->magic = RTLD_MAGIC;
+ obj->version = RTLD_VERSION;
+
+ return (0);
+}
+
+/*
+ * Relocate newly-loaded shared objects. The argument is a pointer to
+ * the Obj_Entry for the first such object. All objects from the first
+ * to the end of the list of objects are relocated. Returns 0 on success,
+ * or -1 on failure.
+ */
+static int
+relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj,
+ int flags, RtldLockState *lockstate)
+{
+ Obj_Entry *obj;
+ int error;
+
+ for (error = 0, obj = first; obj != NULL; obj = obj->next) {
+ error = relocate_object(obj, bind_now, rtldobj, flags,
+ lockstate);
+ if (error == -1)
+ break;
+ }
+ return (error);
+}
+
+/*
+ * The handling of R_MACHINE_IRELATIVE relocations and jumpslots
+ * referencing STT_GNU_IFUNC symbols is postponed till the other
+ * relocations are done. The indirect functions specified as
+ * ifunc are allowed to call other symbols, so we need to have
+ * objects relocated before asking for resolution from indirects.
+ *
+ * The R_MACHINE_IRELATIVE slots are resolved in greedy fashion,
+ * instead of the usual lazy handling of PLT slots. It is
+ * consistent with how GNU does it.
+ */
+static int
+resolve_object_ifunc(Obj_Entry *obj, bool bind_now, int flags,
+ RtldLockState *lockstate)
+{
+ if (obj->irelative && reloc_iresolve(obj, lockstate) == -1)
+ return (-1);
+ if ((obj->bind_now || bind_now) && obj->gnu_ifunc &&
+ reloc_gnu_ifunc(obj, flags, lockstate) == -1)
+ return (-1);
+ return (0);
+}
+
+static int
+resolve_objects_ifunc(Obj_Entry *first, bool bind_now, int flags,
+ RtldLockState *lockstate)
+{
+ Obj_Entry *obj;
+
+ for (obj = first; obj != NULL; obj = obj->next) {
+ if (resolve_object_ifunc(obj, bind_now, flags, lockstate) == -1)
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+initlist_objects_ifunc(Objlist *list, bool bind_now, int flags,
+ RtldLockState *lockstate)
+{
+ Objlist_Entry *elm;
+
+ STAILQ_FOREACH(elm, list, link) {
+ if (resolve_object_ifunc(elm->obj, bind_now, flags,
+ lockstate) == -1)
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Cleanup procedure. It will be called (by the atexit mechanism) just
+ * before the process exits.
+ */
+static void
+rtld_exit(void)
+{
+ RtldLockState lockstate;
+
+ wlock_acquire(rtld_bind_lock, &lockstate);
+ dbg("rtld_exit()");
+ objlist_call_fini(&list_fini, NULL, &lockstate);
+ /* No need to remove the items from the list, since we are exiting. */
+ if (!libmap_disable)
+ lm_fini();
+ lock_release(rtld_bind_lock, &lockstate);
+}
+
+/*
+ * Iterate over a search path, translate each element, and invoke the
+ * callback on the result.
+ */
+static void *
+path_enumerate(const char *path, path_enum_proc callback, void *arg)
+{
+ const char *trans;
+ if (path == NULL)
+ return (NULL);
+
+ path += strspn(path, ":;");
+ while (*path != '\0') {
+ size_t len;
+ char *res;
+
+ len = strcspn(path, ":;");
+ trans = lm_findn(NULL, path, len);
+ if (trans)
+ res = callback(trans, strlen(trans), arg);
+ else
+ res = callback(path, len, arg);
+
+ if (res != NULL)
+ return (res);
+
+ path += len;
+ path += strspn(path, ":;");
+ }
+
+ return (NULL);
+}
+
+struct try_library_args {
+ const char *name;
+ size_t namelen;
+ char *buffer;
+ size_t buflen;
+};
+
+static void *
+try_library_path(const char *dir, size_t dirlen, void *param)
+{
+ struct try_library_args *arg;
+
+ arg = param;
+ if (*dir == '/' || trust) {
+ char *pathname;
+
+ if (dirlen + 1 + arg->namelen + 1 > arg->buflen)
+ return (NULL);
+
+ pathname = arg->buffer;
+ strncpy(pathname, dir, dirlen);
+ pathname[dirlen] = '/';
+ strcpy(pathname + dirlen + 1, arg->name);
+
+ dbg(" Trying \"%s\"", pathname);
+ if (access(pathname, F_OK) == 0) { /* We found it */
+ pathname = xmalloc(dirlen + 1 + arg->namelen + 1);
+ strcpy(pathname, arg->buffer);
+ return (pathname);
+ }
+ }
+ return (NULL);
+}
+
+static char *
+search_library_path(const char *name, const char *path)
+{
+ char *p;
+ struct try_library_args arg;
+
+ if (path == NULL)
+ return NULL;
+
+ arg.name = name;
+ arg.namelen = strlen(name);
+ arg.buffer = xmalloc(PATH_MAX);
+ arg.buflen = PATH_MAX;
+
+ p = path_enumerate(path, try_library_path, &arg);
+
+ free(arg.buffer);
+
+ return (p);
+}
+
+
+/*
+ * Finds the library with the given name using the directory descriptors
+ * listed in the LD_LIBRARY_PATH_FDS environment variable.
+ *
+ * Returns a freshly-opened close-on-exec file descriptor for the library,
+ * or -1 if the library cannot be found.
+ */
+static char *
+search_library_pathfds(const char *name, const char *path, int *fdp)
+{
+ char *envcopy, *fdstr, *found, *last_token;
+ size_t len;
+ int dirfd, fd;
+
+ dbg("%s('%s', '%s', fdp)", __func__, name, path);
+
+ /* Don't load from user-specified libdirs into setuid binaries. */
+ if (!trust)
+ return (NULL);
+
+ /* We can't do anything if LD_LIBRARY_PATH_FDS isn't set. */
+ if (path == NULL)
+ return (NULL);
+
+ /* LD_LIBRARY_PATH_FDS only works with relative paths. */
+ if (name[0] == '/') {
+ dbg("Absolute path (%s) passed to %s", name, __func__);
+ return (NULL);
+ }
+
+ /*
+ * Use strtok_r() to walk the FD:FD:FD list. This requires a local
+ * copy of the path, as strtok_r rewrites separator tokens
+ * with '\0'.
+ */
+ found = NULL;
+ envcopy = xstrdup(path);
+ for (fdstr = strtok_r(envcopy, ":", &last_token); fdstr != NULL;
+ fdstr = strtok_r(NULL, ":", &last_token)) {
+ dirfd = parse_libdir(fdstr);
+ if (dirfd < 0)
+ break;
+ fd = openat(dirfd, name, O_RDONLY | O_CLOEXEC);
+ if (fd >= 0) {
+ *fdp = fd;
+ len = strlen(fdstr) + strlen(name) + 3;
+ found = xmalloc(len);
+ if (rtld_snprintf(found, len, "#%d/%s", dirfd, name) < 0) {
+ _rtld_error("error generating '%d/%s'",
+ dirfd, name);
+ die();
+ }
+ dbg("open('%s') => %d", found, fd);
+ break;
+ }
+ }
+ free(envcopy);
+
+ return (found);
+}
+
+
+int
+dlclose(void *handle)
+{
+ Obj_Entry *root;
+ RtldLockState lockstate;
+
+ wlock_acquire(rtld_bind_lock, &lockstate);
+ root = dlcheck(handle);
+ if (root == NULL) {
+ lock_release(rtld_bind_lock, &lockstate);
+ return -1;
+ }
+ LD_UTRACE(UTRACE_DLCLOSE_START, handle, NULL, 0, root->dl_refcount,
+ root->path);
+
+ /* Unreference the object and its dependencies. */
+ root->dl_refcount--;
+
+ if (root->refcount == 1) {
+ /*
+ * The object will be no longer referenced, so we must unload it.
+ * First, call the fini functions.
+ */
+ objlist_call_fini(&list_fini, root, &lockstate);
+
+ unref_dag(root);
+
+ /* Finish cleaning up the newly-unreferenced objects. */
+ GDB_STATE(RT_DELETE,&root->linkmap);
+ unload_object(root);
+ GDB_STATE(RT_CONSISTENT,NULL);
+ } else
+ unref_dag(root);
+
+ LD_UTRACE(UTRACE_DLCLOSE_STOP, handle, NULL, 0, 0, NULL);
+ lock_release(rtld_bind_lock, &lockstate);
+ return 0;
+}
+
+char *
+dlerror(void)
+{
+ char *msg = error_message;
+ error_message = NULL;
+ return msg;
+}
+
+/*
+ * This function is deprecated and has no effect.
+ */
+void
+dllockinit(void *context,
+ void *(*lock_create)(void *context),
+ void (*rlock_acquire)(void *lock),
+ void (*wlock_acquire)(void *lock),
+ void (*lock_release)(void *lock),
+ void (*lock_destroy)(void *lock),
+ void (*context_destroy)(void *context))
+{
+ static void *cur_context;
+ static void (*cur_context_destroy)(void *);
+
+ /* Just destroy the context from the previous call, if necessary. */
+ if (cur_context_destroy != NULL)
+ cur_context_destroy(cur_context);
+ cur_context = context;
+ cur_context_destroy = context_destroy;
+}
+
+void *
+dlopen(const char *name, int mode)
+{
+
+ return (rtld_dlopen(name, -1, mode));
+}
+
+void *
+fdlopen(int fd, int mode)
+{
+
+ return (rtld_dlopen(NULL, fd, mode));
+}
+
+static void *
+rtld_dlopen(const char *name, int fd, int mode)
+{
+ RtldLockState lockstate;
+ int lo_flags;
+
+ LD_UTRACE(UTRACE_DLOPEN_START, NULL, NULL, 0, mode, name);
+ ld_tracing = (mode & RTLD_TRACE) == 0 ? NULL : "1";
+ if (ld_tracing != NULL) {
+ rlock_acquire(rtld_bind_lock, &lockstate);
+ if (sigsetjmp(lockstate.env, 0) != 0)
+ lock_upgrade(rtld_bind_lock, &lockstate);
+ environ = (char **)*get_program_var_addr("environ", &lockstate);
+ lock_release(rtld_bind_lock, &lockstate);
+ }
+ lo_flags = RTLD_LO_DLOPEN;
+ if (mode & RTLD_NODELETE)
+ lo_flags |= RTLD_LO_NODELETE;
+ if (mode & RTLD_NOLOAD)
+ lo_flags |= RTLD_LO_NOLOAD;
+ if (ld_tracing != NULL)
+ lo_flags |= RTLD_LO_TRACE;
+
+ return (dlopen_object(name, fd, obj_main, lo_flags,
+ mode & (RTLD_MODEMASK | RTLD_GLOBAL), NULL));
+}
+
+static void
+dlopen_cleanup(Obj_Entry *obj)
+{
+
+ obj->dl_refcount--;
+ unref_dag(obj);
+ if (obj->refcount == 0)
+ unload_object(obj);
+}
+
+static Obj_Entry *
+dlopen_object(const char *name, int fd, Obj_Entry *refobj, int lo_flags,
+ int mode, RtldLockState *lockstate)
+{
+ Obj_Entry **old_obj_tail;
+ Obj_Entry *obj;
+ Objlist initlist;
+ RtldLockState mlockstate;
+ int result;
+
+ objlist_init(&initlist);
+
+ if (lockstate == NULL && !(lo_flags & RTLD_LO_EARLY)) {
+ wlock_acquire(rtld_bind_lock, &mlockstate);
+ lockstate = &mlockstate;
+ }
+ GDB_STATE(RT_ADD,NULL);
+
+ old_obj_tail = obj_tail;
+ obj = NULL;
+ if (name == NULL && fd == -1) {
+ obj = obj_main;
+ obj->refcount++;
+ } else {
+ obj = load_object(name, fd, refobj, lo_flags);
+ }
+
+ if (obj) {
+ obj->dl_refcount++;
+ if (mode & RTLD_GLOBAL && objlist_find(&list_global, obj) == NULL)
+ objlist_push_tail(&list_global, obj);
+ if (*old_obj_tail != NULL) { /* We loaded something new. */
+ assert(*old_obj_tail == obj);
+ result = load_needed_objects(obj,
+ lo_flags & (RTLD_LO_DLOPEN | RTLD_LO_EARLY));
+ init_dag(obj);
+ ref_dag(obj);
+ if (result != -1)
+ result = rtld_verify_versions(&obj->dagmembers);
+ if (result != -1 && ld_tracing)
+ goto trace;
+ if (result == -1 || relocate_object_dag(obj,
+ (mode & RTLD_MODEMASK) == RTLD_NOW, &obj_rtld,
+ (lo_flags & RTLD_LO_EARLY) ? SYMLOOK_EARLY : 0,
+ lockstate) == -1) {
+ dlopen_cleanup(obj);
+ obj = NULL;
+ } else if (lo_flags & RTLD_LO_EARLY) {
+ /*
+ * Do not call the init functions for early loaded
+ * filtees. The image is still not initialized enough
+ * for them to work.
+ *
+ * Our object is found by the global object list and
+ * will be ordered among all init calls done right
+ * before transferring control to main.
+ */
+ } else {
+ /* Make list of init functions to call. */
+ initlist_add_objects(obj, &obj->next, &initlist);
+ }
+ /*
+ * Process all no_delete objects here, given them own
+ * DAGs to prevent their dependencies from being unloaded.
+ * This has to be done after we have loaded all of the
+ * dependencies, so that we do not miss any.
+ */
+ if (obj != NULL)
+ process_nodelete(obj);
+ } else {
+ /*
+ * Bump the reference counts for objects on this DAG. If
+ * this is the first dlopen() call for the object that was
+ * already loaded as a dependency, initialize the dag
+ * starting at it.
+ */
+ init_dag(obj);
+ ref_dag(obj);
+
+ if ((lo_flags & RTLD_LO_TRACE) != 0)
+ goto trace;
+ }
+ if (obj != NULL && ((lo_flags & RTLD_LO_NODELETE) != 0 ||
+ obj->z_nodelete) && !obj->ref_nodel) {
+ dbg("obj %s nodelete", obj->path);
+ ref_dag(obj);
+ obj->z_nodelete = obj->ref_nodel = true;
+ }
+ }
+
+ LD_UTRACE(UTRACE_DLOPEN_STOP, obj, NULL, 0, obj ? obj->dl_refcount : 0,
+ name);
+ GDB_STATE(RT_CONSISTENT,obj ? &obj->linkmap : NULL);
+
+ if (!(lo_flags & RTLD_LO_EARLY)) {
+ map_stacks_exec(lockstate);
+ }
+
+ if (initlist_objects_ifunc(&initlist, (mode & RTLD_MODEMASK) == RTLD_NOW,
+ (lo_flags & RTLD_LO_EARLY) ? SYMLOOK_EARLY : 0,
+ lockstate) == -1) {
+ objlist_clear(&initlist);
+ dlopen_cleanup(obj);
+ if (lockstate == &mlockstate)
+ lock_release(rtld_bind_lock, lockstate);
+ return (NULL);
+ }
+
+ if (!(lo_flags & RTLD_LO_EARLY)) {
+ /* Call the init functions. */
+ objlist_call_init(&initlist, lockstate);
+ }
+ objlist_clear(&initlist);
+ if (lockstate == &mlockstate)
+ lock_release(rtld_bind_lock, lockstate);
+ return obj;
+trace:
+ trace_loaded_objects(obj);
+ if (lockstate == &mlockstate)
+ lock_release(rtld_bind_lock, lockstate);
+ exit(0);
+}
+
+static void *
+do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve,
+ int flags)
+{
+ DoneList donelist;
+ const Obj_Entry *obj, *defobj;
+ const Elf_Sym *def;
+ SymLook req;
+ RtldLockState lockstate;
+ tls_index ti;
+ int res;
+
+ def = NULL;
+ defobj = NULL;
+ symlook_init(&req, name);
+ req.ventry = ve;
+ req.flags = flags | SYMLOOK_IN_PLT;
+ req.lockstate = &lockstate;
+
+ rlock_acquire(rtld_bind_lock, &lockstate);
+ if (sigsetjmp(lockstate.env, 0) != 0)
+ lock_upgrade(rtld_bind_lock, &lockstate);
+ if (handle == NULL || handle == RTLD_NEXT ||
+ handle == RTLD_DEFAULT || handle == RTLD_SELF) {
+
+ if ((obj = obj_from_addr(retaddr)) == NULL) {
+ _rtld_error("Cannot determine caller's shared object");
+ lock_release(rtld_bind_lock, &lockstate);
+ return NULL;
+ }
+ if (handle == NULL) { /* Just the caller's shared object. */
+ res = symlook_obj(&req, obj);
+ if (res == 0) {
+ def = req.sym_out;
+ defobj = req.defobj_out;
+ }
+ } else if (handle == RTLD_NEXT || /* Objects after caller's */
+ handle == RTLD_SELF) { /* ... caller included */
+ if (handle == RTLD_NEXT)
+ obj = obj->next;
+ for (; obj != NULL; obj = obj->next) {
+ res = symlook_obj(&req, obj);
+ if (res == 0) {
+ if (def == NULL ||
+ ELF_ST_BIND(req.sym_out->st_info) != STB_WEAK) {
+ def = req.sym_out;
+ defobj = req.defobj_out;
+ if (ELF_ST_BIND(def->st_info) != STB_WEAK)
+ break;
+ }
+ }
+ }
+ /*
+ * Search the dynamic linker itself, and possibly resolve the
+ * symbol from there. This is how the application links to
+ * dynamic linker services such as dlopen.
+ */
+ if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) {
+ res = symlook_obj(&req, &obj_rtld);
+ if (res == 0) {
+ def = req.sym_out;
+ defobj = req.defobj_out;
+ }
+ }
+ } else {
+ assert(handle == RTLD_DEFAULT);
+ res = symlook_default(&req, obj);
+ if (res == 0) {
+ defobj = req.defobj_out;
+ def = req.sym_out;
+ }
+ }
+ } else {
+ if ((obj = dlcheck(handle)) == NULL) {
+ lock_release(rtld_bind_lock, &lockstate);
+ return NULL;
+ }
+
+ donelist_init(&donelist);
+ if (obj->mainprog) {
+ /* Handle obtained by dlopen(NULL, ...) implies global scope. */
+ res = symlook_global(&req, &donelist);
+ if (res == 0) {
+ def = req.sym_out;
+ defobj = req.defobj_out;
+ }
+ /*
+ * Search the dynamic linker itself, and possibly resolve the
+ * symbol from there. This is how the application links to
+ * dynamic linker services such as dlopen.
+ */
+ if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) {
+ res = symlook_obj(&req, &obj_rtld);
+ if (res == 0) {
+ def = req.sym_out;
+ defobj = req.defobj_out;
+ }
+ }
+ }
+ else {
+ /* Search the whole DAG rooted at the given object. */
+ res = symlook_list(&req, &obj->dagmembers, &donelist);
+ if (res == 0) {
+ def = req.sym_out;
+ defobj = req.defobj_out;
+ }
+ }
+ }
+
+ if (def != NULL) {
+ lock_release(rtld_bind_lock, &lockstate);
+
+ /*
+ * The value required by the caller is derived from the value
+ * of the symbol. this is simply the relocated value of the
+ * symbol.
+ */
+ if (ELF_ST_TYPE(def->st_info) == STT_FUNC)
+ return (make_function_pointer(def, defobj));
+ else if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC)
+ return (rtld_resolve_ifunc(defobj, def));
+ else if (ELF_ST_TYPE(def->st_info) == STT_TLS) {
+ ti.ti_module = defobj->tlsindex;
+ ti.ti_offset = def->st_value;
+ return (__tls_get_addr(&ti));
+ } else
+ return (defobj->relocbase + def->st_value);
+ }
+
+ _rtld_error("Undefined symbol \"%s\"", name);
+ lock_release(rtld_bind_lock, &lockstate);
+ return NULL;
+}
+
+void *
+dlsym(void *handle, const char *name)
+{
+ return do_dlsym(handle, name, __builtin_return_address(0), NULL,
+ SYMLOOK_DLSYM);
+}
+
+dlfunc_t
+dlfunc(void *handle, const char *name)
+{
+ union {
+ void *d;
+ dlfunc_t f;
+ } rv;
+
+ rv.d = do_dlsym(handle, name, __builtin_return_address(0), NULL,
+ SYMLOOK_DLSYM);
+ return (rv.f);
+}
+
+void *
+dlvsym(void *handle, const char *name, const char *version)
+{
+ Ver_Entry ventry;
+
+ ventry.name = version;
+ ventry.file = NULL;
+ ventry.hash = elf_hash(version);
+ ventry.flags= 0;
+ return do_dlsym(handle, name, __builtin_return_address(0), &ventry,
+ SYMLOOK_DLSYM);
+}
+
+int
+_rtld_addr_phdr(const void *addr, struct dl_phdr_info *phdr_info)
+{
+ const Obj_Entry *obj;
+ RtldLockState lockstate;
+
+ rlock_acquire(rtld_bind_lock, &lockstate);
+ obj = obj_from_addr(addr);
+ if (obj == NULL) {
+ _rtld_error("No shared object contains address");
+ lock_release(rtld_bind_lock, &lockstate);
+ return (0);
+ }
+ rtld_fill_dl_phdr_info(obj, phdr_info);
+ lock_release(rtld_bind_lock, &lockstate);
+ return (1);
+}
+
+int
+dladdr(const void *addr, Dl_info *info)
+{
+ const Obj_Entry *obj;
+ const Elf_Sym *def;
+ void *symbol_addr;
+ unsigned long symoffset;
+ RtldLockState lockstate;
+
+ rlock_acquire(rtld_bind_lock, &lockstate);
+ obj = obj_from_addr(addr);
+ if (obj == NULL) {
+ _rtld_error("No shared object contains address");
+ lock_release(rtld_bind_lock, &lockstate);
+ return 0;
+ }
+ info->dli_fname = obj->path;
+ info->dli_fbase = obj->mapbase;
+ info->dli_saddr = (void *)0;
+ info->dli_sname = NULL;
+
+ /*
+ * Walk the symbol list looking for the symbol whose address is
+ * closest to the address sent in.
+ */
+ for (symoffset = 0; symoffset < obj->dynsymcount; symoffset++) {
+ def = obj->symtab + symoffset;
+
+ /*
+ * For skip the symbol if st_shndx is either SHN_UNDEF or
+ * SHN_COMMON.
+ */
+ if (def->st_shndx == SHN_UNDEF || def->st_shndx == SHN_COMMON)
+ continue;
+
+ /*
+ * If the symbol is greater than the specified address, or if it
+ * is further away from addr than the current nearest symbol,
+ * then reject it.
+ */
+ symbol_addr = obj->relocbase + def->st_value;
+ if (symbol_addr > addr || symbol_addr < info->dli_saddr)
+ continue;
+
+ /* Update our idea of the nearest symbol. */
+ info->dli_sname = obj->strtab + def->st_name;
+ info->dli_saddr = symbol_addr;
+
+ /* Exact match? */
+ if (info->dli_saddr == addr)
+ break;
+ }
+ lock_release(rtld_bind_lock, &lockstate);
+ return 1;
+}
+
+int
+dlinfo(void *handle, int request, void *p)
+{
+ const Obj_Entry *obj;
+ RtldLockState lockstate;
+ int error;
+
+ rlock_acquire(rtld_bind_lock, &lockstate);
+
+ if (handle == NULL || handle == RTLD_SELF) {
+ void *retaddr;
+
+ retaddr = __builtin_return_address(0); /* __GNUC__ only */
+ if ((obj = obj_from_addr(retaddr)) == NULL)
+ _rtld_error("Cannot determine caller's shared object");
+ } else
+ obj = dlcheck(handle);
+
+ if (obj == NULL) {
+ lock_release(rtld_bind_lock, &lockstate);
+ return (-1);
+ }
+
+ error = 0;
+ switch (request) {
+ case RTLD_DI_LINKMAP:
+ *((struct link_map const **)p) = &obj->linkmap;
+ break;
+ case RTLD_DI_ORIGIN:
+ error = rtld_dirname(obj->path, p);
+ break;
+
+ case RTLD_DI_SERINFOSIZE:
+ case RTLD_DI_SERINFO:
+ error = do_search_info(obj, request, (struct dl_serinfo *)p);
+ break;
+
+ default:
+ _rtld_error("Invalid request %d passed to dlinfo()", request);
+ error = -1;
+ }
+
+ lock_release(rtld_bind_lock, &lockstate);
+
+ return (error);
+}
+
+static void
+rtld_fill_dl_phdr_info(const Obj_Entry *obj, struct dl_phdr_info *phdr_info)
+{
+
+ phdr_info->dlpi_addr = (Elf_Addr)obj->relocbase;
+ phdr_info->dlpi_name = obj->path;
+ phdr_info->dlpi_phdr = obj->phdr;
+ phdr_info->dlpi_phnum = obj->phsize / sizeof(obj->phdr[0]);
+ phdr_info->dlpi_tls_modid = obj->tlsindex;
+ phdr_info->dlpi_tls_data = obj->tlsinit;
+ phdr_info->dlpi_adds = obj_loads;
+ phdr_info->dlpi_subs = obj_loads - obj_count;
+}
+
+int
+dl_iterate_phdr(__dl_iterate_hdr_callback callback, void *param)
+{
+ struct dl_phdr_info phdr_info;
+ const Obj_Entry *obj;
+ RtldLockState bind_lockstate, phdr_lockstate;
+ int error;
+
+ wlock_acquire(rtld_phdr_lock, &phdr_lockstate);
+ rlock_acquire(rtld_bind_lock, &bind_lockstate);
+
+ error = 0;
+
+ for (obj = obj_list; obj != NULL; obj = obj->next) {
+ rtld_fill_dl_phdr_info(obj, &phdr_info);
+ if ((error = callback(&phdr_info, sizeof phdr_info, param)) != 0)
+ break;
+
+ }
+ if (error == 0) {
+ rtld_fill_dl_phdr_info(&obj_rtld, &phdr_info);
+ error = callback(&phdr_info, sizeof(phdr_info), param);
+ }
+
+ lock_release(rtld_bind_lock, &bind_lockstate);
+ lock_release(rtld_phdr_lock, &phdr_lockstate);
+
+ return (error);
+}
+
+static void *
+fill_search_info(const char *dir, size_t dirlen, void *param)
+{
+ struct fill_search_info_args *arg;
+
+ arg = param;
+
+ if (arg->request == RTLD_DI_SERINFOSIZE) {
+ arg->serinfo->dls_cnt ++;
+ arg->serinfo->dls_size += sizeof(struct dl_serpath) + dirlen + 1;
+ } else {
+ struct dl_serpath *s_entry;
+
+ s_entry = arg->serpath;
+ s_entry->dls_name = arg->strspace;
+ s_entry->dls_flags = arg->flags;
+
+ strncpy(arg->strspace, dir, dirlen);
+ arg->strspace[dirlen] = '\0';
+
+ arg->strspace += dirlen + 1;
+ arg->serpath++;
+ }
+
+ return (NULL);
+}
+
+static int
+do_search_info(const Obj_Entry *obj, int request, struct dl_serinfo *info)
+{
+ struct dl_serinfo _info;
+ struct fill_search_info_args args;
+
+ args.request = RTLD_DI_SERINFOSIZE;
+ args.serinfo = &_info;
+
+ _info.dls_size = __offsetof(struct dl_serinfo, dls_serpath);
+ _info.dls_cnt = 0;
+
+ path_enumerate(obj->rpath, fill_search_info, &args);
+ path_enumerate(ld_library_path, fill_search_info, &args);
+ path_enumerate(obj->runpath, fill_search_info, &args);
+ path_enumerate(gethints(obj->z_nodeflib), fill_search_info, &args);
+ if (!obj->z_nodeflib)
+ path_enumerate(STANDARD_LIBRARY_PATH, fill_search_info, &args);
+
+
+ if (request == RTLD_DI_SERINFOSIZE) {
+ info->dls_size = _info.dls_size;
+ info->dls_cnt = _info.dls_cnt;
+ return (0);
+ }
+
+ if (info->dls_cnt != _info.dls_cnt || info->dls_size != _info.dls_size) {
+ _rtld_error("Uninitialized Dl_serinfo struct passed to dlinfo()");
+ return (-1);
+ }
+
+ args.request = RTLD_DI_SERINFO;
+ args.serinfo = info;
+ args.serpath = &info->dls_serpath[0];
+ args.strspace = (char *)&info->dls_serpath[_info.dls_cnt];
+
+ args.flags = LA_SER_RUNPATH;
+ if (path_enumerate(obj->rpath, fill_search_info, &args) != NULL)
+ return (-1);
+
+ args.flags = LA_SER_LIBPATH;
+ if (path_enumerate(ld_library_path, fill_search_info, &args) != NULL)
+ return (-1);
+
+ args.flags = LA_SER_RUNPATH;
+ if (path_enumerate(obj->runpath, fill_search_info, &args) != NULL)
+ return (-1);
+
+ args.flags = LA_SER_CONFIG;
+ if (path_enumerate(gethints(obj->z_nodeflib), fill_search_info, &args)
+ != NULL)
+ return (-1);
+
+ args.flags = LA_SER_DEFAULT;
+ if (!obj->z_nodeflib &&
+ path_enumerate(STANDARD_LIBRARY_PATH, fill_search_info, &args) != NULL)
+ return (-1);
+ return (0);
+}
+
+static int
+rtld_dirname(const char *path, char *bname)
+{
+ const char *endp;
+
+ /* Empty or NULL string gets treated as "." */
+ if (path == NULL || *path == '\0') {
+ bname[0] = '.';
+ bname[1] = '\0';
+ return (0);
+ }
+
+ /* Strip trailing slashes */
+ endp = path + strlen(path) - 1;
+ while (endp > path && *endp == '/')
+ endp--;
+
+ /* Find the start of the dir */
+ while (endp > path && *endp != '/')
+ endp--;
+
+ /* Either the dir is "/" or there are no slashes */
+ if (endp == path) {
+ bname[0] = *endp == '/' ? '/' : '.';
+ bname[1] = '\0';
+ return (0);
+ } else {
+ do {
+ endp--;
+ } while (endp > path && *endp == '/');
+ }
+
+ if (endp - path + 2 > PATH_MAX)
+ {
+ _rtld_error("Filename is too long: %s", path);
+ return(-1);
+ }
+
+ strncpy(bname, path, endp - path + 1);
+ bname[endp - path + 1] = '\0';
+ return (0);
+}
+
+static int
+rtld_dirname_abs(const char *path, char *base)
+{
+ char base_rel[PATH_MAX];
+
+ if (rtld_dirname(path, base) == -1)
+ return (-1);
+ if (base[0] == '/')
+ return (0);
+ if (getcwd(base_rel, sizeof(base_rel)) == NULL ||
+ strlcat(base_rel, "/", sizeof(base_rel)) >= sizeof(base_rel) ||
+ strlcat(base_rel, base, sizeof(base_rel)) >= sizeof(base_rel))
+ return (-1);
+ strcpy(base, base_rel);
+ return (0);
+}
+
+static void
+linkmap_add(Obj_Entry *obj)
+{
+ struct link_map *l = &obj->linkmap;
+ struct link_map *prev;
+
+ obj->linkmap.l_name = obj->path;
+ obj->linkmap.l_addr = obj->mapbase;
+ obj->linkmap.l_ld = obj->dynamic;
+#ifdef __mips__
+ /* GDB needs load offset on MIPS to use the symbols */
+ obj->linkmap.l_offs = obj->relocbase;
+#endif
+
+ if (r_debug.r_map == NULL) {
+ r_debug.r_map = l;
+ return;
+ }
+
+ /*
+ * Scan to the end of the list, but not past the entry for the
+ * dynamic linker, which we want to keep at the very end.
+ */
+ for (prev = r_debug.r_map;
+ prev->l_next != NULL && prev->l_next != &obj_rtld.linkmap;
+ prev = prev->l_next)
+ ;
+
+ /* Link in the new entry. */
+ l->l_prev = prev;
+ l->l_next = prev->l_next;
+ if (l->l_next != NULL)
+ l->l_next->l_prev = l;
+ prev->l_next = l;
+}
+
+static void
+linkmap_delete(Obj_Entry *obj)
+{
+ struct link_map *l = &obj->linkmap;
+
+ if (l->l_prev == NULL) {
+ if ((r_debug.r_map = l->l_next) != NULL)
+ l->l_next->l_prev = NULL;
+ return;
+ }
+
+ if ((l->l_prev->l_next = l->l_next) != NULL)
+ l->l_next->l_prev = l->l_prev;
+}
+
+/*
+ * Function for the debugger to set a breakpoint on to gain control.
+ *
+ * The two parameters allow the debugger to easily find and determine
+ * what the runtime loader is doing and to whom it is doing it.
+ *
+ * When the loadhook trap is hit (r_debug_state, set at program
+ * initialization), the arguments can be found on the stack:
+ *
+ * +8 struct link_map *m
+ * +4 struct r_debug *rd
+ * +0 RetAddr
+ */
+void
+r_debug_state(struct r_debug* rd, struct link_map *m)
+{
+ /*
+ * The following is a hack to force the compiler to emit calls to
+ * this function, even when optimizing. If the function is empty,
+ * the compiler is not obliged to emit any code for calls to it,
+ * even when marked __noinline. However, gdb depends on those
+ * calls being made.
+ */
+ __compiler_membar();
+}
+
+/*
+ * A function called after init routines have completed. This can be used to
+ * break before a program's entry routine is called, and can be used when
+ * main is not available in the symbol table.
+ */
+void
+_r_debug_postinit(struct link_map *m)
+{
+
+ /* See r_debug_state(). */
+ __compiler_membar();
+}
+
+/*
+ * Get address of the pointer variable in the main program.
+ * Prefer non-weak symbol over the weak one.
+ */
+static const void **
+get_program_var_addr(const char *name, RtldLockState *lockstate)
+{
+ SymLook req;
+ DoneList donelist;
+
+ symlook_init(&req, name);
+ req.lockstate = lockstate;
+ donelist_init(&donelist);
+ if (symlook_global(&req, &donelist) != 0)
+ return (NULL);
+ if (ELF_ST_TYPE(req.sym_out->st_info) == STT_FUNC)
+ return ((const void **)make_function_pointer(req.sym_out,
+ req.defobj_out));
+ else if (ELF_ST_TYPE(req.sym_out->st_info) == STT_GNU_IFUNC)
+ return ((const void **)rtld_resolve_ifunc(req.defobj_out, req.sym_out));
+ else
+ return ((const void **)(req.defobj_out->relocbase +
+ req.sym_out->st_value));
+}
+
+/*
+ * Set a pointer variable in the main program to the given value. This
+ * is used to set key variables such as "environ" before any of the
+ * init functions are called.
+ */
+static void
+set_program_var(const char *name, const void *value)
+{
+ const void **addr;
+
+ if ((addr = get_program_var_addr(name, NULL)) != NULL) {
+ dbg("\"%s\": *%p <-- %p", name, addr, value);
+ *addr = value;
+ }
+}
+
+/*
+ * Search the global objects, including dependencies and main object,
+ * for the given symbol.
+ */
+static int
+symlook_global(SymLook *req, DoneList *donelist)
+{
+ SymLook req1;
+ const Objlist_Entry *elm;
+ int res;
+
+ symlook_init_from_req(&req1, req);
+
+ /* Search all objects loaded at program start up. */
+ if (req->defobj_out == NULL ||
+ ELF_ST_BIND(req->sym_out->st_info) == STB_WEAK) {
+ res = symlook_list(&req1, &list_main, donelist);
+ if (res == 0 && (req->defobj_out == NULL ||
+ ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) {
+ req->sym_out = req1.sym_out;
+ req->defobj_out = req1.defobj_out;
+ assert(req->defobj_out != NULL);
+ }
+ }
+
+ /* Search all DAGs whose roots are RTLD_GLOBAL objects. */
+ STAILQ_FOREACH(elm, &list_global, link) {
+ if (req->defobj_out != NULL &&
+ ELF_ST_BIND(req->sym_out->st_info) != STB_WEAK)
+ break;
+ res = symlook_list(&req1, &elm->obj->dagmembers, donelist);
+ if (res == 0 && (req->defobj_out == NULL ||
+ ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) {
+ req->sym_out = req1.sym_out;
+ req->defobj_out = req1.defobj_out;
+ assert(req->defobj_out != NULL);
+ }
+ }
+
+ return (req->sym_out != NULL ? 0 : ESRCH);
+}
+
+/*
+ * Given a symbol name in a referencing object, find the corresponding
+ * definition of the symbol. Returns a pointer to the symbol, or NULL if
+ * no definition was found. Returns a pointer to the Obj_Entry of the
+ * defining object via the reference parameter DEFOBJ_OUT.
+ */
+static int
+symlook_default(SymLook *req, const Obj_Entry *refobj)
+{
+ DoneList donelist;
+ const Objlist_Entry *elm;
+ SymLook req1;
+ int res;
+
+ donelist_init(&donelist);
+ symlook_init_from_req(&req1, req);
+
+ /* Look first in the referencing object if linked symbolically. */
+ if (refobj->symbolic && !donelist_check(&donelist, refobj)) {
+ res = symlook_obj(&req1, refobj);
+ if (res == 0) {
+ req->sym_out = req1.sym_out;
+ req->defobj_out = req1.defobj_out;
+ assert(req->defobj_out != NULL);
+ }
+ }
+
+ symlook_global(req, &donelist);
+
+ /* Search all dlopened DAGs containing the referencing object. */
+ STAILQ_FOREACH(elm, &refobj->dldags, link) {
+ if (req->sym_out != NULL &&
+ ELF_ST_BIND(req->sym_out->st_info) != STB_WEAK)
+ break;
+ res = symlook_list(&req1, &elm->obj->dagmembers, &donelist);
+ if (res == 0 && (req->sym_out == NULL ||
+ ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) {
+ req->sym_out = req1.sym_out;
+ req->defobj_out = req1.defobj_out;
+ assert(req->defobj_out != NULL);
+ }
+ }
+
+ /*
+ * Search the dynamic linker itself, and possibly resolve the
+ * symbol from there. This is how the application links to
+ * dynamic linker services such as dlopen.
+ */
+ if (req->sym_out == NULL ||
+ ELF_ST_BIND(req->sym_out->st_info) == STB_WEAK) {
+ res = symlook_obj(&req1, &obj_rtld);
+ if (res == 0) {
+ req->sym_out = req1.sym_out;
+ req->defobj_out = req1.defobj_out;
+ assert(req->defobj_out != NULL);
+ }
+ }
+
+ return (req->sym_out != NULL ? 0 : ESRCH);
+}
+
+static int
+symlook_list(SymLook *req, const Objlist *objlist, DoneList *dlp)
+{
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+ const Objlist_Entry *elm;
+ SymLook req1;
+ int res;
+
+ def = NULL;
+ defobj = NULL;
+ STAILQ_FOREACH(elm, objlist, link) {
+ if (donelist_check(dlp, elm->obj))
+ continue;
+ symlook_init_from_req(&req1, req);
+ if ((res = symlook_obj(&req1, elm->obj)) == 0) {
+ if (def == NULL || ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK) {
+ def = req1.sym_out;
+ defobj = req1.defobj_out;
+ if (ELF_ST_BIND(def->st_info) != STB_WEAK)
+ break;
+ }
+ }
+ }
+ if (def != NULL) {
+ req->sym_out = def;
+ req->defobj_out = defobj;
+ return (0);
+ }
+ return (ESRCH);
+}
+
+/*
+ * Search the chain of DAGS cointed to by the given Needed_Entry
+ * for a symbol of the given name. Each DAG is scanned completely
+ * before advancing to the next one. Returns a pointer to the symbol,
+ * or NULL if no definition was found.
+ */
+static int
+symlook_needed(SymLook *req, const Needed_Entry *needed, DoneList *dlp)
+{
+ const Elf_Sym *def;
+ const Needed_Entry *n;
+ const Obj_Entry *defobj;
+ SymLook req1;
+ int res;
+
+ def = NULL;
+ defobj = NULL;
+ symlook_init_from_req(&req1, req);
+ for (n = needed; n != NULL; n = n->next) {
+ if (n->obj == NULL ||
+ (res = symlook_list(&req1, &n->obj->dagmembers, dlp)) != 0)
+ continue;
+ if (def == NULL || ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK) {
+ def = req1.sym_out;
+ defobj = req1.defobj_out;
+ if (ELF_ST_BIND(def->st_info) != STB_WEAK)
+ break;
+ }
+ }
+ if (def != NULL) {
+ req->sym_out = def;
+ req->defobj_out = defobj;
+ return (0);
+ }
+ return (ESRCH);
+}
+
+/*
+ * Search the symbol table of a single shared object for a symbol of
+ * the given name and version, if requested. Returns a pointer to the
+ * symbol, or NULL if no definition was found. If the object is
+ * filter, return filtered symbol from filtee.
+ *
+ * The symbol's hash value is passed in for efficiency reasons; that
+ * eliminates many recomputations of the hash value.
+ */
+int
+symlook_obj(SymLook *req, const Obj_Entry *obj)
+{
+ DoneList donelist;
+ SymLook req1;
+ int flags, res, mres;
+
+ /*
+ * If there is at least one valid hash at this point, we prefer to
+ * use the faster GNU version if available.
+ */
+ if (obj->valid_hash_gnu)
+ mres = symlook_obj1_gnu(req, obj);
+ else if (obj->valid_hash_sysv)
+ mres = symlook_obj1_sysv(req, obj);
+ else
+ return (EINVAL);
+
+ if (mres == 0) {
+ if (obj->needed_filtees != NULL) {
+ flags = (req->flags & SYMLOOK_EARLY) ? RTLD_LO_EARLY : 0;
+ load_filtees(__DECONST(Obj_Entry *, obj), flags, req->lockstate);
+ donelist_init(&donelist);
+ symlook_init_from_req(&req1, req);
+ res = symlook_needed(&req1, obj->needed_filtees, &donelist);
+ if (res == 0) {
+ req->sym_out = req1.sym_out;
+ req->defobj_out = req1.defobj_out;
+ }
+ return (res);
+ }
+ if (obj->needed_aux_filtees != NULL) {
+ flags = (req->flags & SYMLOOK_EARLY) ? RTLD_LO_EARLY : 0;
+ load_filtees(__DECONST(Obj_Entry *, obj), flags, req->lockstate);
+ donelist_init(&donelist);
+ symlook_init_from_req(&req1, req);
+ res = symlook_needed(&req1, obj->needed_aux_filtees, &donelist);
+ if (res == 0) {
+ req->sym_out = req1.sym_out;
+ req->defobj_out = req1.defobj_out;
+ return (res);
+ }
+ }
+ }
+ return (mres);
+}
+
+/* Symbol match routine common to both hash functions */
+static bool
+matched_symbol(SymLook *req, const Obj_Entry *obj, Sym_Match_Result *result,
+ const unsigned long symnum)
+{
+ Elf_Versym verndx;
+ const Elf_Sym *symp;
+ const char *strp;
+
+ symp = obj->symtab + symnum;
+ strp = obj->strtab + symp->st_name;
+
+ switch (ELF_ST_TYPE(symp->st_info)) {
+ case STT_FUNC:
+ case STT_NOTYPE:
+ case STT_OBJECT:
+ case STT_COMMON:
+ case STT_GNU_IFUNC:
+ if (symp->st_value == 0)
+ return (false);
+ /* fallthrough */
+ case STT_TLS:
+ if (symp->st_shndx != SHN_UNDEF)
+ break;
+#ifndef __mips__
+ else if (((req->flags & SYMLOOK_IN_PLT) == 0) &&
+ (ELF_ST_TYPE(symp->st_info) == STT_FUNC))
+ break;
+ /* fallthrough */
+#endif
+ default:
+ return (false);
+ }
+ if (req->name[0] != strp[0] || strcmp(req->name, strp) != 0)
+ return (false);
+
+ if (req->ventry == NULL) {
+ if (obj->versyms != NULL) {
+ verndx = VER_NDX(obj->versyms[symnum]);
+ if (verndx > obj->vernum) {
+ _rtld_error(
+ "%s: symbol %s references wrong version %d",
+ obj->path, obj->strtab + symnum, verndx);
+ return (false);
+ }
+ /*
+ * If we are not called from dlsym (i.e. this
+ * is a normal relocation from unversioned
+ * binary), accept the symbol immediately if
+ * it happens to have first version after this
+ * shared object became versioned. Otherwise,
+ * if symbol is versioned and not hidden,
+ * remember it. If it is the only symbol with
+ * this name exported by the shared object, it
+ * will be returned as a match by the calling
+ * function. If symbol is global (verndx < 2)
+ * accept it unconditionally.
+ */
+ if ((req->flags & SYMLOOK_DLSYM) == 0 &&
+ verndx == VER_NDX_GIVEN) {
+ result->sym_out = symp;
+ return (true);
+ }
+ else if (verndx >= VER_NDX_GIVEN) {
+ if ((obj->versyms[symnum] & VER_NDX_HIDDEN)
+ == 0) {
+ if (result->vsymp == NULL)
+ result->vsymp = symp;
+ result->vcount++;
+ }
+ return (false);
+ }
+ }
+ result->sym_out = symp;
+ return (true);
+ }
+ if (obj->versyms == NULL) {
+ if (object_match_name(obj, req->ventry->name)) {
+ _rtld_error("%s: object %s should provide version %s "
+ "for symbol %s", obj_rtld.path, obj->path,
+ req->ventry->name, obj->strtab + symnum);
+ return (false);
+ }
+ } else {
+ verndx = VER_NDX(obj->versyms[symnum]);
+ if (verndx > obj->vernum) {
+ _rtld_error("%s: symbol %s references wrong version %d",
+ obj->path, obj->strtab + symnum, verndx);
+ return (false);
+ }
+ if (obj->vertab[verndx].hash != req->ventry->hash ||
+ strcmp(obj->vertab[verndx].name, req->ventry->name)) {
+ /*
+ * Version does not match. Look if this is a
+ * global symbol and if it is not hidden. If
+ * global symbol (verndx < 2) is available,
+ * use it. Do not return symbol if we are
+ * called by dlvsym, because dlvsym looks for
+ * a specific version and default one is not
+ * what dlvsym wants.
+ */
+ if ((req->flags & SYMLOOK_DLSYM) ||
+ (verndx >= VER_NDX_GIVEN) ||
+ (obj->versyms[symnum] & VER_NDX_HIDDEN))
+ return (false);
+ }
+ }
+ result->sym_out = symp;
+ return (true);
+}
+
+/*
+ * Search for symbol using SysV hash function.
+ * obj->buckets is known not to be NULL at this point; the test for this was
+ * performed with the obj->valid_hash_sysv assignment.
+ */
+static int
+symlook_obj1_sysv(SymLook *req, const Obj_Entry *obj)
+{
+ unsigned long symnum;
+ Sym_Match_Result matchres;
+
+ matchres.sym_out = NULL;
+ matchres.vsymp = NULL;
+ matchres.vcount = 0;
+
+ for (symnum = obj->buckets[req->hash % obj->nbuckets];
+ symnum != STN_UNDEF; symnum = obj->chains[symnum]) {
+ if (symnum >= obj->nchains)
+ return (ESRCH); /* Bad object */
+
+ if (matched_symbol(req, obj, &matchres, symnum)) {
+ req->sym_out = matchres.sym_out;
+ req->defobj_out = obj;
+ return (0);
+ }
+ }
+ if (matchres.vcount == 1) {
+ req->sym_out = matchres.vsymp;
+ req->defobj_out = obj;
+ return (0);
+ }
+ return (ESRCH);
+}
+
+/* Search for symbol using GNU hash function */
+static int
+symlook_obj1_gnu(SymLook *req, const Obj_Entry *obj)
+{
+ Elf_Addr bloom_word;
+ const Elf32_Word *hashval;
+ Elf32_Word bucket;
+ Sym_Match_Result matchres;
+ unsigned int h1, h2;
+ unsigned long symnum;
+
+ matchres.sym_out = NULL;
+ matchres.vsymp = NULL;
+ matchres.vcount = 0;
+
+ /* Pick right bitmask word from Bloom filter array */
+ bloom_word = obj->bloom_gnu[(req->hash_gnu / __ELF_WORD_SIZE) &
+ obj->maskwords_bm_gnu];
+
+ /* Calculate modulus word size of gnu hash and its derivative */
+ h1 = req->hash_gnu & (__ELF_WORD_SIZE - 1);
+ h2 = ((req->hash_gnu >> obj->shift2_gnu) & (__ELF_WORD_SIZE - 1));
+
+ /* Filter out the "definitely not in set" queries */
+ if (((bloom_word >> h1) & (bloom_word >> h2) & 1) == 0)
+ return (ESRCH);
+
+ /* Locate hash chain and corresponding value element*/
+ bucket = obj->buckets_gnu[req->hash_gnu % obj->nbuckets_gnu];
+ if (bucket == 0)
+ return (ESRCH);
+ hashval = &obj->chain_zero_gnu[bucket];
+ do {
+ if (((*hashval ^ req->hash_gnu) >> 1) == 0) {
+ symnum = hashval - obj->chain_zero_gnu;
+ if (matched_symbol(req, obj, &matchres, symnum)) {
+ req->sym_out = matchres.sym_out;
+ req->defobj_out = obj;
+ return (0);
+ }
+ }
+ } while ((*hashval++ & 1) == 0);
+ if (matchres.vcount == 1) {
+ req->sym_out = matchres.vsymp;
+ req->defobj_out = obj;
+ return (0);
+ }
+ return (ESRCH);
+}
+
+static void
+trace_loaded_objects(Obj_Entry *obj)
+{
+ char *fmt1, *fmt2, *fmt, *main_local, *list_containers;
+ int c;
+
+ if ((main_local = getenv(LD_ "TRACE_LOADED_OBJECTS_PROGNAME")) == NULL)
+ main_local = "";
+
+ if ((fmt1 = getenv(LD_ "TRACE_LOADED_OBJECTS_FMT1")) == NULL)
+ fmt1 = "\t%o => %p (%x)\n";
+
+ if ((fmt2 = getenv(LD_ "TRACE_LOADED_OBJECTS_FMT2")) == NULL)
+ fmt2 = "\t%o (%x)\n";
+
+ list_containers = getenv(LD_ "TRACE_LOADED_OBJECTS_ALL");
+
+ for (; obj; obj = obj->next) {
+ Needed_Entry *needed;
+ char *name, *path;
+ bool is_lib;
+
+ if (list_containers && obj->needed != NULL)
+ rtld_printf("%s:\n", obj->path);
+ for (needed = obj->needed; needed; needed = needed->next) {
+ if (needed->obj != NULL) {
+ if (needed->obj->traced && !list_containers)
+ continue;
+ needed->obj->traced = true;
+ path = needed->obj->path;
+ } else
+ path = "not found";
+
+ name = (char *)obj->strtab + needed->name;
+ is_lib = strncmp(name, "lib", 3) == 0; /* XXX - bogus */
+
+ fmt = is_lib ? fmt1 : fmt2;
+ while ((c = *fmt++) != '\0') {
+ switch (c) {
+ default:
+ rtld_putchar(c);
+ continue;
+ case '\\':
+ switch (c = *fmt) {
+ case '\0':
+ continue;
+ case 'n':
+ rtld_putchar('\n');
+ break;
+ case 't':
+ rtld_putchar('\t');
+ break;
+ }
+ break;
+ case '%':
+ switch (c = *fmt) {
+ case '\0':
+ continue;
+ case '%':
+ default:
+ rtld_putchar(c);
+ break;
+ case 'A':
+ rtld_putstr(main_local);
+ break;
+ case 'a':
+ rtld_putstr(obj_main->path);
+ break;
+ case 'o':
+ rtld_putstr(name);
+ break;
+#if 0
+ case 'm':
+ rtld_printf("%d", sodp->sod_major);
+ break;
+ case 'n':
+ rtld_printf("%d", sodp->sod_minor);
+ break;
+#endif
+ case 'p':
+ rtld_putstr(path);
+ break;
+ case 'x':
+ rtld_printf("%p", needed->obj ? needed->obj->mapbase :
+ 0);
+ break;
+ }
+ break;
+ }
+ ++fmt;
+ }
+ }
+ }
+}
+
+/*
+ * Unload a dlopened object and its dependencies from memory and from
+ * our data structures. It is assumed that the DAG rooted in the
+ * object has already been unreferenced, and that the object has a
+ * reference count of 0.
+ */
+static void
+unload_object(Obj_Entry *root)
+{
+ Obj_Entry *obj;
+ Obj_Entry **linkp;
+
+ assert(root->refcount == 0);
+
+ /*
+ * Pass over the DAG removing unreferenced objects from
+ * appropriate lists.
+ */
+ unlink_object(root);
+
+ /* Unmap all objects that are no longer referenced. */
+ linkp = &obj_list->next;
+ while ((obj = *linkp) != NULL) {
+ if (obj->refcount == 0) {
+ LD_UTRACE(UTRACE_UNLOAD_OBJECT, obj, obj->mapbase, obj->mapsize, 0,
+ obj->path);
+ dbg("unloading \"%s\"", obj->path);
+ unload_filtees(root);
+ munmap(obj->mapbase, obj->mapsize);
+ linkmap_delete(obj);
+ *linkp = obj->next;
+ obj_count--;
+ obj_free(obj);
+ } else
+ linkp = &obj->next;
+ }
+ obj_tail = linkp;
+}
+
+static void
+unlink_object(Obj_Entry *root)
+{
+ Objlist_Entry *elm;
+
+ if (root->refcount == 0) {
+ /* Remove the object from the RTLD_GLOBAL list. */
+ objlist_remove(&list_global, root);
+
+ /* Remove the object from all objects' DAG lists. */
+ STAILQ_FOREACH(elm, &root->dagmembers, link) {
+ objlist_remove(&elm->obj->dldags, root);
+ if (elm->obj != root)
+ unlink_object(elm->obj);
+ }
+ }
+}
+
+static void
+ref_dag(Obj_Entry *root)
+{
+ Objlist_Entry *elm;
+
+ assert(root->dag_inited);
+ STAILQ_FOREACH(elm, &root->dagmembers, link)
+ elm->obj->refcount++;
+}
+
+static void
+unref_dag(Obj_Entry *root)
+{
+ Objlist_Entry *elm;
+
+ assert(root->dag_inited);
+ STAILQ_FOREACH(elm, &root->dagmembers, link)
+ elm->obj->refcount--;
+}
+
+/*
+ * Common code for MD __tls_get_addr().
+ */
+static void *tls_get_addr_slow(Elf_Addr **, int, size_t) __noinline;
+static void *
+tls_get_addr_slow(Elf_Addr **dtvp, int index, size_t offset)
+{
+ Elf_Addr *newdtv, *dtv;
+ RtldLockState lockstate;
+ int to_copy;
+
+ dtv = *dtvp;
+ /* Check dtv generation in case new modules have arrived */
+ if (dtv[0] != tls_dtv_generation) {
+ wlock_acquire(rtld_bind_lock, &lockstate);
+ newdtv = xcalloc(tls_max_index + 2, sizeof(Elf_Addr));
+ to_copy = dtv[1];
+ if (to_copy > tls_max_index)
+ to_copy = tls_max_index;
+ memcpy(&newdtv[2], &dtv[2], to_copy * sizeof(Elf_Addr));
+ newdtv[0] = tls_dtv_generation;
+ newdtv[1] = tls_max_index;
+ free(dtv);
+ lock_release(rtld_bind_lock, &lockstate);
+ dtv = *dtvp = newdtv;
+ }
+
+ /* Dynamically allocate module TLS if necessary */
+ if (dtv[index + 1] == 0) {
+ /* Signal safe, wlock will block out signals. */
+ wlock_acquire(rtld_bind_lock, &lockstate);
+ if (!dtv[index + 1])
+ dtv[index + 1] = (Elf_Addr)allocate_module_tls(index);
+ lock_release(rtld_bind_lock, &lockstate);
+ }
+ return ((void *)(dtv[index + 1] + offset));
+}
+
+void *
+tls_get_addr_common(Elf_Addr **dtvp, int index, size_t offset)
+{
+ Elf_Addr *dtv;
+
+ dtv = *dtvp;
+ /* Check dtv generation in case new modules have arrived */
+ if (__predict_true(dtv[0] == tls_dtv_generation &&
+ dtv[index + 1] != 0))
+ return ((void *)(dtv[index + 1] + offset));
+ return (tls_get_addr_slow(dtvp, index, offset));
+}
+
+#if defined(__arm__) || defined(__mips__) || defined(__powerpc__)
+
+/*
+ * Allocate Static TLS using the Variant I method.
+ */
+void *
+allocate_tls(Obj_Entry *objs, void *oldtcb, size_t tcbsize, size_t tcbalign)
+{
+ Obj_Entry *obj;
+ char *tcb;
+ Elf_Addr **tls;
+ Elf_Addr *dtv;
+ Elf_Addr addr;
+ int i;
+
+ if (oldtcb != NULL && tcbsize == TLS_TCB_SIZE)
+ return (oldtcb);
+
+ assert(tcbsize >= TLS_TCB_SIZE);
+ tcb = xcalloc(1, tls_static_space - TLS_TCB_SIZE + tcbsize);
+ tls = (Elf_Addr **)(tcb + tcbsize - TLS_TCB_SIZE);
+
+ if (oldtcb != NULL) {
+ memcpy(tls, oldtcb, tls_static_space);
+ free(oldtcb);
+
+ /* Adjust the DTV. */
+ dtv = tls[0];
+ for (i = 0; i < dtv[1]; i++) {
+ if (dtv[i+2] >= (Elf_Addr)oldtcb &&
+ dtv[i+2] < (Elf_Addr)oldtcb + tls_static_space) {
+ dtv[i+2] = dtv[i+2] - (Elf_Addr)oldtcb + (Elf_Addr)tls;
+ }
+ }
+ } else {
+ dtv = xcalloc(tls_max_index + 2, sizeof(Elf_Addr));
+ tls[0] = dtv;
+ dtv[0] = tls_dtv_generation;
+ dtv[1] = tls_max_index;
+
+ for (obj = objs; obj; obj = obj->next) {
+ if (obj->tlsoffset > 0) {
+ addr = (Elf_Addr)tls + obj->tlsoffset;
+ if (obj->tlsinitsize > 0)
+ memcpy((void*) addr, obj->tlsinit, obj->tlsinitsize);
+ if (obj->tlssize > obj->tlsinitsize)
+ memset((void*) (addr + obj->tlsinitsize), 0,
+ obj->tlssize - obj->tlsinitsize);
+ dtv[obj->tlsindex + 1] = addr;
+ }
+ }
+ }
+
+ return (tcb);
+}
+
+void
+free_tls(void *tcb, size_t tcbsize, size_t tcbalign)
+{
+ Elf_Addr *dtv;
+ Elf_Addr tlsstart, tlsend;
+ int dtvsize, i;
+
+ assert(tcbsize >= TLS_TCB_SIZE);
+
+ tlsstart = (Elf_Addr)tcb + tcbsize - TLS_TCB_SIZE;
+ tlsend = tlsstart + tls_static_space;
+
+ dtv = *(Elf_Addr **)tlsstart;
+ dtvsize = dtv[1];
+ for (i = 0; i < dtvsize; i++) {
+ if (dtv[i+2] && (dtv[i+2] < tlsstart || dtv[i+2] >= tlsend)) {
+ free((void*)dtv[i+2]);
+ }
+ }
+ free(dtv);
+ free(tcb);
+}
+
+#endif
+
+#if defined(__i386__) || defined(__amd64__) || defined(__sparc64__)
+
+/*
+ * Allocate Static TLS using the Variant II method.
+ */
+void *
+allocate_tls(Obj_Entry *objs, void *oldtls, size_t tcbsize, size_t tcbalign)
+{
+ Obj_Entry *obj;
+ size_t size, ralign;
+ char *tls;
+ Elf_Addr *dtv, *olddtv;
+ Elf_Addr segbase, oldsegbase, addr;
+ int i;
+
+ ralign = tcbalign;
+ if (tls_static_max_align > ralign)
+ ralign = tls_static_max_align;
+ size = round(tls_static_space, ralign) + round(tcbsize, ralign);
+
+ assert(tcbsize >= 2*sizeof(Elf_Addr));
+ tls = malloc_aligned(size, ralign);
+ dtv = xcalloc(tls_max_index + 2, sizeof(Elf_Addr));
+
+ segbase = (Elf_Addr)(tls + round(tls_static_space, ralign));
+ ((Elf_Addr*)segbase)[0] = segbase;
+ ((Elf_Addr*)segbase)[1] = (Elf_Addr) dtv;
+
+ dtv[0] = tls_dtv_generation;
+ dtv[1] = tls_max_index;
+
+ if (oldtls) {
+ /*
+ * Copy the static TLS block over whole.
+ */
+ oldsegbase = (Elf_Addr) oldtls;
+ memcpy((void *)(segbase - tls_static_space),
+ (const void *)(oldsegbase - tls_static_space),
+ tls_static_space);
+
+ /*
+ * If any dynamic TLS blocks have been created tls_get_addr(),
+ * move them over.
+ */
+ olddtv = ((Elf_Addr**)oldsegbase)[1];
+ for (i = 0; i < olddtv[1]; i++) {
+ if (olddtv[i+2] < oldsegbase - size || olddtv[i+2] > oldsegbase) {
+ dtv[i+2] = olddtv[i+2];
+ olddtv[i+2] = 0;
+ }
+ }
+
+ /*
+ * We assume that this block was the one we created with
+ * allocate_initial_tls().
+ */
+ free_tls(oldtls, 2*sizeof(Elf_Addr), sizeof(Elf_Addr));
+ } else {
+ for (obj = objs; obj; obj = obj->next) {
+ if (obj->tlsoffset) {
+ addr = segbase - obj->tlsoffset;
+ memset((void*) (addr + obj->tlsinitsize),
+ 0, obj->tlssize - obj->tlsinitsize);
+ if (obj->tlsinit)
+ memcpy((void*) addr, obj->tlsinit, obj->tlsinitsize);
+ dtv[obj->tlsindex + 1] = addr;
+ }
+ }
+ }
+
+ return (void*) segbase;
+}
+
+void
+free_tls(void *tls, size_t tcbsize, size_t tcbalign)
+{
+ Elf_Addr* dtv;
+ size_t size, ralign;
+ int dtvsize, i;
+ Elf_Addr tlsstart, tlsend;
+
+ /*
+ * Figure out the size of the initial TLS block so that we can
+ * find stuff which ___tls_get_addr() allocated dynamically.
+ */
+ ralign = tcbalign;
+ if (tls_static_max_align > ralign)
+ ralign = tls_static_max_align;
+ size = round(tls_static_space, ralign);
+
+ dtv = ((Elf_Addr**)tls)[1];
+ dtvsize = dtv[1];
+ tlsend = (Elf_Addr) tls;
+ tlsstart = tlsend - size;
+ for (i = 0; i < dtvsize; i++) {
+ if (dtv[i + 2] != 0 && (dtv[i + 2] < tlsstart || dtv[i + 2] > tlsend)) {
+ free_aligned((void *)dtv[i + 2]);
+ }
+ }
+
+ free_aligned((void *)tlsstart);
+ free((void*) dtv);
+}
+
+#endif
+
+/*
+ * Allocate TLS block for module with given index.
+ */
+void *
+allocate_module_tls(int index)
+{
+ Obj_Entry* obj;
+ char* p;
+
+ for (obj = obj_list; obj; obj = obj->next) {
+ if (obj->tlsindex == index)
+ break;
+ }
+ if (!obj) {
+ _rtld_error("Can't find module with TLS index %d", index);
+ die();
+ }
+
+ p = malloc_aligned(obj->tlssize, obj->tlsalign);
+ memcpy(p, obj->tlsinit, obj->tlsinitsize);
+ memset(p + obj->tlsinitsize, 0, obj->tlssize - obj->tlsinitsize);
+
+ return p;
+}
+
+bool
+allocate_tls_offset(Obj_Entry *obj)
+{
+ size_t off;
+
+ if (obj->tls_done)
+ return true;
+
+ if (obj->tlssize == 0) {
+ obj->tls_done = true;
+ return true;
+ }
+
+ if (obj->tlsindex == 1)
+ off = calculate_first_tls_offset(obj->tlssize, obj->tlsalign);
+ else
+ off = calculate_tls_offset(tls_last_offset, tls_last_size,
+ obj->tlssize, obj->tlsalign);
+
+ /*
+ * If we have already fixed the size of the static TLS block, we
+ * must stay within that size. When allocating the static TLS, we
+ * leave a small amount of space spare to be used for dynamically
+ * loading modules which use static TLS.
+ */
+ if (tls_static_space != 0) {
+ if (calculate_tls_end(off, obj->tlssize) > tls_static_space)
+ return false;
+ } else if (obj->tlsalign > tls_static_max_align) {
+ tls_static_max_align = obj->tlsalign;
+ }
+
+ tls_last_offset = obj->tlsoffset = off;
+ tls_last_size = obj->tlssize;
+ obj->tls_done = true;
+
+ return true;
+}
+
+void
+free_tls_offset(Obj_Entry *obj)
+{
+
+ /*
+ * If we were the last thing to allocate out of the static TLS
+ * block, we give our space back to the 'allocator'. This is a
+ * simplistic workaround to allow libGL.so.1 to be loaded and
+ * unloaded multiple times.
+ */
+ if (calculate_tls_end(obj->tlsoffset, obj->tlssize)
+ == calculate_tls_end(tls_last_offset, tls_last_size)) {
+ tls_last_offset -= obj->tlssize;
+ tls_last_size = 0;
+ }
+}
+
+void *
+_rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign)
+{
+ void *ret;
+ RtldLockState lockstate;
+
+ wlock_acquire(rtld_bind_lock, &lockstate);
+ ret = allocate_tls(obj_list, oldtls, tcbsize, tcbalign);
+ lock_release(rtld_bind_lock, &lockstate);
+ return (ret);
+}
+
+void
+_rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign)
+{
+ RtldLockState lockstate;
+
+ wlock_acquire(rtld_bind_lock, &lockstate);
+ free_tls(tcb, tcbsize, tcbalign);
+ lock_release(rtld_bind_lock, &lockstate);
+}
+
+static void
+object_add_name(Obj_Entry *obj, const char *name)
+{
+ Name_Entry *entry;
+ size_t len;
+
+ len = strlen(name);
+ entry = malloc(sizeof(Name_Entry) + len);
+
+ if (entry != NULL) {
+ strcpy(entry->name, name);
+ STAILQ_INSERT_TAIL(&obj->names, entry, link);
+ }
+}
+
+static int
+object_match_name(const Obj_Entry *obj, const char *name)
+{
+ Name_Entry *entry;
+
+ STAILQ_FOREACH(entry, &obj->names, link) {
+ if (strcmp(name, entry->name) == 0)
+ return (1);
+ }
+ return (0);
+}
+
+static Obj_Entry *
+locate_dependency(const Obj_Entry *obj, const char *name)
+{
+ const Objlist_Entry *entry;
+ const Needed_Entry *needed;
+
+ STAILQ_FOREACH(entry, &list_main, link) {
+ if (object_match_name(entry->obj, name))
+ return entry->obj;
+ }
+
+ for (needed = obj->needed; needed != NULL; needed = needed->next) {
+ if (strcmp(obj->strtab + needed->name, name) == 0 ||
+ (needed->obj != NULL && object_match_name(needed->obj, name))) {
+ /*
+ * If there is DT_NEEDED for the name we are looking for,
+ * we are all set. Note that object might not be found if
+ * dependency was not loaded yet, so the function can
+ * return NULL here. This is expected and handled
+ * properly by the caller.
+ */
+ return (needed->obj);
+ }
+ }
+ _rtld_error("%s: Unexpected inconsistency: dependency %s not found",
+ obj->path, name);
+ die();
+}
+
+static int
+check_object_provided_version(Obj_Entry *refobj, const Obj_Entry *depobj,
+ const Elf_Vernaux *vna)
+{
+ const Elf_Verdef *vd;
+ const char *vername;
+
+ vername = refobj->strtab + vna->vna_name;
+ vd = depobj->verdef;
+ if (vd == NULL) {
+ _rtld_error("%s: version %s required by %s not defined",
+ depobj->path, vername, refobj->path);
+ return (-1);
+ }
+ for (;;) {
+ if (vd->vd_version != VER_DEF_CURRENT) {
+ _rtld_error("%s: Unsupported version %d of Elf_Verdef entry",
+ depobj->path, vd->vd_version);
+ return (-1);
+ }
+ if (vna->vna_hash == vd->vd_hash) {
+ const Elf_Verdaux *aux = (const Elf_Verdaux *)
+ ((char *)vd + vd->vd_aux);
+ if (strcmp(vername, depobj->strtab + aux->vda_name) == 0)
+ return (0);
+ }
+ if (vd->vd_next == 0)
+ break;
+ vd = (const Elf_Verdef *) ((char *)vd + vd->vd_next);
+ }
+ if (vna->vna_flags & VER_FLG_WEAK)
+ return (0);
+ _rtld_error("%s: version %s required by %s not found",
+ depobj->path, vername, refobj->path);
+ return (-1);
+}
+
+static int
+rtld_verify_object_versions(Obj_Entry *obj)
+{
+ const Elf_Verneed *vn;
+ const Elf_Verdef *vd;
+ const Elf_Verdaux *vda;
+ const Elf_Vernaux *vna;
+ const Obj_Entry *depobj;
+ int maxvernum, vernum;
+
+ if (obj->ver_checked)
+ return (0);
+ obj->ver_checked = true;
+
+ maxvernum = 0;
+ /*
+ * Walk over defined and required version records and figure out
+ * max index used by any of them. Do very basic sanity checking
+ * while there.
+ */
+ vn = obj->verneed;
+ while (vn != NULL) {
+ if (vn->vn_version != VER_NEED_CURRENT) {
+ _rtld_error("%s: Unsupported version %d of Elf_Verneed entry",
+ obj->path, vn->vn_version);
+ return (-1);
+ }
+ vna = (const Elf_Vernaux *) ((char *)vn + vn->vn_aux);
+ for (;;) {
+ vernum = VER_NEED_IDX(vna->vna_other);
+ if (vernum > maxvernum)
+ maxvernum = vernum;
+ if (vna->vna_next == 0)
+ break;
+ vna = (const Elf_Vernaux *) ((char *)vna + vna->vna_next);
+ }
+ if (vn->vn_next == 0)
+ break;
+ vn = (const Elf_Verneed *) ((char *)vn + vn->vn_next);
+ }
+
+ vd = obj->verdef;
+ while (vd != NULL) {
+ if (vd->vd_version != VER_DEF_CURRENT) {
+ _rtld_error("%s: Unsupported version %d of Elf_Verdef entry",
+ obj->path, vd->vd_version);
+ return (-1);
+ }
+ vernum = VER_DEF_IDX(vd->vd_ndx);
+ if (vernum > maxvernum)
+ maxvernum = vernum;
+ if (vd->vd_next == 0)
+ break;
+ vd = (const Elf_Verdef *) ((char *)vd + vd->vd_next);
+ }
+
+ if (maxvernum == 0)
+ return (0);
+
+ /*
+ * Store version information in array indexable by version index.
+ * Verify that object version requirements are satisfied along the
+ * way.
+ */
+ obj->vernum = maxvernum + 1;
+ obj->vertab = xcalloc(obj->vernum, sizeof(Ver_Entry));
+
+ vd = obj->verdef;
+ while (vd != NULL) {
+ if ((vd->vd_flags & VER_FLG_BASE) == 0) {
+ vernum = VER_DEF_IDX(vd->vd_ndx);
+ assert(vernum <= maxvernum);
+ vda = (const Elf_Verdaux *)((char *)vd + vd->vd_aux);
+ obj->vertab[vernum].hash = vd->vd_hash;
+ obj->vertab[vernum].name = obj->strtab + vda->vda_name;
+ obj->vertab[vernum].file = NULL;
+ obj->vertab[vernum].flags = 0;
+ }
+ if (vd->vd_next == 0)
+ break;
+ vd = (const Elf_Verdef *) ((char *)vd + vd->vd_next);
+ }
+
+ vn = obj->verneed;
+ while (vn != NULL) {
+ depobj = locate_dependency(obj, obj->strtab + vn->vn_file);
+ if (depobj == NULL)
+ return (-1);
+ vna = (const Elf_Vernaux *) ((char *)vn + vn->vn_aux);
+ for (;;) {
+ if (check_object_provided_version(obj, depobj, vna))
+ return (-1);
+ vernum = VER_NEED_IDX(vna->vna_other);
+ assert(vernum <= maxvernum);
+ obj->vertab[vernum].hash = vna->vna_hash;
+ obj->vertab[vernum].name = obj->strtab + vna->vna_name;
+ obj->vertab[vernum].file = obj->strtab + vn->vn_file;
+ obj->vertab[vernum].flags = (vna->vna_other & VER_NEED_HIDDEN) ?
+ VER_INFO_HIDDEN : 0;
+ if (vna->vna_next == 0)
+ break;
+ vna = (const Elf_Vernaux *) ((char *)vna + vna->vna_next);
+ }
+ if (vn->vn_next == 0)
+ break;
+ vn = (const Elf_Verneed *) ((char *)vn + vn->vn_next);
+ }
+ return 0;
+}
+
+static int
+rtld_verify_versions(const Objlist *objlist)
+{
+ Objlist_Entry *entry;
+ int rc;
+
+ rc = 0;
+ STAILQ_FOREACH(entry, objlist, link) {
+ /*
+ * Skip dummy objects or objects that have their version requirements
+ * already checked.
+ */
+ if (entry->obj->strtab == NULL || entry->obj->vertab != NULL)
+ continue;
+ if (rtld_verify_object_versions(entry->obj) == -1) {
+ rc = -1;
+ if (ld_tracing == NULL)
+ break;
+ }
+ }
+ if (rc == 0 || ld_tracing != NULL)
+ rc = rtld_verify_object_versions(&obj_rtld);
+ return rc;
+}
+
+const Ver_Entry *
+fetch_ventry(const Obj_Entry *obj, unsigned long symnum)
+{
+ Elf_Versym vernum;
+
+ if (obj->vertab) {
+ vernum = VER_NDX(obj->versyms[symnum]);
+ if (vernum >= obj->vernum) {
+ _rtld_error("%s: symbol %s has wrong verneed value %d",
+ obj->path, obj->strtab + symnum, vernum);
+ } else if (obj->vertab[vernum].hash != 0) {
+ return &obj->vertab[vernum];
+ }
+ }
+ return NULL;
+}
+
+int
+_rtld_get_stack_prot(void)
+{
+
+ return (stack_prot);
+}
+
+static void
+map_stacks_exec(RtldLockState *lockstate)
+{
+ void (*thr_map_stacks_exec)(void);
+
+ if ((max_stack_flags & PF_X) == 0 || (stack_prot & PROT_EXEC) != 0)
+ return;
+ thr_map_stacks_exec = (void (*)(void))(uintptr_t)
+ get_program_var_addr("__pthread_map_stacks_exec", lockstate);
+ if (thr_map_stacks_exec != NULL) {
+ stack_prot |= PROT_EXEC;
+ thr_map_stacks_exec();
+ }
+}
+
+void
+symlook_init(SymLook *dst, const char *name)
+{
+
+ bzero(dst, sizeof(*dst));
+ dst->name = name;
+ dst->hash = elf_hash(name);
+ dst->hash_gnu = gnu_hash(name);
+}
+
+static void
+symlook_init_from_req(SymLook *dst, const SymLook *src)
+{
+
+ dst->name = src->name;
+ dst->hash = src->hash;
+ dst->hash_gnu = src->hash_gnu;
+ dst->ventry = src->ventry;
+ dst->flags = src->flags;
+ dst->defobj_out = NULL;
+ dst->sym_out = NULL;
+ dst->lockstate = src->lockstate;
+}
+
+
+/*
+ * Parse a file descriptor number without pulling in more of libc (e.g. atoi).
+ */
+static int
+parse_libdir(const char *str)
+{
+ static const int RADIX = 10; /* XXXJA: possibly support hex? */
+ const char *orig;
+ int fd;
+ char c;
+
+ orig = str;
+ fd = 0;
+ for (c = *str; c != '\0'; c = *++str) {
+ if (c < '0' || c > '9')
+ return (-1);
+
+ fd *= RADIX;
+ fd += c - '0';
+ }
+
+ /* Make sure we actually parsed something. */
+ if (str == orig) {
+ _rtld_error("failed to parse directory FD from '%s'", str);
+ return (-1);
+ }
+ return (fd);
+}
+
+/*
+ * Overrides for libc_pic-provided functions.
+ */
+
+int
+__getosreldate(void)
+{
+ size_t len;
+ int oid[2];
+ int error, osrel;
+
+ if (osreldate != 0)
+ return (osreldate);
+
+ oid[0] = CTL_KERN;
+ oid[1] = KERN_OSRELDATE;
+ osrel = 0;
+ len = sizeof(osrel);
+ error = sysctl(oid, 2, &osrel, &len, NULL, 0);
+ if (error == 0 && osrel > 0 && len == sizeof(osrel))
+ osreldate = osrel;
+ return (osreldate);
+}
+
+void
+exit(int status)
+{
+
+ _exit(status);
+}
+
+void (*__cleanup)(void);
+int __isthreaded = 0;
+int _thread_autoinit_dummy_decl = 1;
+
+/*
+ * No unresolved symbols for rtld.
+ */
+void
+__pthread_cxa_finalize(struct dl_phdr_info *a)
+{
+}
+
+void
+__stack_chk_fail(void)
+{
+
+ _rtld_error("stack overflow detected; terminated");
+ die();
+}
+__weak_reference(__stack_chk_fail, __stack_chk_fail_local);
+
+void
+__chk_fail(void)
+{
+
+ _rtld_error("buffer overflow detected; terminated");
+ die();
+}
+
+const char *
+rtld_strerror(int errnum)
+{
+
+ if (errnum < 0 || errnum >= sys_nerr)
+ return ("Unknown error");
+ return (sys_errlist[errnum]);
+}
diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h
new file mode 100644
index 0000000..ace229f
--- /dev/null
+++ b/libexec/rtld-elf/rtld.h
@@ -0,0 +1,406 @@
+/*-
+ * Copyright 1996, 1997, 1998, 1999, 2000 John D. Polstra.
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef RTLD_H /* { */
+#define RTLD_H 1
+
+#include <machine/elf.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <elf-hints.h>
+#include <link.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <stddef.h>
+
+#include "rtld_lock.h"
+#include "rtld_machdep.h"
+
+#ifdef COMPAT_32BIT
+#undef STANDARD_LIBRARY_PATH
+#undef _PATH_ELF_HINTS
+#define _PATH_ELF_HINTS "/var/run/ld-elf32.so.hints"
+/* For running 32 bit binaries */
+#define STANDARD_LIBRARY_PATH "/lib32:/usr/lib32"
+#define LD_ "LD_32_"
+#endif
+
+#ifndef STANDARD_LIBRARY_PATH
+#define STANDARD_LIBRARY_PATH "/lib:/usr/lib"
+#endif
+#ifndef LD_
+#define LD_ "LD_"
+#endif
+
+#define NEW(type) ((type *) xmalloc(sizeof(type)))
+#define CNEW(type) ((type *) xcalloc(1, sizeof(type)))
+
+/* We might as well do booleans like C++. */
+typedef unsigned char bool;
+#define false 0
+#define true 1
+
+extern size_t tls_last_offset;
+extern size_t tls_last_size;
+extern size_t tls_static_space;
+extern int tls_dtv_generation;
+extern int tls_max_index;
+
+extern int npagesizes;
+extern size_t *pagesizes;
+
+extern int main_argc;
+extern char **main_argv;
+extern char **environ;
+
+struct stat;
+struct Struct_Obj_Entry;
+
+/* Lists of shared objects */
+typedef struct Struct_Objlist_Entry {
+ STAILQ_ENTRY(Struct_Objlist_Entry) link;
+ struct Struct_Obj_Entry *obj;
+} Objlist_Entry;
+
+typedef STAILQ_HEAD(Struct_Objlist, Struct_Objlist_Entry) Objlist;
+
+/* Types of init and fini functions */
+typedef void (*InitFunc)(void);
+typedef void (*InitArrFunc)(int, char **, char **);
+
+/* Lists of shared object dependencies */
+typedef struct Struct_Needed_Entry {
+ struct Struct_Needed_Entry *next;
+ struct Struct_Obj_Entry *obj;
+ unsigned long name; /* Offset of name in string table */
+} Needed_Entry;
+
+typedef struct Struct_Name_Entry {
+ STAILQ_ENTRY(Struct_Name_Entry) link;
+ char name[1];
+} Name_Entry;
+
+/* Lock object */
+typedef struct Struct_LockInfo {
+ void *context; /* Client context for creating locks */
+ void *thelock; /* The one big lock */
+ /* Debugging aids. */
+ volatile int rcount; /* Number of readers holding lock */
+ volatile int wcount; /* Number of writers holding lock */
+ /* Methods */
+ void *(*lock_create)(void *context);
+ void (*rlock_acquire)(void *lock);
+ void (*wlock_acquire)(void *lock);
+ void (*rlock_release)(void *lock);
+ void (*wlock_release)(void *lock);
+ void (*lock_destroy)(void *lock);
+ void (*context_destroy)(void *context);
+} LockInfo;
+
+typedef struct Struct_Ver_Entry {
+ Elf_Word hash;
+ unsigned int flags;
+ const char *name;
+ const char *file;
+} Ver_Entry;
+
+typedef struct Struct_Sym_Match_Result {
+ const Elf_Sym *sym_out;
+ const Elf_Sym *vsymp;
+ int vcount;
+} Sym_Match_Result;
+
+#define VER_INFO_HIDDEN 0x01
+
+/*
+ * Shared object descriptor.
+ *
+ * Items marked with "(%)" are dynamically allocated, and must be freed
+ * when the structure is destroyed.
+ *
+ * CAUTION: It appears that the JDK port peeks into these structures.
+ * It looks at "next" and "mapbase" at least. Don't add new members
+ * near the front, until this can be straightened out.
+ */
+typedef struct Struct_Obj_Entry {
+ /*
+ * These two items have to be set right for compatibility with the
+ * original ElfKit crt1.o.
+ */
+ Elf_Size magic; /* Magic number (sanity check) */
+ Elf_Size version; /* Version number of struct format */
+
+ struct Struct_Obj_Entry *next;
+ char *path; /* Pathname of underlying file (%) */
+ char *origin_path; /* Directory path of origin file */
+ int refcount;
+ int dl_refcount; /* Number of times loaded by dlopen */
+
+ /* These items are computed by map_object() or by digest_phdr(). */
+ caddr_t mapbase; /* Base address of mapped region */
+ size_t mapsize; /* Size of mapped region in bytes */
+ size_t textsize; /* Size of text segment in bytes */
+ Elf_Addr vaddrbase; /* Base address in shared object file */
+ caddr_t relocbase; /* Relocation constant = mapbase - vaddrbase */
+ const Elf_Dyn *dynamic; /* Dynamic section */
+ caddr_t entry; /* Entry point */
+ const Elf_Phdr *phdr; /* Program header if it is mapped, else NULL */
+ size_t phsize; /* Size of program header in bytes */
+ const char *interp; /* Pathname of the interpreter, if any */
+ Elf_Word stack_flags;
+
+ /* TLS information */
+ int tlsindex; /* Index in DTV for this module */
+ void *tlsinit; /* Base address of TLS init block */
+ size_t tlsinitsize; /* Size of TLS init block for this module */
+ size_t tlssize; /* Size of TLS block for this module */
+ size_t tlsoffset; /* Offset of static TLS block for this module */
+ size_t tlsalign; /* Alignment of static TLS block */
+
+ caddr_t relro_page;
+ size_t relro_size;
+
+ /* Items from the dynamic section. */
+ Elf_Addr *pltgot; /* PLT or GOT, depending on architecture */
+ const Elf_Rel *rel; /* Relocation entries */
+ unsigned long relsize; /* Size in bytes of relocation info */
+ const Elf_Rela *rela; /* Relocation entries with addend */
+ unsigned long relasize; /* Size in bytes of addend relocation info */
+ const Elf_Rel *pltrel; /* PLT relocation entries */
+ unsigned long pltrelsize; /* Size in bytes of PLT relocation info */
+ const Elf_Rela *pltrela; /* PLT relocation entries with addend */
+ unsigned long pltrelasize; /* Size in bytes of PLT addend reloc info */
+ const Elf_Sym *symtab; /* Symbol table */
+ const char *strtab; /* String table */
+ unsigned long strsize; /* Size in bytes of string table */
+#ifdef __mips__
+ Elf_Word local_gotno; /* Number of local GOT entries */
+ Elf_Word symtabno; /* Number of dynamic symbols */
+ Elf_Word gotsym; /* First dynamic symbol in GOT */
+#endif
+
+ const Elf_Verneed *verneed; /* Required versions. */
+ Elf_Word verneednum; /* Number of entries in verneed table */
+ const Elf_Verdef *verdef; /* Provided versions. */
+ Elf_Word verdefnum; /* Number of entries in verdef table */
+ const Elf_Versym *versyms; /* Symbol versions table */
+
+ const Elf_Hashelt *buckets; /* Hash table buckets array */
+ unsigned long nbuckets; /* Number of buckets */
+ const Elf_Hashelt *chains; /* Hash table chain array */
+ unsigned long nchains; /* Number of entries in chain array */
+
+ Elf32_Word nbuckets_gnu; /* Number of GNU hash buckets*/
+ Elf32_Word symndx_gnu; /* 1st accessible symbol on dynsym table */
+ Elf32_Word maskwords_bm_gnu; /* Bloom filter words - 1 (bitmask) */
+ Elf32_Word shift2_gnu; /* Bloom filter shift count */
+ Elf32_Word dynsymcount; /* Total entries in dynsym table */
+ Elf_Addr *bloom_gnu; /* Bloom filter used by GNU hash func */
+ const Elf_Hashelt *buckets_gnu; /* GNU hash table bucket array */
+ const Elf_Hashelt *chain_zero_gnu; /* GNU hash table value array (Zeroed) */
+
+ char *rpath; /* Search path specified in object */
+ char *runpath; /* Search path with different priority */
+ Needed_Entry *needed; /* Shared objects needed by this one (%) */
+ Needed_Entry *needed_filtees;
+ Needed_Entry *needed_aux_filtees;
+
+ STAILQ_HEAD(, Struct_Name_Entry) names; /* List of names for this object we
+ know about. */
+ Ver_Entry *vertab; /* Versions required /defined by this object */
+ int vernum; /* Number of entries in vertab */
+
+ Elf_Addr init; /* Initialization function to call */
+ Elf_Addr fini; /* Termination function to call */
+ Elf_Addr preinit_array; /* Pre-initialization array of functions */
+ Elf_Addr init_array; /* Initialization array of functions */
+ Elf_Addr fini_array; /* Termination array of functions */
+ int preinit_array_num; /* Number of entries in preinit_array */
+ int init_array_num; /* Number of entries in init_array */
+ int fini_array_num; /* Number of entries in fini_array */
+
+ int32_t osrel; /* OSREL note value */
+
+ bool mainprog : 1; /* True if this is the main program */
+ bool rtld : 1; /* True if this is the dynamic linker */
+ bool relocated : 1; /* True if processed by relocate_objects() */
+ bool ver_checked : 1; /* True if processed by rtld_verify_object_versions */
+ bool textrel : 1; /* True if there are relocations to text seg */
+ bool symbolic : 1; /* True if generated with "-Bsymbolic" */
+ bool bind_now : 1; /* True if all relocations should be made first */
+ bool traced : 1; /* Already printed in ldd trace output */
+ bool jmpslots_done : 1; /* Already have relocated the jump slots */
+ bool init_done : 1; /* Already have added object to init list */
+ bool tls_done : 1; /* Already allocated offset for static TLS */
+ bool phdr_alloc : 1; /* Phdr is allocated and needs to be freed. */
+ bool z_origin : 1; /* Process rpath and soname tokens */
+ bool z_nodelete : 1; /* Do not unload the object and dependencies */
+ bool z_noopen : 1; /* Do not load on dlopen */
+ bool z_loadfltr : 1; /* Immediately load filtees */
+ bool z_interpose : 1; /* Interpose all objects but main */
+ bool z_nodeflib : 1; /* Don't search default library path */
+ bool ref_nodel : 1; /* Refcount increased to prevent dlclose */
+ bool init_scanned: 1; /* Object is already on init list. */
+ bool on_fini_list: 1; /* Object is already on fini list. */
+ bool dag_inited : 1; /* Object has its DAG initialized. */
+ bool filtees_loaded : 1; /* Filtees loaded */
+ bool irelative : 1; /* Object has R_MACHDEP_IRELATIVE relocs */
+ bool gnu_ifunc : 1; /* Object has references to STT_GNU_IFUNC */
+ bool non_plt_gnu_ifunc : 1; /* Object has non-plt IFUNC references */
+ bool crt_no_init : 1; /* Object' crt does not call _init/_fini */
+ bool valid_hash_sysv : 1; /* A valid System V hash hash tag is available */
+ bool valid_hash_gnu : 1; /* A valid GNU hash tag is available */
+
+ struct link_map linkmap; /* For GDB and dlinfo() */
+ Objlist dldags; /* Object belongs to these dlopened DAGs (%) */
+ Objlist dagmembers; /* DAG has these members (%) */
+ dev_t dev; /* Object's filesystem's device */
+ ino_t ino; /* Object's inode number */
+ void *priv; /* Platform-dependent */
+} Obj_Entry;
+
+#define RTLD_MAGIC 0xd550b87a
+#define RTLD_VERSION 1
+
+#define RTLD_STATIC_TLS_EXTRA 128
+
+/* Flags to be passed into symlook_ family of functions. */
+#define SYMLOOK_IN_PLT 0x01 /* Lookup for PLT symbol */
+#define SYMLOOK_DLSYM 0x02 /* Return newest versioned symbol. Used by
+ dlsym. */
+#define SYMLOOK_EARLY 0x04 /* Symlook is done during initialization. */
+#define SYMLOOK_IFUNC 0x08 /* Allow IFUNC processing in
+ reloc_non_plt(). */
+
+/* Flags for load_object(). */
+#define RTLD_LO_NOLOAD 0x01 /* dlopen() specified RTLD_NOLOAD. */
+#define RTLD_LO_DLOPEN 0x02 /* Load_object() called from dlopen(). */
+#define RTLD_LO_TRACE 0x04 /* Only tracing. */
+#define RTLD_LO_NODELETE 0x08 /* Loaded object cannot be closed. */
+#define RTLD_LO_FILTEES 0x10 /* Loading filtee. */
+#define RTLD_LO_EARLY 0x20 /* Do not call ctors, postpone it to the
+ initialization during the image start. */
+
+/*
+ * Symbol cache entry used during relocation to avoid multiple lookups
+ * of the same symbol.
+ */
+typedef struct Struct_SymCache {
+ const Elf_Sym *sym; /* Symbol table entry */
+ const Obj_Entry *obj; /* Shared object which defines it */
+} SymCache;
+
+/*
+ * This structure provides a reentrant way to keep a list of objects and
+ * check which ones have already been processed in some way.
+ */
+typedef struct Struct_DoneList {
+ const Obj_Entry **objs; /* Array of object pointers */
+ unsigned int num_alloc; /* Allocated size of the array */
+ unsigned int num_used; /* Number of array slots used */
+} DoneList;
+
+struct Struct_RtldLockState {
+ int lockstate;
+ sigjmp_buf env;
+};
+
+struct fill_search_info_args {
+ int request;
+ unsigned int flags;
+ struct dl_serinfo *serinfo;
+ struct dl_serpath *serpath;
+ char *strspace;
+};
+
+/*
+ * The pack of arguments and results for the symbol lookup functions.
+ */
+typedef struct Struct_SymLook {
+ const char *name;
+ unsigned long hash;
+ uint32_t hash_gnu;
+ const Ver_Entry *ventry;
+ int flags;
+ const Obj_Entry *defobj_out;
+ const Elf_Sym *sym_out;
+ struct Struct_RtldLockState *lockstate;
+} SymLook;
+
+void _rtld_error(const char *, ...) __printflike(1, 2);
+const char *rtld_strerror(int);
+Obj_Entry *map_object(int, const char *, const struct stat *);
+void *xcalloc(size_t, size_t);
+void *xmalloc(size_t);
+char *xstrdup(const char *);
+void *malloc_aligned(size_t size, size_t align);
+void free_aligned(void *ptr);
+extern Elf_Addr _GLOBAL_OFFSET_TABLE_[];
+extern Elf_Sym sym_zero; /* For resolving undefined weak refs. */
+
+void dump_relocations(Obj_Entry *);
+void dump_obj_relocations(Obj_Entry *);
+void dump_Elf_Rel(Obj_Entry *, const Elf_Rel *, u_long);
+void dump_Elf_Rela(Obj_Entry *, const Elf_Rela *, u_long);
+
+/*
+ * Function declarations.
+ */
+unsigned long elf_hash(const char *);
+const Elf_Sym *find_symdef(unsigned long, const Obj_Entry *,
+ const Obj_Entry **, int, SymCache *, struct Struct_RtldLockState *);
+void init_pltgot(Obj_Entry *);
+void lockdflt_init(void);
+void digest_notes(Obj_Entry *, Elf_Addr, Elf_Addr);
+void obj_free(Obj_Entry *);
+Obj_Entry *obj_new(void);
+void _rtld_bind_start(void);
+void *rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def);
+void symlook_init(SymLook *, const char *);
+int symlook_obj(SymLook *, const Obj_Entry *);
+void *tls_get_addr_common(Elf_Addr** dtvp, int index, size_t offset);
+void *allocate_tls(Obj_Entry *, void *, size_t, size_t);
+void free_tls(void *, size_t, size_t);
+void *allocate_module_tls(int index);
+bool allocate_tls_offset(Obj_Entry *obj);
+void free_tls_offset(Obj_Entry *obj);
+const Ver_Entry *fetch_ventry(const Obj_Entry *obj, unsigned long);
+
+/*
+ * MD function declarations.
+ */
+int do_copy_relocations(Obj_Entry *);
+int reloc_non_plt(Obj_Entry *, Obj_Entry *, int flags,
+ struct Struct_RtldLockState *);
+int reloc_plt(Obj_Entry *);
+int reloc_jmpslots(Obj_Entry *, int flags, struct Struct_RtldLockState *);
+int reloc_iresolve(Obj_Entry *, struct Struct_RtldLockState *);
+int reloc_gnu_ifunc(Obj_Entry *, int flags, struct Struct_RtldLockState *);
+void allocate_initial_tls(Obj_Entry *);
+
+#endif /* } */
diff --git a/libexec/rtld-elf/rtld_lock.c b/libexec/rtld-elf/rtld_lock.c
new file mode 100644
index 0000000..ea16c4d
--- /dev/null
+++ b/libexec/rtld-elf/rtld_lock.c
@@ -0,0 +1,400 @@
+/*-
+ * Copyright 1999, 2000 John D. Polstra.
+ * 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.
+ *
+ * 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.
+ *
+ * from: FreeBSD: src/libexec/rtld-elf/sparc64/lockdflt.c,v 1.3 2002/10/09
+ * $FreeBSD$
+ */
+
+/*
+ * Thread locking implementation for the dynamic linker.
+ *
+ * We use the "simple, non-scalable reader-preference lock" from:
+ *
+ * J. M. Mellor-Crummey and M. L. Scott. "Scalable Reader-Writer
+ * Synchronization for Shared-Memory Multiprocessors." 3rd ACM Symp. on
+ * Principles and Practice of Parallel Programming, April 1991.
+ *
+ * In this algorithm the lock is a single word. Its low-order bit is
+ * set when a writer holds the lock. The remaining high-order bits
+ * contain a count of readers desiring the lock. The algorithm requires
+ * atomic "compare_and_store" and "add" operations, which we implement
+ * using assembly language sequences in "rtld_start.S".
+ */
+
+#include <sys/param.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "debug.h"
+#include "rtld.h"
+#include "rtld_machdep.h"
+
+#define WAFLAG 0x1 /* A writer holds the lock */
+#define RC_INCR 0x2 /* Adjusts count of readers desiring lock */
+
+typedef struct Struct_Lock {
+ volatile u_int lock;
+ void *base;
+} Lock;
+
+static sigset_t fullsigmask, oldsigmask;
+static int thread_flag;
+
+static void *
+def_lock_create()
+{
+ void *base;
+ char *p;
+ uintptr_t r;
+ Lock *l;
+
+ /*
+ * Arrange for the lock to occupy its own cache line. First, we
+ * optimistically allocate just a cache line, hoping that malloc
+ * will give us a well-aligned block of memory. If that doesn't
+ * work, we allocate a larger block and take a well-aligned cache
+ * line from it.
+ */
+ base = xmalloc(CACHE_LINE_SIZE);
+ p = (char *)base;
+ if ((uintptr_t)p % CACHE_LINE_SIZE != 0) {
+ free(base);
+ base = xmalloc(2 * CACHE_LINE_SIZE);
+ p = (char *)base;
+ if ((r = (uintptr_t)p % CACHE_LINE_SIZE) != 0)
+ p += CACHE_LINE_SIZE - r;
+ }
+ l = (Lock *)p;
+ l->base = base;
+ l->lock = 0;
+ return l;
+}
+
+static void
+def_lock_destroy(void *lock)
+{
+ Lock *l = (Lock *)lock;
+
+ free(l->base);
+}
+
+static void
+def_rlock_acquire(void *lock)
+{
+ Lock *l = (Lock *)lock;
+
+ atomic_add_acq_int(&l->lock, RC_INCR);
+ while (l->lock & WAFLAG)
+ ; /* Spin */
+}
+
+static void
+def_wlock_acquire(void *lock)
+{
+ Lock *l = (Lock *)lock;
+ sigset_t tmp_oldsigmask;
+
+ for ( ; ; ) {
+ sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask);
+ if (atomic_cmpset_acq_int(&l->lock, 0, WAFLAG))
+ break;
+ sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL);
+ }
+ oldsigmask = tmp_oldsigmask;
+}
+
+static void
+def_lock_release(void *lock)
+{
+ Lock *l = (Lock *)lock;
+
+ if ((l->lock & WAFLAG) == 0)
+ atomic_add_rel_int(&l->lock, -RC_INCR);
+ else {
+ atomic_add_rel_int(&l->lock, -WAFLAG);
+ sigprocmask(SIG_SETMASK, &oldsigmask, NULL);
+ }
+}
+
+static int
+def_thread_set_flag(int mask)
+{
+ int old_val = thread_flag;
+ thread_flag |= mask;
+ return (old_val);
+}
+
+static int
+def_thread_clr_flag(int mask)
+{
+ int old_val = thread_flag;
+ thread_flag &= ~mask;
+ return (old_val);
+}
+
+/*
+ * Public interface exposed to the rest of the dynamic linker.
+ */
+static struct RtldLockInfo lockinfo;
+static struct RtldLockInfo deflockinfo;
+
+static __inline int
+thread_mask_set(int mask)
+{
+ return lockinfo.thread_set_flag(mask);
+}
+
+static __inline void
+thread_mask_clear(int mask)
+{
+ lockinfo.thread_clr_flag(mask);
+}
+
+#define RTLD_LOCK_CNT 3
+struct rtld_lock {
+ void *handle;
+ int mask;
+} rtld_locks[RTLD_LOCK_CNT];
+
+rtld_lock_t rtld_bind_lock = &rtld_locks[0];
+rtld_lock_t rtld_libc_lock = &rtld_locks[1];
+rtld_lock_t rtld_phdr_lock = &rtld_locks[2];
+
+void
+rlock_acquire(rtld_lock_t lock, RtldLockState *lockstate)
+{
+
+ if (lockstate == NULL)
+ return;
+
+ if (thread_mask_set(lock->mask) & lock->mask) {
+ dbg("rlock_acquire: recursed");
+ lockstate->lockstate = RTLD_LOCK_UNLOCKED;
+ return;
+ }
+ lockinfo.rlock_acquire(lock->handle);
+ lockstate->lockstate = RTLD_LOCK_RLOCKED;
+}
+
+void
+wlock_acquire(rtld_lock_t lock, RtldLockState *lockstate)
+{
+
+ if (lockstate == NULL)
+ return;
+
+ if (thread_mask_set(lock->mask) & lock->mask) {
+ dbg("wlock_acquire: recursed");
+ lockstate->lockstate = RTLD_LOCK_UNLOCKED;
+ return;
+ }
+ lockinfo.wlock_acquire(lock->handle);
+ lockstate->lockstate = RTLD_LOCK_WLOCKED;
+}
+
+void
+lock_release(rtld_lock_t lock, RtldLockState *lockstate)
+{
+
+ if (lockstate == NULL)
+ return;
+
+ switch (lockstate->lockstate) {
+ case RTLD_LOCK_UNLOCKED:
+ break;
+ case RTLD_LOCK_RLOCKED:
+ case RTLD_LOCK_WLOCKED:
+ thread_mask_clear(lock->mask);
+ lockinfo.lock_release(lock->handle);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+void
+lock_upgrade(rtld_lock_t lock, RtldLockState *lockstate)
+{
+
+ if (lockstate == NULL)
+ return;
+
+ lock_release(lock, lockstate);
+ wlock_acquire(lock, lockstate);
+}
+
+void
+lock_restart_for_upgrade(RtldLockState *lockstate)
+{
+
+ if (lockstate == NULL)
+ return;
+
+ switch (lockstate->lockstate) {
+ case RTLD_LOCK_UNLOCKED:
+ case RTLD_LOCK_WLOCKED:
+ break;
+ case RTLD_LOCK_RLOCKED:
+ siglongjmp(lockstate->env, 1);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+void
+lockdflt_init()
+{
+ int i;
+
+ deflockinfo.rtli_version = RTLI_VERSION;
+ deflockinfo.lock_create = def_lock_create;
+ deflockinfo.lock_destroy = def_lock_destroy;
+ deflockinfo.rlock_acquire = def_rlock_acquire;
+ deflockinfo.wlock_acquire = def_wlock_acquire;
+ deflockinfo.lock_release = def_lock_release;
+ deflockinfo.thread_set_flag = def_thread_set_flag;
+ deflockinfo.thread_clr_flag = def_thread_clr_flag;
+ deflockinfo.at_fork = NULL;
+
+ for (i = 0; i < RTLD_LOCK_CNT; i++) {
+ rtld_locks[i].mask = (1 << i);
+ rtld_locks[i].handle = NULL;
+ }
+
+ memcpy(&lockinfo, &deflockinfo, sizeof(lockinfo));
+ _rtld_thread_init(NULL);
+ /*
+ * Construct a mask to block all signals except traps which might
+ * conceivably be generated within the dynamic linker itself.
+ */
+ sigfillset(&fullsigmask);
+ sigdelset(&fullsigmask, SIGILL);
+ sigdelset(&fullsigmask, SIGTRAP);
+ sigdelset(&fullsigmask, SIGABRT);
+ sigdelset(&fullsigmask, SIGEMT);
+ sigdelset(&fullsigmask, SIGFPE);
+ sigdelset(&fullsigmask, SIGBUS);
+ sigdelset(&fullsigmask, SIGSEGV);
+ sigdelset(&fullsigmask, SIGSYS);
+}
+
+/*
+ * Callback function to allow threads implementation to
+ * register their own locking primitives if the default
+ * one is not suitable.
+ * The current context should be the only context
+ * executing at the invocation time.
+ */
+void
+_rtld_thread_init(struct RtldLockInfo *pli)
+{
+ int flags, i;
+ void *locks[RTLD_LOCK_CNT];
+
+ /* disable all locking while this function is running */
+ flags = thread_mask_set(~0);
+
+ if (pli == NULL)
+ pli = &deflockinfo;
+
+
+ for (i = 0; i < RTLD_LOCK_CNT; i++)
+ if ((locks[i] = pli->lock_create()) == NULL)
+ break;
+
+ if (i < RTLD_LOCK_CNT) {
+ while (--i >= 0)
+ pli->lock_destroy(locks[i]);
+ abort();
+ }
+
+ for (i = 0; i < RTLD_LOCK_CNT; i++) {
+ if (rtld_locks[i].handle == NULL)
+ continue;
+ if (flags & rtld_locks[i].mask)
+ lockinfo.lock_release(rtld_locks[i].handle);
+ lockinfo.lock_destroy(rtld_locks[i].handle);
+ }
+
+ for (i = 0; i < RTLD_LOCK_CNT; i++) {
+ rtld_locks[i].handle = locks[i];
+ if (flags & rtld_locks[i].mask)
+ pli->wlock_acquire(rtld_locks[i].handle);
+ }
+
+ lockinfo.lock_create = pli->lock_create;
+ lockinfo.lock_destroy = pli->lock_destroy;
+ lockinfo.rlock_acquire = pli->rlock_acquire;
+ lockinfo.wlock_acquire = pli->wlock_acquire;
+ lockinfo.lock_release = pli->lock_release;
+ lockinfo.thread_set_flag = pli->thread_set_flag;
+ lockinfo.thread_clr_flag = pli->thread_clr_flag;
+ lockinfo.at_fork = pli->at_fork;
+
+ /* restore thread locking state, this time with new locks */
+ thread_mask_clear(~0);
+ thread_mask_set(flags);
+ dbg("_rtld_thread_init: done");
+}
+
+void
+_rtld_atfork_pre(int *locks)
+{
+ RtldLockState ls[2];
+
+ if (locks == NULL)
+ return;
+
+ /*
+ * Warning: this does not work with the rtld compat locks
+ * above, since the thread signal mask is corrupted (set to
+ * all signals blocked) if two locks are taken in write mode.
+ * The caller of the _rtld_atfork_pre() must provide the
+ * working implementation of the locks, and libthr locks are
+ * fine.
+ */
+ wlock_acquire(rtld_phdr_lock, &ls[0]);
+ wlock_acquire(rtld_bind_lock, &ls[1]);
+
+ /* XXXKIB: I am really sorry for this. */
+ locks[0] = ls[1].lockstate;
+ locks[2] = ls[0].lockstate;
+}
+
+void
+_rtld_atfork_post(int *locks)
+{
+ RtldLockState ls[2];
+
+ if (locks == NULL)
+ return;
+
+ bzero(ls, sizeof(ls));
+ ls[0].lockstate = locks[2];
+ ls[1].lockstate = locks[0];
+ lock_release(rtld_bind_lock, &ls[1]);
+ lock_release(rtld_phdr_lock, &ls[0]);
+}
diff --git a/libexec/rtld-elf/rtld_lock.h b/libexec/rtld-elf/rtld_lock.h
new file mode 100644
index 0000000..fa63787
--- /dev/null
+++ b/libexec/rtld-elf/rtld_lock.h
@@ -0,0 +1,75 @@
+/*-
+ * Copyright 2003 Alexander Kabaev.
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _RTLD_LOCK_H_
+#define _RTLD_LOCK_H_
+
+#define RTLI_VERSION 0x01
+#define MAX_RTLD_LOCKS 8
+
+struct RtldLockInfo
+{
+ unsigned int rtli_version;
+ void *(*lock_create)(void);
+ void (*lock_destroy)(void *);
+ void (*rlock_acquire)(void *);
+ void (*wlock_acquire)(void *);
+ void (*lock_release)(void *);
+ int (*thread_set_flag)(int);
+ int (*thread_clr_flag)(int);
+ void (*at_fork)(void);
+};
+
+extern void _rtld_thread_init(struct RtldLockInfo *);
+extern void _rtld_atfork_pre(int *);
+extern void _rtld_atfork_post(int *);
+
+#ifdef IN_RTLD
+
+struct rtld_lock;
+typedef struct rtld_lock *rtld_lock_t;
+
+extern rtld_lock_t rtld_bind_lock;
+extern rtld_lock_t rtld_libc_lock;
+extern rtld_lock_t rtld_phdr_lock;
+
+#define RTLD_LOCK_UNLOCKED 0
+#define RTLD_LOCK_RLOCKED 1
+#define RTLD_LOCK_WLOCKED 2
+
+struct Struct_RtldLockState;
+typedef struct Struct_RtldLockState RtldLockState;
+
+void rlock_acquire(rtld_lock_t, RtldLockState *);
+void wlock_acquire(rtld_lock_t, RtldLockState *);
+void lock_release(rtld_lock_t, RtldLockState *);
+void lock_upgrade(rtld_lock_t, RtldLockState *);
+void lock_restart_for_upgrade(RtldLockState *);
+
+#endif /* IN_RTLD */
+
+#endif
diff --git a/libexec/rtld-elf/rtld_printf.c b/libexec/rtld-elf/rtld_printf.c
new file mode 100644
index 0000000..9d945dc
--- /dev/null
+++ b/libexec/rtld-elf/rtld_printf.c
@@ -0,0 +1,501 @@
+/*-
+ * Copyright (c) 1986, 1988, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ * Copyright (c) 2011 Konstantin Belousov <kib@FreeBSD.org>
+ *
+ * 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.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include "rtld_printf.h"
+
+#define MAXNBUF (sizeof(intmax_t) * NBBY + 1)
+
+#define PRINT_METHOD_SNPRINTF 1
+#define PRINT_METHOD_WRITE 2
+
+struct snprintf_arg {
+ int method;
+ char *str;
+ char *buf;
+ size_t remain;
+ size_t buf_total;
+ int fd;
+};
+
+static void
+printf_out(struct snprintf_arg *info)
+{
+
+ if (info->remain == info->buf_total)
+ return;
+ write(info->fd, info->buf, info->buf_total - info->remain);
+ info->str = info->buf;
+ info->remain = info->buf_total;
+}
+
+static void
+snprintf_func(int ch, struct snprintf_arg *const info)
+{
+
+ switch (info->method) {
+ case PRINT_METHOD_SNPRINTF:
+ if (info->remain >= 2) {
+ *info->str++ = ch;
+ info->remain--;
+ }
+ break;
+ case PRINT_METHOD_WRITE:
+ if (info->remain > 0) {
+ *info->str++ = ch;
+ info->remain--;
+ } else
+ printf_out(info);
+ break;
+ }
+}
+
+static char const hex2ascii_lower[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+static char const hex2ascii_upper[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+#define hex2ascii(hex) (hex2ascii_lower[hex])
+#define hex2ascii_upper(hex) (hex2ascii_upper[hex])
+
+static __inline int
+imax(int a, int b)
+{
+
+ return (a > b ? a : b);
+}
+
+static char *
+ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
+{
+ char *p, c;
+
+ p = nbuf;
+ *p = '\0';
+ do {
+ c = upper ? hex2ascii_upper(num % base) :
+ hex2ascii(num % base);
+ *++p = c;
+ } while (num /= base);
+ if (lenp)
+ *lenp = p - nbuf;
+ return (p);
+}
+
+static int
+kvprintf(char const *fmt, struct snprintf_arg *arg, int radix, va_list ap)
+{
+#define PCHAR(c) snprintf_func((c), arg)
+ char nbuf[MAXNBUF];
+ const char *p, *percent, *q;
+ u_char *up;
+ int ch, n;
+ uintmax_t num;
+ int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
+ int cflag, hflag, jflag, tflag, zflag;
+ int dwidth, upper;
+ char padc;
+ int stop = 0, retval = 0;
+
+ num = 0;
+
+ if (fmt == NULL)
+ fmt = "(fmt null)\n";
+
+ if (radix < 2 || radix > 36)
+ radix = 10;
+
+ for (;;) {
+ padc = ' ';
+ width = 0;
+ while ((ch = (u_char)*fmt++) != '%' || stop) {
+ if (ch == '\0')
+ return (retval);
+ PCHAR(ch);
+ }
+ percent = fmt - 1;
+ qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
+ sign = 0; dot = 0; dwidth = 0; upper = 0;
+ cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
+reswitch: switch (ch = (u_char)*fmt++) {
+ case '.':
+ dot = 1;
+ goto reswitch;
+ case '#':
+ sharpflag = 1;
+ goto reswitch;
+ case '+':
+ sign = 1;
+ goto reswitch;
+ case '-':
+ ladjust = 1;
+ goto reswitch;
+ case '%':
+ PCHAR(ch);
+ break;
+ case '*':
+ if (!dot) {
+ width = va_arg(ap, int);
+ if (width < 0) {
+ ladjust = !ladjust;
+ width = -width;
+ }
+ } else {
+ dwidth = va_arg(ap, int);
+ }
+ goto reswitch;
+ case '0':
+ if (!dot) {
+ padc = '0';
+ goto reswitch;
+ }
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ for (n = 0;; ++fmt) {
+ n = n * 10 + ch - '0';
+ ch = *fmt;
+ if (ch < '0' || ch > '9')
+ break;
+ }
+ if (dot)
+ dwidth = n;
+ else
+ width = n;
+ goto reswitch;
+ case 'b':
+ num = (u_int)va_arg(ap, int);
+ p = va_arg(ap, char *);
+ for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;)
+ PCHAR(*q--);
+
+ if (num == 0)
+ break;
+
+ for (tmp = 0; *p;) {
+ n = *p++;
+ if (num & (1 << (n - 1))) {
+ PCHAR(tmp ? ',' : '<');
+ for (; (n = *p) > ' '; ++p)
+ PCHAR(n);
+ tmp = 1;
+ } else
+ for (; *p > ' '; ++p)
+ continue;
+ }
+ if (tmp)
+ PCHAR('>');
+ break;
+ case 'c':
+ PCHAR(va_arg(ap, int));
+ break;
+ case 'D':
+ up = va_arg(ap, u_char *);
+ p = va_arg(ap, char *);
+ if (!width)
+ width = 16;
+ while(width--) {
+ PCHAR(hex2ascii(*up >> 4));
+ PCHAR(hex2ascii(*up & 0x0f));
+ up++;
+ if (width)
+ for (q=p;*q;q++)
+ PCHAR(*q);
+ }
+ break;
+ case 'd':
+ case 'i':
+ base = 10;
+ sign = 1;
+ goto handle_sign;
+ case 'h':
+ if (hflag) {
+ hflag = 0;
+ cflag = 1;
+ } else
+ hflag = 1;
+ goto reswitch;
+ case 'j':
+ jflag = 1;
+ goto reswitch;
+ case 'l':
+ if (lflag) {
+ lflag = 0;
+ qflag = 1;
+ } else
+ lflag = 1;
+ goto reswitch;
+ case 'n':
+ if (jflag)
+ *(va_arg(ap, intmax_t *)) = retval;
+ else if (qflag)
+ *(va_arg(ap, quad_t *)) = retval;
+ else if (lflag)
+ *(va_arg(ap, long *)) = retval;
+ else if (zflag)
+ *(va_arg(ap, size_t *)) = retval;
+ else if (hflag)
+ *(va_arg(ap, short *)) = retval;
+ else if (cflag)
+ *(va_arg(ap, char *)) = retval;
+ else
+ *(va_arg(ap, int *)) = retval;
+ break;
+ case 'o':
+ base = 8;
+ goto handle_nosign;
+ case 'p':
+ base = 16;
+ sharpflag = (width == 0);
+ sign = 0;
+ num = (uintptr_t)va_arg(ap, void *);
+ goto number;
+ case 'q':
+ qflag = 1;
+ goto reswitch;
+ case 'r':
+ base = radix;
+ if (sign)
+ goto handle_sign;
+ goto handle_nosign;
+ case 's':
+ p = va_arg(ap, char *);
+ if (p == NULL)
+ p = "(null)";
+ if (!dot)
+ n = strlen (p);
+ else
+ for (n = 0; n < dwidth && p[n]; n++)
+ continue;
+
+ width -= n;
+
+ if (!ladjust && width > 0)
+ while (width--)
+ PCHAR(padc);
+ while (n--)
+ PCHAR(*p++);
+ if (ladjust && width > 0)
+ while (width--)
+ PCHAR(padc);
+ break;
+ case 't':
+ tflag = 1;
+ goto reswitch;
+ case 'u':
+ base = 10;
+ goto handle_nosign;
+ case 'X':
+ upper = 1;
+ case 'x':
+ base = 16;
+ goto handle_nosign;
+ case 'y':
+ base = 16;
+ sign = 1;
+ goto handle_sign;
+ case 'z':
+ zflag = 1;
+ goto reswitch;
+handle_nosign:
+ sign = 0;
+ if (jflag)
+ num = va_arg(ap, uintmax_t);
+ else if (qflag)
+ num = va_arg(ap, u_quad_t);
+ else if (tflag)
+ num = va_arg(ap, ptrdiff_t);
+ else if (lflag)
+ num = va_arg(ap, u_long);
+ else if (zflag)
+ num = va_arg(ap, size_t);
+ else if (hflag)
+ num = (u_short)va_arg(ap, int);
+ else if (cflag)
+ num = (u_char)va_arg(ap, int);
+ else
+ num = va_arg(ap, u_int);
+ goto number;
+handle_sign:
+ if (jflag)
+ num = va_arg(ap, intmax_t);
+ else if (qflag)
+ num = va_arg(ap, quad_t);
+ else if (tflag)
+ num = va_arg(ap, ptrdiff_t);
+ else if (lflag)
+ num = va_arg(ap, long);
+ else if (zflag)
+ num = va_arg(ap, ssize_t);
+ else if (hflag)
+ num = (short)va_arg(ap, int);
+ else if (cflag)
+ num = (char)va_arg(ap, int);
+ else
+ num = va_arg(ap, int);
+number:
+ if (sign && (intmax_t)num < 0) {
+ neg = 1;
+ num = -(intmax_t)num;
+ }
+ p = ksprintn(nbuf, num, base, &n, upper);
+ tmp = 0;
+ if (sharpflag && num != 0) {
+ if (base == 8)
+ tmp++;
+ else if (base == 16)
+ tmp += 2;
+ }
+ if (neg)
+ tmp++;
+
+ if (!ladjust && padc == '0')
+ dwidth = width - tmp;
+ width -= tmp + imax(dwidth, n);
+ dwidth -= n;
+ if (!ladjust)
+ while (width-- > 0)
+ PCHAR(' ');
+ if (neg)
+ PCHAR('-');
+ if (sharpflag && num != 0) {
+ if (base == 8) {
+ PCHAR('0');
+ } else if (base == 16) {
+ PCHAR('0');
+ PCHAR('x');
+ }
+ }
+ while (dwidth-- > 0)
+ PCHAR('0');
+
+ while (*p)
+ PCHAR(*p--);
+
+ if (ladjust)
+ while (width-- > 0)
+ PCHAR(' ');
+
+ break;
+ default:
+ while (percent < fmt)
+ PCHAR(*percent++);
+ /*
+ * Since we ignore an formatting argument it is no
+ * longer safe to obey the remaining formatting
+ * arguments as the arguments will no longer match
+ * the format specs.
+ */
+ stop = 1;
+ break;
+ }
+ }
+#undef PCHAR
+}
+
+int
+rtld_snprintf(char *buf, size_t bufsize, const char *fmt, ...)
+{
+ va_list ap;
+ int retval;
+
+ va_start(ap, fmt);
+ retval = rtld_vsnprintf(buf, bufsize, fmt, ap);
+ va_end(ap);
+ return (retval);
+}
+
+int
+rtld_vsnprintf(char *buf, size_t bufsize, const char *fmt, va_list ap)
+{
+ struct snprintf_arg info;
+ int retval;
+
+ info.method = PRINT_METHOD_SNPRINTF;
+ info.buf = info.str = buf;
+ info.buf_total = info.remain = bufsize;
+ info.fd = -1;
+ retval = kvprintf(fmt, &info, 10, ap);
+ if (info.remain >= 1)
+ *info.str++ = '\0';
+ return (retval);
+}
+
+int
+rtld_vfdprintf(int fd, const char *fmt, va_list ap)
+{
+ char buf[512];
+ struct snprintf_arg info;
+ int retval;
+
+ info.method = PRINT_METHOD_WRITE;
+ info.buf = info.str = buf;
+ info.buf_total = info.remain = sizeof(buf);
+ info.fd = fd;
+ retval = kvprintf(fmt, &info, 10, ap);
+ printf_out(&info);
+ return (retval);
+}
+
+int
+rtld_fdprintf(int fd, const char *fmt, ...)
+{
+ va_list ap;
+ int retval;
+
+ va_start(ap, fmt);
+ retval = rtld_vfdprintf(fd, fmt, ap);
+ va_end(ap);
+ return (retval);
+}
+
+void
+rtld_fdputstr(int fd, const char *str)
+{
+
+ write(fd, str, strlen(str));
+}
+
+void
+rtld_fdputchar(int fd, int c)
+{
+ char c1;
+
+ c1 = c;
+ write(fd, &c1, 1);
+}
diff --git a/libexec/rtld-elf/rtld_printf.h b/libexec/rtld-elf/rtld_printf.h
new file mode 100644
index 0000000..aaf2971
--- /dev/null
+++ b/libexec/rtld-elf/rtld_printf.h
@@ -0,0 +1,46 @@
+/*-
+ * Copyright 2011 Konstantin Belousov <kib@FreeBSD.org>.
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef RTLD_PRINTF_H
+#define RTLD_PRINTF_H 1
+
+#include <sys/cdefs.h>
+#include <unistd.h>
+
+int rtld_snprintf(char *buf, size_t bufsize, const char *fmt, ...)
+ __printflike(3, 4);
+int rtld_vsnprintf(char *buf, size_t bufsize, const char *fmt, va_list ap);
+int rtld_vfdprintf(int fd, const char *fmt, va_list ap);
+int rtld_fdprintf(int fd, const char *fmt, ...) __printflike(2, 3);
+void rtld_fdputstr(int fd, const char *str);
+void rtld_fdputchar(int fd, int c);
+
+#define rtld_printf(...) rtld_fdprintf(STDOUT_FILENO, __VA_ARGS__)
+#define rtld_putstr(str) rtld_fdputstr(STDOUT_FILENO, (str))
+#define rtld_putchar(c) rtld_fdputchar(STDOUT_FILENO, (c))
+
+#endif
diff --git a/libexec/rtld-elf/rtld_tls.h b/libexec/rtld-elf/rtld_tls.h
new file mode 100644
index 0000000..b85db59
--- /dev/null
+++ b/libexec/rtld-elf/rtld_tls.h
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 2004 Doug Rabson
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Semi-public interface from thread libraries to rtld for managing
+ * TLS.
+ */
+
+#ifndef _RTLD_TLS_H_
+#define _RTLD_TLS_H_
+
+/*
+ * Allocate a TLS block for a new thread. The memory allocated will
+ * include 'tcbsize' bytes aligned to a 'tcbalign' boundary (in bytes)
+ * for the thread library's private purposes. The location of the TCB
+ * block is returned by this function. For architectures using
+ * 'Variant I' TLS, the thread local storage follows the TCB, and for
+ * 'Variant II', the thread local storage precedes it. For
+ * architectures using the 'Variant II' model (e.g. i386, amd64,
+ * sparc64), the TCB must begin with two pointer fields which are used
+ * by rtld for its TLS implementation. For the 'Variant I' model, the
+ * TCB must begin with a single pointer field for rtld's
+ * implementation.
+ *
+ * If the value of 'oldtls' is non-NULL, the new TLS block will be
+ * initialised using the values contained in 'oldtls' and 'oldtls'
+ * will be freed. This is typically used when initialising a thread
+ * library to migrate from using the initial bootstrap TLS block
+ * created by rtld to one which contains suitable thread library
+ * private data.
+ *
+ * The value returned from this function is suitable for installing
+ * directly into the thread pointer register.
+ */
+extern void *_rtld_allocate_tls(void* oldtls, size_t tcbsize, size_t tcbalign);
+
+/*
+ * Free a TLS block allocated using _rtld_allocate_tls(). The tcbsize
+ * and tcbalign parameters must be the same as those used to allocate
+ * the block.
+ */
+extern void _rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign);
+
+#endif
diff --git a/libexec/rtld-elf/sparc64/Makefile.inc b/libexec/rtld-elf/sparc64/Makefile.inc
new file mode 100644
index 0000000..e8c0da7
--- /dev/null
+++ b/libexec/rtld-elf/sparc64/Makefile.inc
@@ -0,0 +1 @@
+# $FreeBSD$
diff --git a/libexec/rtld-elf/sparc64/reloc.c b/libexec/rtld-elf/sparc64/reloc.c
new file mode 100644
index 0000000..738a847
--- /dev/null
+++ b/libexec/rtld-elf/sparc64/reloc.c
@@ -0,0 +1,855 @@
+/* $NetBSD: mdreloc.c,v 1.42 2008/04/28 20:23:04 martin Exp $ */
+
+/*-
+ * Copyright (c) 2000 Eduardo Horvath.
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Paul Kranenburg.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/mman.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+#include "rtld.h"
+
+/*
+ * The following table holds for each relocation type:
+ * - the width in bits of the memory location the relocation
+ * applies to (not currently used)
+ * - the number of bits the relocation value must be shifted to the
+ * right (i.e. discard least significant bits) to fit into
+ * the appropriate field in the instruction word.
+ * - flags indicating whether
+ * * the relocation involves a symbol
+ * * the relocation is relative to the current position
+ * * the relocation is for a GOT entry
+ * * the relocation is relative to the load address
+ *
+ */
+#define _RF_S 0x80000000 /* Resolve symbol */
+#define _RF_A 0x40000000 /* Use addend */
+#define _RF_P 0x20000000 /* Location relative */
+#define _RF_G 0x10000000 /* GOT offset */
+#define _RF_B 0x08000000 /* Load address relative */
+#define _RF_U 0x04000000 /* Unaligned */
+#define _RF_X 0x02000000 /* Bare symbols, needs proc */
+#define _RF_D 0x01000000 /* Use dynamic TLS offset */
+#define _RF_O 0x00800000 /* Use static TLS offset */
+#define _RF_I 0x00400000 /* Use TLS object ID */
+#define _RF_SZ(s) (((s) & 0xff) << 8) /* memory target size */
+#define _RF_RS(s) ( (s) & 0xff) /* right shift */
+static const int reloc_target_flags[] = {
+ 0, /* NONE */
+ _RF_S|_RF_A| _RF_SZ(8) | _RF_RS(0), /* 8 */
+ _RF_S|_RF_A| _RF_SZ(16) | _RF_RS(0), /* 16 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 32 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(8) | _RF_RS(0), /* DISP_8 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(16) | _RF_RS(0), /* DISP_16 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* DISP_32 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP_30 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP_22 */
+ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(10), /* HI22 */
+ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 22 */
+ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 13 */
+ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* LO10 */
+ _RF_G| _RF_SZ(32) | _RF_RS(0), /* GOT10 */
+ _RF_G| _RF_SZ(32) | _RF_RS(0), /* GOT13 */
+ _RF_G| _RF_SZ(32) | _RF_RS(10), /* GOT22 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PC10 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PC22 */
+ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WPLT30 */
+ _RF_SZ(32) | _RF_RS(0), /* COPY */
+ _RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0), /* GLOB_DAT */
+ _RF_SZ(32) | _RF_RS(0), /* JMP_SLOT */
+ _RF_A| _RF_B| _RF_SZ(64) | _RF_RS(0), /* RELATIVE */
+ _RF_S|_RF_A| _RF_U| _RF_SZ(32) | _RF_RS(0), /* UA_32 */
+
+ _RF_A| _RF_SZ(32) | _RF_RS(0), /* PLT32 */
+ _RF_A| _RF_SZ(32) | _RF_RS(10), /* HIPLT22 */
+ _RF_A| _RF_SZ(32) | _RF_RS(0), /* LOPLT10 */
+ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PCPLT32 */
+ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PCPLT22 */
+ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PCPLT10 */
+ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 10 */
+ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 11 */
+ _RF_S|_RF_A|_RF_X| _RF_SZ(64) | _RF_RS(0), /* 64 */
+ _RF_S|_RF_A|/*extra*/ _RF_SZ(32) | _RF_RS(0), /* OLO10 */
+ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(42), /* HH22 */
+ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(32), /* HM10 */
+ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(10), /* LM22 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(42), /* PC_HH22 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(32), /* PC_HM10 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PC_LM22 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP16 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP19 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* GLOB_JMP */
+ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 7 */
+ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 5 */
+ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 6 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(64) | _RF_RS(0), /* DISP64 */
+ _RF_A| _RF_SZ(64) | _RF_RS(0), /* PLT64 */
+ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(10), /* HIX22 */
+ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* LOX10 */
+ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(22), /* H44 */
+ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(12), /* M44 */
+ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* L44 */
+ _RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0), /* REGISTER */
+ _RF_S|_RF_A| _RF_U| _RF_SZ(64) | _RF_RS(0), /* UA64 */
+ _RF_S|_RF_A| _RF_U| _RF_SZ(16) | _RF_RS(0), /* UA16 */
+
+ /* TLS */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* GD_HI22 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* GD_LO10 */
+ 0, /* GD_ADD */
+ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* GD_CALL */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* LDM_HI22 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* LDM_LO10 */
+ 0, /* LDM_ADD */
+ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* LDM_CALL */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* LDO_HIX22 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* LDO_LOX10 */
+ 0, /* LDO_ADD */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* IE_HI22 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* IE_LO10 */
+ 0, /* IE_LD */
+ 0, /* IE_LDX */
+ 0, /* IE_ADD */
+ _RF_S|_RF_A| _RF_O| _RF_SZ(32) | _RF_RS(10), /* LE_HIX22 */
+ _RF_S|_RF_A| _RF_O| _RF_SZ(32) | _RF_RS(0), /* LE_LOX10 */
+ _RF_S| _RF_I| _RF_SZ(32) | _RF_RS(0), /* DTPMOD32 */
+ _RF_S| _RF_I| _RF_SZ(64) | _RF_RS(0), /* DTPMOD64 */
+ _RF_S|_RF_A| _RF_D| _RF_SZ(32) | _RF_RS(0), /* DTPOFF32 */
+ _RF_S|_RF_A| _RF_D| _RF_SZ(64) | _RF_RS(0), /* DTPOFF64 */
+ _RF_S|_RF_A| _RF_O| _RF_SZ(32) | _RF_RS(0), /* TPOFF32 */
+ _RF_S|_RF_A| _RF_O| _RF_SZ(64) | _RF_RS(0) /* TPOFF64 */
+};
+
+#if 0
+static const char *const reloc_names[] = {
+ "NONE", "8", "16", "32", "DISP_8", "DISP_16", "DISP_32", "WDISP_30",
+ "WDISP_22", "HI22", "22", "13", "LO10", "GOT10", "GOT13", "GOT22",
+ "PC10", "PC22", "WPLT30", "COPY", "GLOB_DAT", "JMP_SLOT", "RELATIVE",
+ "UA_32", "PLT32", "HIPLT22", "LOPLT10", "LOPLT10", "PCPLT22",
+ "PCPLT32", "10", "11", "64", "OLO10", "HH22", "HM10", "LM22",
+ "PC_HH22", "PC_HM10", "PC_LM22", "WDISP16", "WDISP19", "GLOB_JMP",
+ "7", "5", "6", "DISP64", "PLT64", "HIX22", "LOX10", "H44", "M44",
+ "L44", "REGISTER", "UA64", "UA16", "GD_HI22", "GD_LO10", "GD_ADD",
+ "GD_CALL", "LDM_HI22", "LDMO10", "LDM_ADD", "LDM_CALL", "LDO_HIX22",
+ "LDO_LOX10", "LDO_ADD", "IE_HI22", "IE_LO10", "IE_LD", "IE_LDX",
+ "IE_ADD", "LE_HIX22", "LE_LOX10", "DTPMOD32", "DTPMOD64", "DTPOFF32",
+ "DTPOFF64", "TPOFF32", "TPOFF64"
+};
+#endif
+
+#define RELOC_RESOLVE_SYMBOL(t) ((reloc_target_flags[t] & _RF_S) != 0)
+#define RELOC_PC_RELATIVE(t) ((reloc_target_flags[t] & _RF_P) != 0)
+#define RELOC_BASE_RELATIVE(t) ((reloc_target_flags[t] & _RF_B) != 0)
+#define RELOC_UNALIGNED(t) ((reloc_target_flags[t] & _RF_U) != 0)
+#define RELOC_USE_ADDEND(t) ((reloc_target_flags[t] & _RF_A) != 0)
+#define RELOC_BARE_SYMBOL(t) ((reloc_target_flags[t] & _RF_X) != 0)
+#define RELOC_USE_TLS_DOFF(t) ((reloc_target_flags[t] & _RF_D) != 0)
+#define RELOC_USE_TLS_OFF(t) ((reloc_target_flags[t] & _RF_O) != 0)
+#define RELOC_USE_TLS_ID(t) ((reloc_target_flags[t] & _RF_I) != 0)
+#define RELOC_TARGET_SIZE(t) ((reloc_target_flags[t] >> 8) & 0xff)
+#define RELOC_VALUE_RIGHTSHIFT(t) (reloc_target_flags[t] & 0xff)
+
+static const long reloc_target_bitmask[] = {
+#define _BM(x) (~(-(1ULL << (x))))
+ 0, /* NONE */
+ _BM(8), _BM(16), _BM(32), /* 8, 16, 32 */
+ _BM(8), _BM(16), _BM(32), /* DISP8, DISP16, DISP32 */
+ _BM(30), _BM(22), /* WDISP30, WDISP22 */
+ _BM(22), _BM(22), /* HI22, 22 */
+ _BM(13), _BM(10), /* 13, LO10 */
+ _BM(10), _BM(13), _BM(22), /* GOT10, GOT13, GOT22 */
+ _BM(10), _BM(22), /* PC10, PC22 */
+ _BM(30), 0, /* WPLT30, COPY */
+ _BM(32), _BM(32), _BM(32), /* GLOB_DAT, JMP_SLOT, RELATIVE */
+ _BM(32), _BM(32), /* UA32, PLT32 */
+ _BM(22), _BM(10), /* HIPLT22, LOPLT10 */
+ _BM(32), _BM(22), _BM(10), /* PCPLT32, PCPLT22, PCPLT10 */
+ _BM(10), _BM(11), -1, /* 10, 11, 64 */
+ _BM(13), _BM(22), /* OLO10, HH22 */
+ _BM(10), _BM(22), /* HM10, LM22 */
+ _BM(22), _BM(10), _BM(22), /* PC_HH22, PC_HM10, PC_LM22 */
+ _BM(16), _BM(19), /* WDISP16, WDISP19 */
+ -1, /* GLOB_JMP */
+ _BM(7), _BM(5), _BM(6), /* 7, 5, 6 */
+ -1, -1, /* DISP64, PLT64 */
+ _BM(22), _BM(13), /* HIX22, LOX10 */
+ _BM(22), _BM(10), _BM(13), /* H44, M44, L44 */
+ -1, -1, _BM(16), /* REGISTER, UA64, UA16 */
+ _BM(22), _BM(10), 0, _BM(30), /* GD_HI22, GD_LO10, GD_ADD, GD_CALL */
+ _BM(22), _BM(10), 0, /* LDM_HI22, LDMO10, LDM_ADD */
+ _BM(30), /* LDM_CALL */
+ _BM(22), _BM(10), 0, /* LDO_HIX22, LDO_LOX10, LDO_ADD */
+ _BM(22), _BM(10), 0, 0, /* IE_HI22, IE_LO10, IE_LD, IE_LDX */
+ 0, /* IE_ADD */
+ _BM(22), _BM(13), /* LE_HIX22, LE_LOX10 */
+ _BM(32), -1, /* DTPMOD32, DTPMOD64 */
+ _BM(32), -1, /* DTPOFF32, DTPOFF64 */
+ _BM(32), -1 /* TPOFF32, TPOFF64 */
+#undef _BM
+};
+#define RELOC_VALUE_BITMASK(t) (reloc_target_bitmask[t])
+
+#undef flush
+#define flush(va, offs) \
+ __asm __volatile("flush %0 + %1" : : "r" (va), "I" (offs));
+
+static int reloc_nonplt_object(Obj_Entry *obj, const Elf_Rela *rela,
+ SymCache *cache, int flags, RtldLockState *lockstate);
+static void install_plt(Elf_Word *pltgot, Elf_Addr proc);
+
+extern char _rtld_bind_start_0[];
+extern char _rtld_bind_start_1[];
+
+int
+do_copy_relocations(Obj_Entry *dstobj)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ const Elf_Sym *dstsym;
+ const Elf_Sym *srcsym;
+ void *dstaddr;
+ const void *srcaddr;
+ const Obj_Entry *srcobj, *defobj;
+ SymLook req;
+ const char *name;
+ size_t size;
+ int res;
+
+ assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */
+
+ relalim = (const Elf_Rela *)((caddr_t)dstobj->rela + dstobj->relasize);
+ for (rela = dstobj->rela; rela < relalim; rela++) {
+ if (ELF_R_TYPE(rela->r_info) == R_SPARC_COPY) {
+ dstaddr = (void *)(dstobj->relocbase + rela->r_offset);
+ dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
+ name = dstobj->strtab + dstsym->st_name;
+ size = dstsym->st_size;
+ symlook_init(&req, name);
+ req.ventry = fetch_ventry(dstobj,
+ ELF_R_SYM(rela->r_info));
+ req.flags = SYMLOOK_EARLY;
+
+ for (srcobj = dstobj->next; srcobj != NULL;
+ srcobj = srcobj->next) {
+ res = symlook_obj(&req, srcobj);
+ if (res == 0) {
+ srcsym = req.sym_out;
+ defobj = req.defobj_out;
+ break;
+ }
+ }
+ if (srcobj == NULL) {
+ _rtld_error("Undefined symbol \"%s\""
+ "referenced from COPY relocation"
+ "in %s", name, dstobj->path);
+ return (-1);
+ }
+
+ srcaddr = (const void *)(defobj->relocbase +
+ srcsym->st_value);
+ memcpy(dstaddr, srcaddr, size);
+ }
+ }
+
+ return (0);
+}
+
+int
+reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,
+ RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ SymCache *cache;
+ int r = -1;
+
+ if ((flags & SYMLOOK_IFUNC) != 0)
+ /* XXX not implemented */
+ return (0);
+
+ /*
+ * The dynamic loader may be called from a thread, we have
+ * limited amounts of stack available so we cannot use alloca().
+ */
+ if (obj != obj_rtld) {
+ cache = calloc(obj->dynsymcount, sizeof(SymCache));
+ /* No need to check for NULL here */
+ } else
+ cache = NULL;
+
+ relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize);
+ for (rela = obj->rela; rela < relalim; rela++) {
+ if (reloc_nonplt_object(obj, rela, cache, flags, lockstate) < 0)
+ goto done;
+ }
+ r = 0;
+done:
+ if (cache != NULL)
+ free(cache);
+ return (r);
+}
+
+static int
+reloc_nonplt_object(Obj_Entry *obj, const Elf_Rela *rela, SymCache *cache,
+ int flags, RtldLockState *lockstate)
+{
+ const Obj_Entry *defobj;
+ const Elf_Sym *def;
+ Elf_Addr *where;
+ Elf_Word *where32;
+ Elf_Word type;
+ Elf_Addr value;
+ Elf_Addr mask;
+
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ where32 = (Elf_Word *)where;
+ defobj = NULL;
+ def = NULL;
+
+ type = ELF64_R_TYPE_ID(rela->r_info);
+ if (type == R_SPARC_NONE)
+ return (0);
+
+ /* We do JMP_SLOTs below. */
+ if (type == R_SPARC_JMP_SLOT)
+ return (0);
+
+ /* COPY relocs are also handled elsewhere. */
+ if (type == R_SPARC_COPY)
+ return (0);
+
+ /* Ignore ADD and CALL relocations for dynamic TLS references. */
+ if (type == R_SPARC_TLS_GD_ADD || type == R_SPARC_TLS_GD_CALL ||
+ type == R_SPARC_TLS_LDM_ADD || type == R_SPARC_TLS_LDM_CALL ||
+ type == R_SPARC_TLS_LDO_ADD)
+ return (0);
+
+ /*
+ * Note: R_SPARC_TLS_TPOFF64 must be the numerically largest
+ * relocation type.
+ */
+ if (type >= sizeof(reloc_target_bitmask) /
+ sizeof(*reloc_target_bitmask)) {
+ _rtld_error("%s: Unsupported relocation type %d in non-PLT "
+ "object\n", obj->path, type);
+ return (-1);
+ }
+
+ value = rela->r_addend;
+
+ /*
+ * Handle relative relocs here, because we might not be able to access
+ * globals yet.
+ */
+ if (type == R_SPARC_RELATIVE) {
+ /* XXXX -- apparently we ignore the preexisting value. */
+ *where = (Elf_Addr)(obj->relocbase + value);
+ return (0);
+ }
+
+ /*
+ * If we get here while relocating rtld itself, we will crash because
+ * a non-local variable is accessed.
+ */
+ if (RELOC_RESOLVE_SYMBOL(type)) {
+ /* Find the symbol. */
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ flags, cache, lockstate);
+ if (def == NULL)
+ return (-1);
+
+ if (RELOC_USE_TLS_ID(type))
+ value = (Elf_Addr)defobj->tlsindex;
+ else if (RELOC_USE_TLS_DOFF(type))
+ value += (Elf_Addr)def->st_value;
+ else if (RELOC_USE_TLS_OFF(type)) {
+ /*
+ * We lazily allocate offsets for static TLS as we
+ * see the first relocation that references the TLS
+ * block. This allows us to support (small amounts
+ * of) static TLS in dynamically loaded modules. If
+ * we run out of space, we generate an error.
+ */
+ if (!defobj->tls_done &&
+ !allocate_tls_offset((Obj_Entry*)defobj)) {
+ _rtld_error("%s: No space available for "
+ "static Thread Local Storage", obj->path);
+ return (-1);
+ }
+ value += (Elf_Addr)(def->st_value -
+ defobj->tlsoffset);
+ } else {
+ /* Add in the symbol's absolute address. */
+ value += (Elf_Addr)(def->st_value +
+ defobj->relocbase);
+ }
+ }
+
+ if (type == R_SPARC_OLO10)
+ value = (value & 0x3ff) + ELF64_R_TYPE_DATA(rela->r_info);
+
+ if (type == R_SPARC_HIX22 || type == R_SPARC_TLS_LE_HIX22)
+ value ^= 0xffffffffffffffff;
+
+ if (RELOC_PC_RELATIVE(type))
+ value -= (Elf_Addr)where;
+
+ if (RELOC_BASE_RELATIVE(type)) {
+ /*
+ * Note that even though sparcs use `Elf_rela' exclusively
+ * we still need the implicit memory addend in relocations
+ * referring to GOT entries. Undoubtedly, someone f*cked
+ * this up in the distant past, and now we're stuck with
+ * it in the name of compatibility for all eternity ...
+ *
+ * In any case, the implicit and explicit should be mutually
+ * exclusive. We provide a check for that here.
+ */
+ /* XXXX -- apparently we ignore the preexisting value */
+ value += (Elf_Addr)(obj->relocbase);
+ }
+
+ mask = RELOC_VALUE_BITMASK(type);
+ value >>= RELOC_VALUE_RIGHTSHIFT(type);
+ value &= mask;
+
+ if (type == R_SPARC_LOX10 || type == R_SPARC_TLS_LE_LOX10)
+ value |= 0x1c00;
+
+ if (RELOC_UNALIGNED(type)) {
+ /* Handle unaligned relocations. */
+ Elf_Addr tmp;
+ char *ptr;
+ int size;
+ int i;
+
+ size = RELOC_TARGET_SIZE(type) / 8;
+ ptr = (char *)where;
+ tmp = 0;
+
+ /* Read it in one byte at a time. */
+ for (i = 0; i < size; i++)
+ tmp = (tmp << 8) | ptr[i];
+
+ tmp &= ~mask;
+ tmp |= value;
+
+ /* Write it back out. */
+ for (i = 0; i < size; i++)
+ ptr[i] = ((tmp >> ((size - i - 1) * 8)) & 0xff);
+ } else if (RELOC_TARGET_SIZE(type) > 32) {
+ *where &= ~mask;
+ *where |= value;
+ } else {
+ *where32 &= ~mask;
+ *where32 |= value;
+ }
+
+ return (0);
+}
+
+int
+reloc_plt(Obj_Entry *obj)
+{
+#if 0
+ const Obj_Entry *defobj;
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ const Elf_Sym *def;
+ Elf_Addr *where;
+ Elf_Addr value;
+
+ relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ if (rela->r_addend == 0)
+ continue;
+ assert(ELF64_R_TYPE_ID(rela->r_info) == R_SPARC_JMP_SLOT);
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ true, NULL, lockstate);
+ value = (Elf_Addr)(defobj->relocbase + def->st_value);
+ *where = value;
+ }
+#endif
+ return (0);
+}
+
+/*
+ * Instruction templates:
+ */
+#define BAA 0x10400000 /* ba,a %xcc, 0 */
+#define SETHI 0x03000000 /* sethi %hi(0), %g1 */
+#define JMP 0x81c06000 /* jmpl %g1+%lo(0), %g0 */
+#define NOP 0x01000000 /* sethi %hi(0), %g0 */
+#define OR 0x82806000 /* or %g1, 0, %g1 */
+#define XOR 0x82c06000 /* xor %g1, 0, %g1 */
+#define MOV71 0x8283a000 /* or %o7, 0, %g1 */
+#define MOV17 0x9c806000 /* or %g1, 0, %o7 */
+#define CALL 0x40000000 /* call 0 */
+#define SLLX 0x8b407000 /* sllx %g1, 0, %g1 */
+#define SETHIG5 0x0b000000 /* sethi %hi(0), %g5 */
+#define ORG5 0x82804005 /* or %g1, %g5, %g1 */
+
+/* %hi(v) with variable shift */
+#define HIVAL(v, s) (((v) >> (s)) & 0x003fffff)
+#define LOVAL(v) ((v) & 0x000003ff)
+
+int
+reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+ const Obj_Entry *defobj;
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+ const Elf_Sym *def;
+ Elf_Addr *where;
+ Elf_Addr target;
+
+ relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ assert(ELF64_R_TYPE_ID(rela->r_info) == R_SPARC_JMP_SLOT);
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
+ SYMLOOK_IN_PLT | flags, NULL, lockstate);
+ if (def == NULL)
+ return -1;
+ target = (Elf_Addr)(defobj->relocbase + def->st_value);
+ reloc_jmpslot(where, target, defobj, obj, (Elf_Rel *)rela);
+ }
+ obj->jmpslots_done = true;
+ return (0);
+}
+
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, int flags,
+ struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+Elf_Addr
+reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *obj,
+ const Obj_Entry *refobj, const Elf_Rel *rel)
+{
+ const Elf_Rela *rela = (const Elf_Rela *)rel;
+ Elf_Addr offset;
+ Elf_Word *where;
+
+ if (rela - refobj->pltrela < 32764) {
+ /*
+ * At the PLT entry pointed at by `where', we now construct
+ * a direct transfer to the now fully resolved function
+ * address.
+ *
+ * A PLT entry is supposed to start by looking like this:
+ *
+ * sethi (. - .PLT0), %g1
+ * ba,a %xcc, .PLT1
+ * nop
+ * nop
+ * nop
+ * nop
+ * nop
+ * nop
+ *
+ * When we replace these entries we start from the second
+ * entry and do it in reverse order so the last thing we
+ * do is replace the branch. That allows us to change this
+ * atomically.
+ *
+ * We now need to find out how far we need to jump. We
+ * have a choice of several different relocation techniques
+ * which are increasingly expensive.
+ */
+ where = (Elf_Word *)wherep;
+ offset = ((Elf_Addr)where) - target;
+ if (offset <= (1L<<20) && offset >= -(1L<<20)) {
+ /*
+ * We're within 1MB -- we can use a direct branch
+ * instruction.
+ *
+ * We can generate this pattern:
+ *
+ * sethi %hi(. - .PLT0), %g1
+ * ba,a %xcc, addr
+ * nop
+ * nop
+ * nop
+ * nop
+ * nop
+ * nop
+ *
+ */
+ where[1] = BAA | ((offset >> 2) &0x3fffff);
+ flush(where, 4);
+ } else if (target >= 0 && target < (1L<<32)) {
+ /*
+ * We're within 32-bits of address zero.
+ *
+ * The resulting code in the jump slot is:
+ *
+ * sethi %hi(. - .PLT0), %g1
+ * sethi %hi(addr), %g1
+ * jmp %g1+%lo(addr)
+ * nop
+ * nop
+ * nop
+ * nop
+ * nop
+ *
+ */
+ where[2] = JMP | LOVAL(target);
+ flush(where, 8);
+ where[1] = SETHI | HIVAL(target, 10);
+ flush(where, 4);
+ } else if (target <= 0 && target > -(1L<<32)) {
+ /*
+ * We're within 32-bits of address -1.
+ *
+ * The resulting code in the jump slot is:
+ *
+ * sethi %hi(. - .PLT0), %g1
+ * sethi %hix(addr), %g1
+ * xor %g1, %lox(addr), %g1
+ * jmp %g1
+ * nop
+ * nop
+ * nop
+ * nop
+ *
+ */
+ where[3] = JMP;
+ flush(where, 12);
+ where[2] = XOR | ((~target) & 0x00001fff);
+ flush(where, 8);
+ where[1] = SETHI | HIVAL(~target, 10);
+ flush(where, 4);
+ } else if (offset <= (1L<<32) && offset >= -((1L<<32) - 4)) {
+ /*
+ * We're within 32-bits -- we can use a direct call
+ * insn
+ *
+ * The resulting code in the jump slot is:
+ *
+ * sethi %hi(. - .PLT0), %g1
+ * mov %o7, %g1
+ * call (.+offset)
+ * mov %g1, %o7
+ * nop
+ * nop
+ * nop
+ * nop
+ *
+ */
+ where[3] = MOV17;
+ flush(where, 12);
+ where[2] = CALL | ((offset >> 4) & 0x3fffffff);
+ flush(where, 8);
+ where[1] = MOV71;
+ flush(where, 4);
+ } else if (offset >= 0 && offset < (1L<<44)) {
+ /*
+ * We're within 44 bits. We can generate this
+ * pattern:
+ *
+ * The resulting code in the jump slot is:
+ *
+ * sethi %hi(. - .PLT0), %g1
+ * sethi %h44(addr), %g1
+ * or %g1, %m44(addr), %g1
+ * sllx %g1, 12, %g1
+ * jmp %g1+%l44(addr)
+ * nop
+ * nop
+ * nop
+ *
+ */
+ where[4] = JMP | LOVAL(offset);
+ flush(where, 16);
+ where[3] = SLLX | 12;
+ flush(where, 12);
+ where[2] = OR | (((offset) >> 12) & 0x00001fff);
+ flush(where, 8);
+ where[1] = SETHI | HIVAL(offset, 22);
+ flush(where, 4);
+ } else if (offset < 0 && offset > -(1L<<44)) {
+ /*
+ * We're within 44 bits. We can generate this
+ * pattern:
+ *
+ * The resulting code in the jump slot is:
+ *
+ * sethi %hi(. - .PLT0), %g1
+ * sethi %h44(-addr), %g1
+ * xor %g1, %m44(-addr), %g1
+ * sllx %g1, 12, %g1
+ * jmp %g1+%l44(addr)
+ * nop
+ * nop
+ * nop
+ *
+ */
+ where[4] = JMP | LOVAL(offset);
+ flush(where, 16);
+ where[3] = SLLX | 12;
+ flush(where, 12);
+ where[2] = XOR | (((~offset) >> 12) & 0x00001fff);
+ flush(where, 8);
+ where[1] = SETHI | HIVAL(~offset, 22);
+ flush(where, 4);
+ } else {
+ /*
+ * We need to load all 64-bits
+ *
+ * The resulting code in the jump slot is:
+ *
+ * sethi %hi(. - .PLT0), %g1
+ * sethi %hh(addr), %g1
+ * sethi %lm(addr), %g5
+ * or %g1, %hm(addr), %g1
+ * sllx %g1, 32, %g1
+ * or %g1, %g5, %g1
+ * jmp %g1+%lo(addr)
+ * nop
+ *
+ */
+ where[6] = JMP | LOVAL(target);
+ flush(where, 24);
+ where[5] = ORG5;
+ flush(where, 20);
+ where[4] = SLLX | 32;
+ flush(where, 16);
+ where[3] = OR | LOVAL((target) >> 32);
+ flush(where, 12);
+ where[2] = SETHIG5 | HIVAL(target, 10);
+ flush(where, 8);
+ where[1] = SETHI | HIVAL(target, 42);
+ flush(where, 4);
+ }
+ } else {
+ /*
+ * This is a high PLT slot; the relocation offset specifies a
+ * pointer that needs to be frobbed; no actual code needs to
+ * be modified. The pointer to be calculated needs the addend
+ * added and the reference object relocation base subtraced.
+ */
+ *wherep = target + rela->r_addend -
+ (Elf_Addr)refobj->relocbase;
+ }
+
+ return (target);
+}
+
+/*
+ * Install rtld function call into this PLT slot.
+ */
+#define SAVE 0x9de3bf50
+#define SETHI_l0 0x21000000
+#define SETHI_l1 0x23000000
+#define OR_l0_l0 0xa0142000
+#define SLLX_l0_32_l0 0xa12c3020
+#define OR_l0_l1_l0 0xa0140011
+#define JMPL_l0_o1 0x93c42000
+#define MOV_g1_o0 0x90100001
+
+void
+init_pltgot(Obj_Entry *obj)
+{
+ Elf_Word *entry;
+
+ if (obj->pltgot != NULL) {
+ entry = (Elf_Word *)obj->pltgot;
+ install_plt(&entry[0], (Elf_Addr)_rtld_bind_start_0);
+ install_plt(&entry[8], (Elf_Addr)_rtld_bind_start_1);
+ obj->pltgot[8] = (Elf_Addr)obj;
+ }
+}
+
+static void
+install_plt(Elf_Word *pltgot, Elf_Addr proc)
+{
+ pltgot[0] = SAVE;
+ flush(pltgot, 0);
+ pltgot[1] = SETHI_l0 | HIVAL(proc, 42);
+ flush(pltgot, 4);
+ pltgot[2] = SETHI_l1 | HIVAL(proc, 10);
+ flush(pltgot, 8);
+ pltgot[3] = OR_l0_l0 | LOVAL((proc) >> 32);
+ flush(pltgot, 12);
+ pltgot[4] = SLLX_l0_32_l0;
+ flush(pltgot, 16);
+ pltgot[5] = OR_l0_l1_l0;
+ flush(pltgot, 20);
+ pltgot[6] = JMPL_l0_o1 | LOVAL(proc);
+ flush(pltgot, 24);
+ pltgot[7] = MOV_g1_o0;
+ flush(pltgot, 28);
+}
+
+void
+allocate_initial_tls(Obj_Entry *objs)
+{
+ Elf_Addr* tpval;
+
+ /*
+ * Fix the size of the static TLS block by using the maximum offset
+ * allocated so far and adding a bit for dynamic modules to use.
+ */
+ tls_static_space = tls_last_offset + RTLD_STATIC_TLS_EXTRA;
+ tpval = allocate_tls(objs, NULL, 3 * sizeof(Elf_Addr),
+ sizeof(Elf_Addr));
+ __asm __volatile("mov %0, %%g7" : : "r" (tpval));
+}
+
+void *__tls_get_addr(tls_index *ti)
+{
+ register Elf_Addr** tp __asm__("%g7");
+
+ return (tls_get_addr_common(tp, ti->ti_module, ti->ti_offset));
+}
diff --git a/libexec/rtld-elf/sparc64/rtld_machdep.h b/libexec/rtld-elf/sparc64/rtld_machdep.h
new file mode 100644
index 0000000..44fe2cf
--- /dev/null
+++ b/libexec/rtld-elf/sparc64/rtld_machdep.h
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 1999, 2000 John D. Polstra.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef RTLD_MACHDEP_H
+#define RTLD_MACHDEP_H 1
+
+#include <sys/types.h>
+#include <machine/atomic.h>
+
+struct Struct_Obj_Entry;
+
+/* Return the address of the .dynamic section in the dynamic linker. */
+Elf_Dyn *rtld_dynamic_addr(void);
+#define rtld_dynamic(obj) rtld_dynamic_addr()
+#define RTLD_IS_DYNAMIC() (rtld_dynamic_addr() != NULL)
+
+Elf_Addr reloc_jmpslot(Elf_Addr *, Elf_Addr,
+ const struct Struct_Obj_Entry *,
+ const struct Struct_Obj_Entry *,
+ const Elf_Rel *);
+
+#define make_function_pointer(def, defobj) \
+ ((defobj)->relocbase + (def)->st_value)
+
+#define call_initfini_pointer(obj, target) \
+ (((InitFunc)(target))())
+
+#define call_init_pointer(obj, target) \
+ (((InitArrFunc)(target))(main_argc, main_argv, environ))
+
+#define round(size, align) \
+ (((size) + (align) - 1) & ~((align) - 1))
+#define calculate_first_tls_offset(size, align) \
+ round(size, align)
+#define calculate_tls_offset(prev_offset, prev_size, size, align) \
+ round((prev_offset) + (size), align)
+#define calculate_tls_end(off, size) ((off) + (size))
+
+typedef struct {
+ unsigned long ti_module;
+ unsigned long ti_offset;
+} tls_index;
+
+extern void *__tls_get_addr(tls_index *ti);
+
+#define RTLD_DEFAULT_STACK_PF_EXEC 0
+#define RTLD_DEFAULT_STACK_EXEC 0
+
+#endif
diff --git a/libexec/rtld-elf/sparc64/rtld_start.S b/libexec/rtld-elf/sparc64/rtld_start.S
new file mode 100644
index 0000000..58bdd1b
--- /dev/null
+++ b/libexec/rtld-elf/sparc64/rtld_start.S
@@ -0,0 +1,170 @@
+/* $NetBSD: rtld_start.S,v 1.5 2001/08/14 22:17:48 eeh Exp $ */
+
+/*-
+ * Copyright (c) 2001 Jake Burkholder.
+ * Copyright (c) 2000 Eduardo Horvath.
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas and Paul Kranenburg.
+ *
+ * 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 NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/asm.h>
+
+/*
+ * ELF:
+ * On startup the stack should contain 16 extended word register save
+ * area, followed by the arg count, etc.
+ */
+
+ENTRY(.rtld_start)
+ clr %fp
+ mov %o0, %l0
+ mov %o3, %l1
+
+ sub %sp, 16, %sp
+ add %sp, SPOFF + CCFSZ + 0x0, %o1
+ call _rtld
+ add %sp, SPOFF + CCFSZ + 0x8, %o2
+
+ ldx [%sp + SPOFF + CCFSZ + 0x0], %o1
+ ldx [%sp + SPOFF + CCFSZ + 0x8], %o2
+ add %sp, 16, %sp
+
+ mov %l1, %o3
+ jmp %o0
+ mov %l0, %o0
+END(.rtld_start)
+
+/*
+ * Find the address of _DYNAMIC by disassembling a call instruction to it.
+ * Binutils may not fill in the GOT as expected on other architectures.
+ */
+.weak _DYNAMIC
+
+ENTRY(rtld_dynamic_addr)
+ save %sp, -CCFSZ, %sp
+ call 1f
+ nop
+ call _DYNAMIC + 8
+1: lduw [%o7 + 8], %o0
+ sll %o0, 2, %o0
+ sra %o0, 0, %o0
+ ret
+ restore %o0, %o7, %o0
+END(rtld_dynamic_addr)
+
+ /*
+ * We have two separate entry points to the runtime linker.
+ * I'm implementing this following the SPARC v9 ABI spec.
+ *
+ * _rtld_bind_start_0(x, y) is called from .PLT0, and is used for
+ * PLT entries above 32768.
+ *
+ * _rtld_bind_start_1(x, y) is called from .PLT1, and is used for
+ * PLT entries below 32768.
+ *
+ * The first two entries of PLT2 contain the xword object pointer.
+ *
+ * These routines are called with two longword arguments,
+ * x and y. To calculate the address of the entry,
+ * _rtld_bind_start_1(x, y) does:
+ *
+ * n = x >> 15;
+ *
+ * and _rtld_bind_start_0(x, y) should do, according to the SCD:
+ *
+ * i = x - y - 1048596;
+ * n = 32768 + (i/5120)*160 + (i%5120)/24;
+ *
+ * Note that the number of 1048596 from above is incorrect; rather,
+ * we need to use HIPLTOFFS as defined below.
+ *
+ * Neither routine needs to issue a save since it's already been
+ * done in the PLT entry.
+ */
+
+#define NPLTLOSLOTS 32768
+#define PLTSLOTSZ 32
+/*
+ * - 16 to compensate for the difference of the positions of the jumps that
+ * generate the arguments in .PLT0 and the high plt entry.
+ */
+#define HIPLTOFFS (NPLTLOSLOTS * PLTSLOTSZ - 16)
+
+ENTRY(_rtld_bind_start_0)
+ sethi %hi(HIPLTOFFS), %l1
+ or %l1, %lo(HIPLTOFFS), %l1
+ sub %o0, %o1, %l0 /* x - y */
+ sub %l0, %l1, %l0 /* i = x - y - HIPLTOFFS */
+ sethi %hi(5120), %l7
+ sdivx %l0, %l7, %l1 /* Calculate i / 5120 */
+ mulx %l1, %l7, %l3
+ sub %l0, %l3, %l2 /* And i % 5120 */
+ mulx %l1, 160, %l5 /* (i / 5120) * 160 */
+ sdivx %l2, 24, %l4 /* (i % 5120) / 24 */
+ sethi %hi(NPLTLOSLOTS), %l6
+ add %l4, %l5, %l4 /* (i / 5120) * 160 + (i % 5120) / 24 */
+ add %l4, %l6, %l4 /* + NPLTLOSLOTS */
+ sub %l4, 4, %l4 /* XXX: 4 entries are reserved */
+
+ sllx %l4, 1, %l5 /* Each element is an Elf_Rela which */
+ add %l5, %l4, %l4 /* is 3 longwords or 24 bytes. */
+ sllx %l4, 3, %l4 /* So multiply by 24. */
+
+ ldx [%o1 + (10*4)], %o0 /* Load object pointer from PLT2 */
+
+ call _rtld_bind /* Call _rtld_bind(obj, offset) */
+ mov %l4, %o1
+
+ jmp %o0 /* return value == function address */
+ restore /* Dump our stack frame */
+END(_rtld_bind_start_0)
+
+ENTRY(_rtld_bind_start_1)
+ srax %o0, 15, %o2 /* %o0 is the index to our PLT slot */
+ sub %o2, 4, %o2 /* XXX: 4 entries are reserved */
+
+ sllx %o2, 1, %o3 /* Each element is an Elf_Rela which */
+ add %o3, %o2, %o2 /* is 3 longwords or 24 bytes. */
+ sllx %o2, 3, %o2 /* So multiply by 24. */
+
+ ldx [%o1 + 8], %o0 /* The object pointer is at [%o1 + 8] */
+
+ call _rtld_bind /* Call _rtld_bind(obj, offset) */
+ mov %o2, %o1
+
+ jmp %o0 /* return value == function address */
+ restore /* Dump our stack frame */
+END(_rtld_bind_start_1)
diff --git a/libexec/rtld-elf/tests/Makefile b/libexec/rtld-elf/tests/Makefile
new file mode 100644
index 0000000..f384218
--- /dev/null
+++ b/libexec/rtld-elf/tests/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/libexec/rtld-elf
+SUBDIR+= libpythagoras target
+
+ATF_TESTS_C= ld_library_pathfds
+
+.include <bsd.test.mk>
diff --git a/libexec/rtld-elf/tests/ld_library_pathfds.c b/libexec/rtld-elf/tests/ld_library_pathfds.c
new file mode 100644
index 0000000..1100894
--- /dev/null
+++ b/libexec/rtld-elf/tests/ld_library_pathfds.c
@@ -0,0 +1,221 @@
+/*-
+ * Copyright 2014 Jonathan Anderson.
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+
+struct descriptors {
+ int binary;
+ int testdir;
+ int root;
+ int etc;
+ int usr;
+};
+
+static void setup(struct descriptors *, const atf_tc_t *);
+static void expect_success(int binary, char *pathfds);
+static void expect_missing_library(int binary, char *pathfds);
+
+static void try_to_run(int binary, int expected_exit_status,
+ char * const *env, const char *expected_out, const char *expected_err);
+static int opendir(const char *name);
+static int opendirat(int parent, const char *name);
+
+
+ATF_TC_WITHOUT_HEAD(missing_library);
+ATF_TC_BODY(missing_library, tc)
+{
+ struct descriptors files;
+
+ setup(&files, tc);
+ expect_missing_library(files.binary, NULL);
+}
+
+
+ATF_TC_WITHOUT_HEAD(wrong_library_directories);
+ATF_TC_BODY(wrong_library_directories, tc)
+{
+ struct descriptors files;
+ char *pathfds;
+
+ setup(&files, tc);
+ ATF_REQUIRE(
+ asprintf(&pathfds, "LD_LIBRARY_PATH_FDS=%d", files.etc) > 0);
+
+ expect_missing_library(files.binary, pathfds);
+}
+
+
+ATF_TC_WITHOUT_HEAD(bad_library_directories);
+ATF_TC_BODY(bad_library_directories, tc)
+{
+ struct descriptors files;
+ char *pathfds;
+
+ setup(&files, tc);
+ ATF_REQUIRE(asprintf(&pathfds, "LD_LIBRARY_PATH_FDS=::") > 0);
+
+ expect_missing_library(files.binary, pathfds);
+}
+
+
+ATF_TC_WITHOUT_HEAD(single_library_directory);
+ATF_TC_BODY(single_library_directory, tc)
+{
+ struct descriptors files;
+ char *pathfds;
+
+ setup(&files, tc);
+ ATF_REQUIRE(
+ asprintf(&pathfds, "LD_LIBRARY_PATH_FDS=%d", files.testdir) > 0);
+
+ expect_success(files.binary, pathfds);
+}
+
+
+ATF_TC_WITHOUT_HEAD(first_library_directory);
+ATF_TC_BODY(first_library_directory, tc)
+{
+ struct descriptors files;
+ char *pathfds;
+
+ setup(&files, tc);
+ ATF_REQUIRE(
+ asprintf(&pathfds, "LD_LIBRARY_PATH_FDS=%d:%d",
+ files.testdir, files.etc) > 0);
+
+ expect_success(files.binary, pathfds);
+}
+
+
+ATF_TC_WITHOUT_HEAD(middle_library_directory);
+ATF_TC_BODY(middle_library_directory, tc)
+{
+ struct descriptors files;
+ char *pathfds;
+
+ setup(&files, tc);
+ ATF_REQUIRE(
+ asprintf(&pathfds, "LD_LIBRARY_PATH_FDS=%d:%d:%d",
+ files.root, files.testdir, files.usr) > 0);
+
+ expect_success(files.binary, pathfds);
+}
+
+
+ATF_TC_WITHOUT_HEAD(last_library_directory);
+ATF_TC_BODY(last_library_directory, tc)
+{
+ struct descriptors files;
+ char *pathfds;
+
+ setup(&files, tc);
+ ATF_REQUIRE(
+ asprintf(&pathfds, "LD_LIBRARY_PATH_FDS=%d:%d",
+ files.root, files.testdir) > 0);
+
+ expect_success(files.binary, pathfds);
+}
+
+
+
+/* Register test cases with ATF. */
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, missing_library);
+ ATF_TP_ADD_TC(tp, wrong_library_directories);
+ ATF_TP_ADD_TC(tp, bad_library_directories);
+ ATF_TP_ADD_TC(tp, single_library_directory);
+ ATF_TP_ADD_TC(tp, first_library_directory);
+ ATF_TP_ADD_TC(tp, middle_library_directory);
+ ATF_TP_ADD_TC(tp, last_library_directory);
+
+ return atf_no_error();
+}
+
+
+static void
+setup(struct descriptors *dp, const atf_tc_t *tc)
+{
+
+ dp->testdir = opendir(atf_tc_get_config_var(tc, "srcdir"));
+ ATF_REQUIRE(dp->testdir >= 0);
+ ATF_REQUIRE(
+ (dp->binary = openat(dp->testdir, "target", O_RDONLY)) >= 0);
+
+ ATF_REQUIRE((dp->root = opendir("/")) >= 0);
+ ATF_REQUIRE((dp->etc = opendirat(dp->root, "etc")) >= 0);
+ ATF_REQUIRE((dp->usr = opendirat(dp->root, "usr")) >= 0);
+}
+
+static void
+expect_success(int binary, char *pathfds)
+{
+ char * const env[] = { pathfds, NULL };
+ try_to_run(binary, 0, env, "the hypotenuse of 3 and 4 is 5\n", "");
+}
+
+static void
+expect_missing_library(int binary, char *pathfds)
+{
+ char * const env[] = { pathfds, NULL };
+ try_to_run(binary, 1, env, "",
+ "Shared object \"libpythagoras.so.0\" not found,"
+ " required by \"target\"\n");
+}
+
+
+static void
+try_to_run(int binary, int exit_status, char * const *env,
+ const char *expected_out, const char *expected_err)
+{
+ pid_t child = atf_utils_fork();
+
+ if (child == 0) {
+ char * const args[] = { "target", NULL };
+
+ fexecve(binary, args, env);
+ atf_tc_fail("fexecve() failed");
+ }
+
+ atf_utils_wait(child, exit_status, expected_out, expected_err);
+}
+
+
+static int
+opendir(const char *name)
+{
+ return open(name, O_RDONLY | O_DIRECTORY);
+}
+
+static int
+opendirat(int parent, const char *name)
+{
+ return openat(parent, name, O_RDONLY | O_DIRECTORY);
+}
diff --git a/libexec/rtld-elf/tests/libpythagoras/Makefile b/libexec/rtld-elf/tests/libpythagoras/Makefile
new file mode 100644
index 0000000..618612f
--- /dev/null
+++ b/libexec/rtld-elf/tests/libpythagoras/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+LIB= pythagoras
+SHLIB_MAJOR= 0
+
+LIBDIR= ${TESTSBASE}/libexec/rtld-elf
+SHLIBDIR= ${TESTSBASE}/libexec/rtld-elf
+
+SRCS= pythagoras.c
+
+DPADD= ${LIBM}
+LDADD= -lm
+
+.include <bsd.lib.mk>
diff --git a/libexec/rtld-elf/tests/libpythagoras/pythagoras.c b/libexec/rtld-elf/tests/libpythagoras/pythagoras.c
new file mode 100644
index 0000000..c7fa604
--- /dev/null
+++ b/libexec/rtld-elf/tests/libpythagoras/pythagoras.c
@@ -0,0 +1,42 @@
+/*-
+ * Copyright 2014 Jonathan Anderson.
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <errno.h>
+#include <math.h>
+
+#include "pythagoras.h"
+
+double
+pythagorean_theorem(double a, double b)
+{
+
+ if (a <= 0 || b <= 0) {
+ errno = ERANGE;
+ return (-1.0);
+ }
+ return (sqrt(pow(a, 2) + pow(b, 2)));
+}
diff --git a/libexec/rtld-elf/tests/libpythagoras/pythagoras.h b/libexec/rtld-elf/tests/libpythagoras/pythagoras.h
new file mode 100644
index 0000000..ef5379c
--- /dev/null
+++ b/libexec/rtld-elf/tests/libpythagoras/pythagoras.h
@@ -0,0 +1,28 @@
+/*-
+ * Copyright 2014 Jonathan Anderson.
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+double pythagorean_theorem(double, double);
diff --git a/libexec/rtld-elf/tests/target/Makefile b/libexec/rtld-elf/tests/target/Makefile
new file mode 100644
index 0000000..d5305f9
--- /dev/null
+++ b/libexec/rtld-elf/tests/target/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+PROG= target
+BINDIR= ${TESTSBASE}/libexec/rtld-elf
+
+CFLAGS+= -I${.CURDIR}/../libpythagoras
+
+LDFLAGS+= -L${.OBJDIR}/../libpythagoras
+DPADD+= ${.OBJDIR}/../libpythagoras/libpythagoras.a
+LDADD= -lpythagoras
+
+MAN=
+
+.include <bsd.prog.mk>
diff --git a/libexec/rtld-elf/tests/target/target.c b/libexec/rtld-elf/tests/target/target.c
new file mode 100644
index 0000000..e88b899
--- /dev/null
+++ b/libexec/rtld-elf/tests/target/target.c
@@ -0,0 +1,39 @@
+/*-
+ * Copyright 2014 Jonathan Anderson.
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include "pythagoras.h"
+
+#include <stdio.h>
+
+int
+main(int argc, char *argv[])
+{
+ float hypotenuse = pythagorean_theorem(3, 4);
+ printf("the hypotenuse of 3 and 4 is %d\n", (int) hypotenuse);
+
+ return 0;
+}
diff --git a/libexec/rtld-elf/xmalloc.c b/libexec/rtld-elf/xmalloc.c
new file mode 100644
index 0000000..ed195df
--- /dev/null
+++ b/libexec/rtld-elf/xmalloc.c
@@ -0,0 +1,97 @@
+/*-
+ * Copyright 1996-1998 John D. Polstra.
+ * 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "rtld.h"
+#include "rtld_printf.h"
+
+void *
+xcalloc(size_t number, size_t size)
+{
+ void *p;
+
+ p = calloc(number, size);
+ if (p == NULL) {
+ rtld_fdputstr(STDERR_FILENO, "Out of memory\n");
+ _exit(1);
+ }
+ return (p);
+}
+
+void *
+xmalloc(size_t size)
+{
+ void *p = malloc(size);
+ if (p == NULL) {
+ rtld_fdputstr(STDERR_FILENO, "Out of memory\n");
+ _exit(1);
+ }
+ return p;
+}
+
+char *
+xstrdup(const char *str)
+{
+ char *copy;
+ size_t len;
+
+ len = strlen(str) + 1;
+ copy = xmalloc(len);
+ memcpy(copy, str, len);
+ return (copy);
+}
+
+void *
+malloc_aligned(size_t size, size_t align)
+{
+ void *mem, *res;
+
+ if (align < sizeof(void *))
+ align = sizeof(void *);
+
+ mem = xmalloc(size + sizeof(void *) + align - 1);
+ res = (void *)round((uintptr_t)mem + sizeof(void *), align);
+ *(void **)((uintptr_t)res - sizeof(void *)) = mem;
+ return (res);
+}
+
+void
+free_aligned(void *ptr)
+{
+ void *mem;
+ uintptr_t x;
+
+ if (ptr == NULL)
+ return;
+ x = (uintptr_t)ptr;
+ x -= sizeof(void *);
+ mem = *(void **)x;
+ free(mem);
+}
diff --git a/libexec/save-entropy/Makefile b/libexec/save-entropy/Makefile
new file mode 100644
index 0000000..3e27c48
--- /dev/null
+++ b/libexec/save-entropy/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+SCRIPTS= save-entropy.sh
+NO_OBJ=
+
+.include <bsd.prog.mk>
diff --git a/libexec/save-entropy/save-entropy.sh b/libexec/save-entropy/save-entropy.sh
new file mode 100755
index 0000000..06319d5
--- /dev/null
+++ b/libexec/save-entropy/save-entropy.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+#
+# Copyright (c) 2001-2006,2012 Douglas Barton, dougb@FreeBSD.org
+# 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.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+#
+# $FreeBSD$
+
+# This script is called by cron to store bits of randomness which are
+# then used to seed /dev/random on boot.
+
+# Originally developed by Doug Barton, dougb@FreeBSD.org
+
+PATH=/bin:/usr/bin
+
+# If there is a global system configuration file, suck it in.
+#
+if [ -r /etc/defaults/rc.conf ]; then
+ . /etc/defaults/rc.conf
+ source_rc_confs 2>/dev/null
+elif [ -r /etc/rc.conf ]; then
+ . /etc/rc.conf 2>/dev/null
+fi
+
+[ $(/sbin/sysctl -n security.jail.jailed) = 0 ] || exit 0
+
+case ${entropy_dir} in
+[Nn][Oo])
+ exit 0
+ ;;
+*)
+ entropy_dir=${entropy_dir:-/var/db/entropy}
+ ;;
+esac
+
+entropy_save_sz=${entropy_save_sz:-4096}
+entropy_save_num=${entropy_save_num:-8}
+
+if [ ! -d "${entropy_dir}" ]; then
+ install -d -o operator -g operator -m 0700 "${entropy_dir}" || {
+ logger -is -t "$0" The entropy directory "${entropy_dir}" does \
+ not exist, and cannot be created. Therefore no entropy can \
+ be saved.; exit 1; }
+fi
+
+cd "${entropy_dir}" || {
+ logger -is -t "$0" Cannot cd to the entropy directory: "${entropy_dir}". \
+ Entropy file rotation is aborted.; exit 1; }
+
+for f in saved-entropy.*; do
+ case "${f}" in saved-entropy.\*) continue ;; esac # No files match
+ [ ${f#saved-entropy\.} -ge ${entropy_save_num} ] && unlink ${f}
+done
+
+umask 377
+
+n=$(( ${entropy_save_num} - 1 ))
+while [ ${n} -ge 1 ]; do
+ if [ -f "saved-entropy.${n}" ]; then
+ mv "saved-entropy.${n}" "saved-entropy.$(( ${n} + 1 ))"
+ elif [ -e "saved-entropy.${n}" -o -L "saved-entropy.${n}" ]; then
+ logger -is -t "$0" \
+ "${entropy_dir}/saved-entropy.${n}" is not a regular file, and so \
+ it will not be rotated. Entropy file rotation is aborted.
+ exit 1
+ fi
+ n=$(( ${n} - 1 ))
+done
+
+dd if=/dev/random of=saved-entropy.1 bs=${entropy_save_sz} count=1 2>/dev/null
+
+exit 0
diff --git a/libexec/smrsh/Makefile b/libexec/smrsh/Makefile
new file mode 100644
index 0000000..ae86155
--- /dev/null
+++ b/libexec/smrsh/Makefile
@@ -0,0 +1,32 @@
+# @(#)Makefile 8.1 (Berkeley) 7/2/95
+# $FreeBSD$
+
+SENDMAIL_DIR=${.CURDIR}/../../contrib/sendmail
+.PATH: ${SENDMAIL_DIR}/smrsh
+
+PROG= smrsh
+SRCS= smrsh.c
+MAN= smrsh.8
+CFLAGS+=-I${SENDMAIL_DIR}/src -I${SENDMAIL_DIR}/include -I.
+
+LIBSMDIR= ${.OBJDIR}/../../lib/libsm
+LIBSM= ${LIBSMDIR}/libsm.a
+
+DPADD= ${LIBSM}
+LDADD= ${LIBSM}
+
+WARNS?= 2
+
+SRCS+= sm_os.h
+CLEANFILES+=sm_os.h
+
+# User customizations to the sendmail build environment
+CFLAGS+=${SENDMAIL_CFLAGS}
+DPADD+=${SENDMAIL_DPADD}
+LDADD+=${SENDMAIL_LDADD}
+LDFLAGS+=${SENDMAIL_LDFLAGS}
+
+sm_os.h:
+ ln -sf ${SENDMAIL_DIR}/include/sm/os/sm_os_freebsd.h sm_os.h
+
+.include <bsd.prog.mk>
diff --git a/libexec/talkd/Makefile b/libexec/talkd/Makefile
new file mode 100644
index 0000000..cc0c597
--- /dev/null
+++ b/libexec/talkd/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+# $FreeBSD$
+
+PROG= ntalkd
+SRCS= talkd.c announce.c process.c table.c print.c ttymsg.c
+.PATH: ${.CURDIR}/../../usr.bin/wall
+MAN= talkd.8
+CFLAGS+=-I${.CURDIR}/../../usr.bin/wall
+
+.include <bsd.prog.mk>
diff --git a/libexec/talkd/announce.c b/libexec/talkd/announce.c
new file mode 100644
index 0000000..00c01ac
--- /dev/null
+++ b/libexec/talkd/announce.c
@@ -0,0 +1,165 @@
+/*
+ * 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. 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
+#if 0
+static char sccsid[] = "@(#)announce.c 8.3 (Berkeley) 4/28/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#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 <errno.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <vis.h>
+
+#include "ttymsg.h"
+#include "extern.h"
+
+/*
+ * Announce an invitation to talk.
+ */
+
+/*
+ * See if the user is accepting messages. If so, announce that
+ * a talk is requested.
+ */
+int
+announce(CTL_MSG *request, const char *remote_machine)
+{
+ char full_tty[32];
+ 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, request, remote_machine));
+}
+
+#define max(a,b) ( (a) > (b) ? (a) : (b) )
+#define N_LINES 5
+#define N_CHARS 256
+
+/*
+ * 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 vi at the time
+ */
+int
+print_mesg(const char *tty, CTL_MSG *request,
+ const char *remote_machine)
+{
+ struct timeval now;
+ time_t clock_sec;
+ 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, *vis_user;
+ int i, j, max_size;
+
+ i = 0;
+ max_size = 0;
+ gettimeofday(&now, NULL);
+ clock_sec = now.tv_sec;
+ localclock = localtime(&clock_sec);
+ (void)snprintf(line_buf[i], N_CHARS, " ");
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+ (void)snprintf(line_buf[i], N_CHARS,
+ "Message from Talk_Daemon@%s at %d:%02d on %d/%.2d/%.2d ...",
+ hostname, localclock->tm_hour , localclock->tm_min,
+ localclock->tm_year + 1900, localclock->tm_mon + 1,
+ localclock->tm_mday);
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+
+ vis_user = malloc(strlen(request->l_name) * 4 + 1);
+ strvis(vis_user, request->l_name, VIS_CSTYLE);
+ (void)snprintf(line_buf[i], N_CHARS,
+ "talk: connection requested by %s@%s", vis_user, remote_machine);
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+ (void)snprintf(line_buf[i], N_CHARS, "talk: respond with: talk %s@%s",
+ vis_user, remote_machine);
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+ (void)snprintf(line_buf[i], N_CHARS, " ");
+ 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/extern.h b/libexec/talkd/extern.h
new file mode 100644
index 0000000..a91cb25
--- /dev/null
+++ b/libexec/talkd/extern.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2002 M. Warner Losh. 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.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+extern int debug;
+extern char hostname[];
+
+int announce(CTL_MSG *, const char *);
+int delete_invite(u_int32_t);
+void do_announce(CTL_MSG *, CTL_RESPONSE *);
+CTL_MSG *find_match(CTL_MSG *request);
+CTL_MSG *find_request(CTL_MSG *request);
+int find_user(const char *name, char *tty);
+void insert_table(CTL_MSG *, CTL_RESPONSE *);
+int new_id(void);
+int print_mesg(const char *, CTL_MSG *, const char *);
+void print_request(const char *, CTL_MSG *);
+void print_response(const char *, CTL_RESPONSE *);
+void process_request(CTL_MSG *mp, CTL_RESPONSE *rp);
+void timeout(int sig);
diff --git a/libexec/talkd/print.c b/libexec/talkd/print.c
new file mode 100644
index 0000000..9a74961
--- /dev/null
+++ b/libexec/talkd/print.c
@@ -0,0 +1,89 @@
+/*
+ * 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. 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
+#if 0
+static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/4/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/* debug print routines */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <protocols/talkd.h>
+#include <stdio.h>
+#include <syslog.h>
+
+#include "extern.h"
+
+static const char *types[] =
+ { "leave_invite", "look_up", "delete", "announce" };
+#define NTYPES (sizeof (types) / sizeof (types[0]))
+static const char *answers[] =
+ { "success", "not_here", "failed", "machine_unknown", "permission_denied",
+ "unknown_request", "badversion", "badaddr", "badctladdr" };
+#define NANSWERS (sizeof (answers) / sizeof (answers[0]))
+
+void
+print_request(const char *cp, CTL_MSG *mp)
+{
+ const char *tp;
+ char tbuf[80];
+
+ if (mp->type > NTYPES) {
+ (void)snprintf(tbuf, sizeof(tbuf), "type %d", mp->type);
+ tp = tbuf;
+ } else
+ tp = types[mp->type];
+ syslog(LOG_DEBUG, "%s: %s: id %lu, l_user %s, r_user %s, r_tty %s",
+ cp, tp, (long)mp->id_num, mp->l_name, mp->r_name, mp->r_tty);
+}
+
+void
+print_response(const char *cp, CTL_RESPONSE *rp)
+{
+ const char *tp, *ap;
+ char tbuf[80], abuf[80];
+
+ if (rp->type > NTYPES) {
+ (void)snprintf(tbuf, sizeof(tbuf), "type %d", rp->type);
+ tp = tbuf;
+ } else
+ tp = types[rp->type];
+ if (rp->answer > NANSWERS) {
+ (void)snprintf(abuf, sizeof(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..742043f
--- /dev/null
+++ b/libexec/talkd/process.c
@@ -0,0 +1,221 @@
+/*
+ * 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. 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
+#if 0
+static char sccsid[] = "@(#)process.c 8.2 (Berkeley) 11/16/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#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 <ctype.h>
+#include <err.h>
+#include <netdb.h>
+#include <paths.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <utmpx.h>
+
+#include "extern.h"
+
+void
+process_request(CTL_MSG *mp, CTL_RESPONSE *rp)
+{
+ CTL_MSG *ptr;
+ 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);
+}
+
+void
+do_announce(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 *)(void *)(sa))
+ hp = gethostbyaddr(&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;
+ }
+}
+
+/*
+ * Search utmp for the local user
+ */
+int
+find_user(const char *name, char *tty)
+{
+ struct utmpx *ut;
+ int status;
+ struct stat statb;
+ time_t best = 0;
+ char ftty[sizeof(_PATH_DEV) - 1 + sizeof(ut->ut_line)];
+
+ setutxent();
+ status = NOT_HERE;
+ (void) strcpy(ftty, _PATH_DEV);
+ while ((ut = getutxent()) != NULL)
+ if (ut->ut_type == USER_PROCESS &&
+ strcmp(ut->ut_user, name) == 0) {
+ if (*tty == '\0' || best != 0) {
+ if (best == 0)
+ status = PERMISSION_DENIED;
+ /* no particular tty was requested */
+ (void) strcpy(ftty + sizeof(_PATH_DEV) - 1,
+ ut->ut_line);
+ if (stat(ftty, &statb) == 0) {
+ if (!(statb.st_mode & 020))
+ continue;
+ if (statb.st_atime > best) {
+ best = statb.st_atime;
+ (void) strcpy(tty, ut->ut_line);
+ status = SUCCESS;
+ continue;
+ }
+ }
+ }
+ if (strcmp(ut->ut_line, tty) == 0) {
+ status = SUCCESS;
+ break;
+ }
+ }
+ endutxent();
+ return (status);
+}
diff --git a/libexec/talkd/table.c b/libexec/talkd/table.c
new file mode 100644
index 0000000..70b71b2
--- /dev/null
+++ b/libexec/talkd/table.c
@@ -0,0 +1,232 @@
+/*
+ * 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. 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
+#if 0
+static char sccsid[] = "@(#)table.c 8.1 (Berkeley) 6/4/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#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 <netinet/in.h>
+#include <protocols/talkd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+#define MAX_ID 16000 /* << 2^15 so I don't have sign troubles */
+
+#define NIL ((TABLE_ENTRY *)0)
+
+static struct timeval tp;
+
+typedef struct table_entry TABLE_ENTRY;
+
+struct table_entry {
+ CTL_MSG request;
+ long time;
+ TABLE_ENTRY *next;
+ TABLE_ENTRY *last;
+};
+
+static void delete(TABLE_ENTRY *);
+
+static TABLE_ENTRY *table = NIL;
+
+/*
+ * Look in the table for an invitation that matches the current
+ * request looking for an invitation
+ */
+CTL_MSG *
+find_match(CTL_MSG *request)
+{
+ TABLE_ENTRY *ptr;
+ time_t current_time;
+
+ gettimeofday(&tp, NULL);
+ 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(CTL_MSG *request)
+{
+ TABLE_ENTRY *ptr;
+ time_t current_time;
+
+ gettimeofday(&tp, NULL);
+ 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);
+}
+
+void
+insert_table(CTL_MSG *request, CTL_RESPONSE *response)
+{
+ TABLE_ENTRY *ptr;
+ time_t current_time;
+
+ gettimeofday(&tp, NULL);
+ 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
+ */
+int
+new_id(void)
+{
+ 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'
+ */
+int
+delete_invite(u_int32_t id_num)
+{
+ 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
+ */
+static void
+delete(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..27f3a4e
--- /dev/null
+++ b/libexec/talkd/talkd.8
@@ -0,0 +1,76 @@
+.\" 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. 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
+.\" $FreeBSD$
+.\"
+.Dd December 11, 1993
+.Dt TALKD 8
+.Os
+.Sh NAME
+.Nm talkd
+.Nd remote user communication server
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility
+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
+.In 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).
+.Pp
+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.
+.Pp
+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
+utility appeared in
+.Bx 4.3 .
diff --git a/libexec/talkd/talkd.c b/libexec/talkd/talkd.c
new file mode 100644
index 0000000..a444fe7
--- /dev/null
+++ b/libexec/talkd/talkd.c
@@ -0,0 +1,136 @@
+/*
+ * 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. 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 const char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)talkd.c 8.1 (Berkeley) 6/4/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#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 <netinet/in.h>
+#include <protocols/talkd.h>
+#include <err.h>
+#include <errno.h>
+#include <paths.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"
+
+static CTL_MSG request;
+static CTL_RESPONSE response;
+
+int debug = 0;
+static long lastmsgtime;
+
+char hostname[MAXHOSTNAMELEN];
+
+#define TIMEOUT 30
+#define MAXIDLE 120
+
+int
+main(int argc, char *argv[])
+{
+ register CTL_MSG *mp = &request;
+ int cc;
+ struct sockaddr ctl_addr;
+
+#ifdef NOTDEF
+ /*
+ * removed so ntalkd can run in tty sandbox
+ */
+ if (getuid())
+ errx(1, "getuid: not super-user");
+#endif
+ openlog("talkd", LOG_PID, LOG_DAEMON);
+ if (gethostname(hostname, sizeof(hostname) - 1) < 0) {
+ syslog(LOG_ERR, "gethostname: %m");
+ _exit(1);
+ }
+ hostname[sizeof(hostname) - 1] = '\0';
+ 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 (;;) {
+ 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);
+ (void)memcpy(&ctl_addr, &mp->ctl_addr, sizeof(ctl_addr));
+ ctl_addr.sa_family = ntohs(mp->ctl_addr.sa_family);
+ ctl_addr.sa_len = sizeof(ctl_addr);
+ process_request(mp, &response);
+ /* can block here, is this what I want? */
+ cc = sendto(STDIN_FILENO, (char *)&response,
+ sizeof(response), 0, &ctl_addr, sizeof(ctl_addr));
+ if (cc != sizeof (response))
+ syslog(LOG_WARNING, "sendto: %m");
+ }
+}
+
+void
+timeout(int sig __unused)
+{
+ int save_errno = errno;
+
+ if (time(0) - lastmsgtime >= MAXIDLE)
+ _exit(0);
+ alarm(TIMEOUT);
+ errno = save_errno;
+}
diff --git a/libexec/tcpd/Makefile b/libexec/tcpd/Makefile
new file mode 100644
index 0000000..f3fbf29
--- /dev/null
+++ b/libexec/tcpd/Makefile
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+.include <src.opts.mk>
+
+.PATH: ${.CURDIR}/../../contrib/tcp_wrappers
+
+PROG= tcpd
+MAN= tcpd.8
+CSTD?= c89
+CFLAGS+=-DREAL_DAEMON_DIR=\"/usr/libexec\" \
+ -DSEVERITY=LOG_INFO -DRFC931_TIMEOUT=10 \
+ -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\" \
+ -DFACILITY=LOG_DAEMON
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+=-DINET6
+.endif
+
+DPADD= ${LIBWRAP}
+LDADD= -lwrap
+
+WARNS?= 1
+
+.include <bsd.prog.mk>
diff --git a/libexec/telnetd/Makefile b/libexec/telnetd/Makefile
new file mode 100644
index 0000000..690b03c
--- /dev/null
+++ b/libexec/telnetd/Makefile
@@ -0,0 +1,47 @@
+# $FreeBSD$
+
+# Do not define -DKLUDGELINEMODE, as it does not interact well with many
+# telnet implementations.
+
+.include <src.opts.mk>
+
+TELNETDIR= ${.CURDIR}/../../contrib/telnet
+.PATH: ${TELNETDIR}/telnetd
+
+PROG= telnetd
+MAN= telnetd.8
+
+SRCS= global.c slc.c state.c sys_term.c telnetd.c \
+ termstat.c utility.c
+
+WARNS?= 2
+WFORMAT?= 0
+
+CFLAGS+= -DLINEMODE -DUSE_TERMIO -DDIAGNOSTICS -DOLD_ENVIRON \
+ -DENV_HACK -DSTREAMSPTY
+
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+= -DINET6
+.endif
+
+CFLAGS+= -I${TELNETDIR}
+
+LIBTELNET= ${.OBJDIR}/../../lib/libtelnet/libtelnet.a
+
+DPADD= ${LIBUTIL} ${LIBTERMCAPW} ${LIBTELNET}
+LDADD= -lutil -ltermcapw ${LIBTELNET}
+
+.if ${MK_OPENSSL} != "no"
+SRCS+= authenc.c
+CFLAGS+= -DAUTHENTICATION -DENCRYPTION
+DPADD+= ${LIBMP} ${LIBCRYPTO} ${LIBCRYPT} ${LIBPAM}
+LDADD+= -lmp -lcrypto -lcrypt ${MINUSLPAM}
+.endif
+
+.if ${MK_KERBEROS_SUPPORT} != "no"
+CFLAGS+= -DKRB5 -DFORWARD -Dnet_write=telnet_net_write
+DPADD+= ${LIBKRB5} ${LIBHX509} ${LIBASN1} ${LIBROKEN} ${LIBCOM_ERR}
+LDADD+= -lkrb5 -lhx509 -lasn1 -lroken -lcom_err
+.endif
+
+.include <bsd.prog.mk>
diff --git a/libexec/tests/Makefile b/libexec/tests/Makefile
new file mode 100644
index 0000000..665efab
--- /dev/null
+++ b/libexec/tests/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/libexec
+
+.PATH: ${.CURDIR:H:H}/tests
+KYUAFILE= yes
+
+.include <bsd.test.mk>
diff --git a/libexec/tftp-proxy/Makefile b/libexec/tftp-proxy/Makefile
new file mode 100644
index 0000000..d8541c4
--- /dev/null
+++ b/libexec/tftp-proxy/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/pf/tftp-proxy
+
+PROG= tftp-proxy
+SRCS= tftp-proxy.c filter.c
+MAN= tftp-proxy.8
+
+WARNS?= 3
+
+.include <bsd.prog.mk>
diff --git a/libexec/tftpd/Makefile b/libexec/tftpd/Makefile
new file mode 100644
index 0000000..f005001
--- /dev/null
+++ b/libexec/tftpd/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.1 (Berkeley) 6/4/93
+# $FreeBSD$
+
+PROG= tftpd
+MAN= tftpd.8
+SRCS= tftp-file.c tftp-io.c tftp-options.c tftp-transfer.c tftp-utils.c
+SRCS+= tftpd.c
+WFORMAT=0
+
+DPADD= ${LIBWRAP}
+LDADD= -lwrap
+
+.include <bsd.prog.mk>
diff --git a/libexec/tftpd/tftp-file.c b/libexec/tftpd/tftp-file.c
new file mode 100644
index 0000000..6b8fb6e
--- /dev/null
+++ b/libexec/tftpd/tftp-file.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2008 Edwin Groothuis. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <arpa/tftp.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "tftp-file.h"
+#include "tftp-utils.h"
+
+static FILE *file;
+static int convert;
+
+static char convbuffer[66000];
+static int gotcr = 0;
+
+static size_t
+convert_from_net(char *buffer, size_t count)
+{
+ size_t i, n;
+
+ /*
+ * Convert all CR/LF to LF and all CR,NUL to CR
+ */
+
+ n = 0;
+ for (i = 0; i < count; i++) {
+
+ if (gotcr == 0) {
+ convbuffer[n++] = buffer[i];
+ gotcr = (buffer[i] == '\r');
+ continue;
+ }
+
+ /* CR, NULL -> CR */
+ if (buffer[i] == '\0') {
+ gotcr = 0;
+ continue;
+ }
+
+ /* CR, LF -> LF */
+ if (buffer[i] == '\n') {
+ if (n == 0) {
+ if (ftell(file) != 0) {
+ fseek(file, -1, SEEK_END);
+ convbuffer[n++] = '\n';
+ } else {
+ /* This shouldn't happen */
+ tftp_log(LOG_ERR,
+ "Received LF as first character");
+ abort();
+ }
+ } else
+ convbuffer[n-1] = '\n';
+ gotcr = 0;
+ continue;
+ }
+
+ /* Everything else just accept as is */
+ convbuffer[n++] = buffer[i];
+ gotcr = (buffer[i] == '\r');
+ continue;
+ }
+
+ return fwrite(convbuffer, 1, n, file);
+}
+
+static size_t
+convert_to_net(char *buffer, size_t count, int init)
+{
+ size_t i;
+ static size_t n = 0, in = 0;
+ static int newline = 0;
+
+ if (init) {
+ newline = 0;
+ n = 0;
+ in = 0;
+ return 0 ;
+ }
+
+ /*
+ * Convert all LF to CR,LF and all CR to CR,NUL
+ */
+ i = 0;
+
+ if (newline) {
+ buffer[i++] = newline;
+ newline = 0;
+ }
+
+ while (i < count) {
+ if (n == in) {
+ /* When done we're done */
+ if (feof(file)) break;
+
+ /* Otherwise read another bunch */
+ in = fread(convbuffer, 1, count, file);
+ if (in == 0) break;
+ n = 0;
+ }
+
+ /* CR -> CR,NULL */
+ if (convbuffer[n] == '\r') {
+ buffer[i++] = '\r';
+ buffer[i++] = '\0';
+ n++;
+ continue;
+ }
+
+ /* LF -> CR,LF */
+ if (convbuffer[n] == '\n') {
+ buffer[i++] = '\r';
+ buffer[i++] = '\n';
+ n++;
+ continue;
+ }
+
+ buffer[i++] = convbuffer[n++];
+ }
+
+ if (i > count) {
+ /*
+ * Whoops... that isn't alllowed (but it will happen
+ * when there is a CR or LF at the end of the buffer)
+ */
+ newline = buffer[i-1];
+ }
+
+ if (i < count) {
+ /* We are done! */
+ return i;
+ } else
+ return count;
+
+}
+
+int
+write_init(int fd, FILE *f, const char *mode)
+{
+
+ if (f == NULL) {
+ file = fdopen(fd, "w");
+ if (file == NULL) {
+ int en = errno;
+ tftp_log(LOG_ERR, "fdopen() failed: %s",
+ strerror(errno));
+ return en;
+ }
+ } else
+ file = f;
+ convert = !strcmp(mode, "netascii");
+ return 0;
+}
+
+size_t
+write_file(char *buffer, int count)
+{
+
+ if (convert == 0)
+ return fwrite(buffer, 1, count, file);
+
+ return convert_from_net(buffer, count);
+}
+
+int
+write_close(void)
+{
+
+ if (fclose(file) != 0) {
+ tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+int
+read_init(int fd, FILE *f, const char *mode)
+{
+
+ convert_to_net(NULL, 0, 1);
+ if (f == NULL) {
+ file = fdopen(fd, "r");
+ if (file == NULL) {
+ int en = errno;
+ tftp_log(LOG_ERR, "fdopen() failed: %s",
+ strerror(errno));
+ return en;
+ }
+ } else
+ file = f;
+ convert = !strcmp(mode, "netascii");
+ return 0;
+}
+
+size_t
+read_file(char *buffer, int count)
+{
+
+ if (convert == 0)
+ return fread(buffer, 1, count, file);
+
+ return convert_to_net(buffer, count, 0);
+}
+
+int
+read_close(void)
+{
+
+ if (fclose(file) != 0) {
+ tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+
+/* When an error has occurred, it is possible that the two sides
+ * are out of synch. Ie: that what I think is the other side's
+ * response to packet N is really their response to packet N-1.
+ *
+ * So, to try to prevent that, we flush all the input queued up
+ * for us on the network connection on our host.
+ *
+ * We return the number of packets we flushed (mostly for reporting
+ * when trace is active).
+ */
+
+int
+synchnet(int peer) /* socket to flush */
+{
+ int i, j = 0;
+ char rbuf[MAXPKTSIZE];
+ struct sockaddr_storage from;
+ socklen_t fromlen;
+
+ while (1) {
+ (void) ioctl(peer, FIONREAD, &i);
+ if (i) {
+ j++;
+ fromlen = sizeof from;
+ (void) recvfrom(peer, rbuf, sizeof (rbuf), 0,
+ (struct sockaddr *)&from, &fromlen);
+ } else {
+ return(j);
+ }
+ }
+}
diff --git a/libexec/tftpd/tftp-file.h b/libexec/tftpd/tftp-file.h
new file mode 100644
index 0000000..fcc4d0d
--- /dev/null
+++ b/libexec/tftpd/tftp-file.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008 Edwin Groothuis. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+int write_init(int fd, FILE *f, const char *mode);
+size_t write_file(char *buffer, int count);
+int write_close(void);
+
+int read_init(int fd, FILE *f, const char *mode);
+size_t read_file(char *buffer, int count);
+int read_close(void);
+
+int synchnet(int peer);
diff --git a/libexec/tftpd/tftp-io.c b/libexec/tftpd/tftp-io.c
new file mode 100644
index 0000000..6dabf4f
--- /dev/null
+++ b/libexec/tftpd/tftp-io.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2008 Edwin Groothuis. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/tftp.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "tftp-file.h"
+#include "tftp-io.h"
+#include "tftp-utils.h"
+#include "tftp-options.h"
+
+struct sockaddr_storage peer_sock;
+struct sockaddr_storage me_sock;
+
+static int send_packet(int peer, uint16_t block, char *pkt, int size);
+
+static struct errmsg {
+ int e_code;
+ const 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" },
+ { EOPTNEG, "Option negotiation" },
+ { -1, NULL }
+};
+
+#define DROPPACKET(s) \
+ if (packetdroppercentage != 0 && \
+ random()%100 < packetdroppercentage) { \
+ tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s); \
+ return; \
+ }
+#define DROPPACKETn(s,n) \
+ if (packetdroppercentage != 0 && \
+ random()%100 < packetdroppercentage) { \
+ tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s); \
+ return (n); \
+ }
+
+const char *
+errtomsg(int error)
+{
+ static char ebuf[40];
+ 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);
+ snprintf(ebuf, sizeof(ebuf), "error %d", error);
+ return (ebuf);
+}
+
+static int
+send_packet(int peer, uint16_t block, char *pkt, int size)
+{
+ int i;
+ int t = 1;
+
+ for (i = 0; i < 12 ; i++) {
+ DROPPACKETn("send_packet", 0);
+
+ if (sendto(peer, pkt, size, 0, (struct sockaddr *)&peer_sock,
+ peer_sock.ss_len) == size) {
+ if (i)
+ tftp_log(LOG_ERR,
+ "%s block %d, attempt %d successful",
+ packettype(ntohs(((struct tftphdr *)
+ (pkt))->th_opcode)), block, i);
+ return (0);
+ }
+ tftp_log(LOG_ERR,
+ "%s block %d, attempt %d failed (Error %d: %s)",
+ packettype(ntohs(((struct tftphdr *)(pkt))->th_opcode)),
+ block, i, errno, strerror(errno));
+ sleep(t);
+ if (t < 32)
+ t <<= 1;
+ }
+ tftp_log(LOG_ERR, "send_packet: %s", strerror(errno));
+ return (1);
+}
+
+/*
+ * Send an ERROR packet (error message).
+ * Error code passed in is one of the
+ * standard TFTP codes, or a UNIX errno
+ * offset by 100.
+ */
+void
+send_error(int peer, int error)
+{
+ struct tftphdr *tp;
+ int length;
+ struct errmsg *pe;
+ char buf[MAXPKTSIZE];
+
+ if (debug&DEBUG_PACKETS)
+ tftp_log(LOG_DEBUG, "Sending ERROR %d", error);
+
+ DROPPACKET("send_error");
+
+ 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 (debug&DEBUG_PACKETS)
+ tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg);
+
+ if (sendto(peer, buf, length, 0,
+ (struct sockaddr *)&peer_sock, peer_sock.ss_len) != length)
+ tftp_log(LOG_ERR, "send_error: %s", strerror(errno));
+}
+
+/*
+ * Send an WRQ packet (write request).
+ */
+int
+send_wrq(int peer, char *filename, char *mode)
+{
+ int n;
+ struct tftphdr *tp;
+ char *bp;
+ char buf[MAXPKTSIZE];
+ int size;
+
+ if (debug&DEBUG_PACKETS)
+ tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'",
+ filename, mode
+ );
+
+ DROPPACKETn("send_wrq", 1);
+
+ tp = (struct tftphdr *)buf;
+ tp->th_opcode = htons((u_short)WRQ);
+ size = 2;
+
+ bp = tp->th_stuff;
+ strcpy(bp, filename);
+ bp += strlen(filename);
+ *bp = 0;
+ bp++;
+ size += strlen(filename) + 1;
+
+ strcpy(bp, mode);
+ bp += strlen(mode);
+ *bp = 0;
+ bp++;
+ size += strlen(mode) + 1;
+
+ if (options_rfc_enabled)
+ size += make_options(peer, bp, sizeof(buf) - size);
+
+ n = sendto(peer, buf, size, 0,
+ (struct sockaddr *)&peer_sock, peer_sock.ss_len);
+ if (n != size) {
+ tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno));
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Send an RRQ packet (write request).
+ */
+int
+send_rrq(int peer, char *filename, char *mode)
+{
+ int n;
+ struct tftphdr *tp;
+ char *bp;
+ char buf[MAXPKTSIZE];
+ int size;
+
+ if (debug&DEBUG_PACKETS)
+ tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'",
+ filename, mode
+ );
+
+ DROPPACKETn("send_rrq", 1);
+
+ tp = (struct tftphdr *)buf;
+ tp->th_opcode = htons((u_short)RRQ);
+ size = 2;
+
+ bp = tp->th_stuff;
+ strcpy(bp, filename);
+ bp += strlen(filename);
+ *bp = 0;
+ bp++;
+ size += strlen(filename) + 1;
+
+ strcpy(bp, mode);
+ bp += strlen(mode);
+ *bp = 0;
+ bp++;
+ size += strlen(mode) + 1;
+
+ if (options_rfc_enabled) {
+ options[OPT_TSIZE].o_request = strdup("0");
+ size += make_options(peer, bp, sizeof(buf) - size);
+ }
+
+ n = sendto(peer, buf, size, 0,
+ (struct sockaddr *)&peer_sock, peer_sock.ss_len);
+ if (n != size) {
+ tftp_log(LOG_ERR, "send_rrq: %d %s", n, strerror(errno));
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Send an OACK packet (option acknowledgement).
+ */
+int
+send_oack(int peer)
+{
+ struct tftphdr *tp;
+ int size, i, n;
+ char *bp;
+ char buf[MAXPKTSIZE];
+
+ if (debug&DEBUG_PACKETS)
+ tftp_log(LOG_DEBUG, "Sending OACK");
+
+ DROPPACKETn("send_oack", 0);
+
+ /*
+ * Send back an options acknowledgement (only the ones with
+ * a reply for)
+ */
+ tp = (struct tftphdr *)buf;
+ bp = buf + 2;
+ size = sizeof(buf) - 2;
+ tp->th_opcode = htons((u_short)OACK);
+ for (i = 0; options[i].o_type != NULL; i++) {
+ if (options[i].o_reply != NULL) {
+ n = snprintf(bp, size, "%s%c%s", options[i].o_type,
+ 0, options[i].o_reply);
+ bp += n+1;
+ size -= n+1;
+ if (size < 0) {
+ tftp_log(LOG_ERR, "oack: buffer overflow");
+ exit(1);
+ }
+ }
+ }
+ size = bp - buf;
+
+ if (sendto(peer, buf, size, 0,
+ (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
+ tftp_log(LOG_INFO, "send_oack: %s", strerror(errno));
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Send an ACK packet (acknowledgement).
+ */
+int
+send_ack(int fp, uint16_t block)
+{
+ struct tftphdr *tp;
+ int size;
+ char buf[MAXPKTSIZE];
+
+ if (debug&DEBUG_PACKETS)
+ tftp_log(LOG_DEBUG, "Sending ACK for block %d", block);
+
+ DROPPACKETn("send_ack", 0);
+
+ tp = (struct tftphdr *)buf;
+ size = sizeof(buf) - 2;
+ tp->th_opcode = htons((u_short)ACK);
+ tp->th_block = htons((u_short)block);
+ size = 4;
+
+ if (sendto(fp, buf, size, 0,
+ (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) {
+ tftp_log(LOG_INFO, "send_ack: %s", strerror(errno));
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Send a DATA packet
+ */
+int
+send_data(int peer, uint16_t block, char *data, int size)
+{
+ char buf[MAXPKTSIZE];
+ struct tftphdr *pkt;
+ int n;
+
+ if (debug&DEBUG_PACKETS)
+ tftp_log(LOG_DEBUG, "Sending DATA packet %d of %d bytes",
+ block, size);
+
+ DROPPACKETn("send_data", 0);
+
+ pkt = (struct tftphdr *)buf;
+
+ pkt->th_opcode = htons((u_short)DATA);
+ pkt->th_block = htons((u_short)block);
+ memcpy(pkt->th_data, data, size);
+
+ n = send_packet(peer, block, (char *)pkt, size + 4);
+ return (n);
+}
+
+
+/*
+ * Receive a packet
+ */
+static jmp_buf timeoutbuf;
+
+static void
+timeout(int sig __unused)
+{
+
+ /* tftp_log(LOG_DEBUG, "Timeout\n"); Inside a signal handler... */
+ longjmp(timeoutbuf, 1);
+}
+
+int
+receive_packet(int peer, char *data, int size, struct sockaddr_storage *from,
+ int thistimeout)
+{
+ struct tftphdr *pkt;
+ struct sockaddr_storage from_local;
+ struct sockaddr_storage *pfrom;
+ socklen_t fromlen;
+ int n;
+ static int waiting;
+
+ if (debug&DEBUG_PACKETS)
+ tftp_log(LOG_DEBUG,
+ "Waiting %d seconds for packet", timeoutpacket);
+
+ pkt = (struct tftphdr *)data;
+
+ waiting = 0;
+ signal(SIGALRM, timeout);
+ setjmp(timeoutbuf);
+ alarm(thistimeout);
+
+ if (waiting > 0) {
+ alarm(0);
+ return (RP_TIMEOUT);
+ }
+
+ if (waiting > 0) {
+ tftp_log(LOG_ERR, "receive_packet: timeout");
+ alarm(0);
+ return (RP_TIMEOUT);
+ }
+
+ waiting++;
+ pfrom = (from == NULL) ? &from_local : from;
+ fromlen = sizeof(*pfrom);
+ n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen);
+
+ alarm(0);
+
+ DROPPACKETn("receive_packet", RP_TIMEOUT);
+
+ if (n < 0) {
+ tftp_log(LOG_ERR, "receive_packet: timeout");
+ return (RP_TIMEOUT);
+ }
+
+ alarm(0);
+
+ if (n < 0) {
+ /* No idea what could have happened if it isn't a timeout */
+ tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno));
+ return (RP_RECVFROM);
+ }
+ if (n < 4) {
+ tftp_log(LOG_ERR,
+ "receive_packet: packet too small (%d bytes)", n);
+ return (RP_TOOSMALL);
+ }
+
+ pkt->th_opcode = ntohs((u_short)pkt->th_opcode);
+ if (pkt->th_opcode == DATA ||
+ pkt->th_opcode == ACK)
+ pkt->th_block = ntohs((u_short)pkt->th_block);
+
+ if (pkt->th_opcode == DATA && n > pktsize) {
+ tftp_log(LOG_ERR, "receive_packet: packet too big");
+ return (RP_TOOBIG);
+ }
+
+ if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr !=
+ ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) {
+ tftp_log(LOG_ERR,
+ "receive_packet: received packet from wrong source");
+ return (RP_WRONGSOURCE);
+ }
+
+ if (pkt->th_opcode == ERROR) {
+ tftp_log(pkt->th_code == EUNDEF ? LOG_DEBUG : LOG_ERR,
+ "Got ERROR packet: %s", pkt->th_msg);
+ return (RP_ERROR);
+ }
+
+ if (debug&DEBUG_PACKETS)
+ tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet",
+ n, packettype(pkt->th_opcode));
+
+ return n - 4;
+}
diff --git a/libexec/tftpd/tftp-io.h b/libexec/tftpd/tftp-io.h
new file mode 100644
index 0000000..70558bc
--- /dev/null
+++ b/libexec/tftpd/tftp-io.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 Edwin Groothuis. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define RP_NONE 0
+#define RP_RECVFROM -1
+#define RP_TOOSMALL -2
+#define RP_ERROR -3
+#define RP_WRONGSOURCE -4
+#define RP_TIMEOUT -5
+#define RP_TOOBIG -6
+
+const char *errtomsg(int);
+void send_error(int peer, int);
+int send_wrq(int peer, char *, char *);
+int send_rrq(int peer, char *, char *);
+int send_oack(int peer);
+int send_ack(int peer, unsigned short);
+int send_data(int peer, uint16_t, char *, int);
+int receive_packet(int peer, char *, int, struct sockaddr_storage *, int);
+
+extern struct sockaddr_storage peer_sock;
+extern struct sockaddr_storage me_sock;
diff --git a/libexec/tftpd/tftp-options.c b/libexec/tftpd/tftp-options.c
new file mode 100644
index 0000000..f926f32
--- /dev/null
+++ b/libexec/tftpd/tftp-options.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2008 Edwin Groothuis. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <arpa/tftp.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "tftp-utils.h"
+#include "tftp-io.h"
+#include "tftp-options.h"
+
+/*
+ * Option handlers
+ */
+
+struct options options[] = {
+ { "tsize", NULL, NULL, NULL /* option_tsize */, 1 },
+ { "timeout", NULL, NULL, option_timeout, 1 },
+ { "blksize", NULL, NULL, option_blksize, 1 },
+ { "blksize2", NULL, NULL, option_blksize2, 0 },
+ { "rollover", NULL, NULL, option_rollover, 0 },
+ { NULL, NULL, NULL, NULL, 0 }
+};
+
+/* By default allow them */
+int options_rfc_enabled = 1;
+int options_extra_enabled = 1;
+
+/*
+ * Rules for the option handlers:
+ * - If there is no o_request, there will be no processing.
+ *
+ * For servers
+ * - Logging is done as warnings.
+ * - The handler exit()s if there is a serious problem with the
+ * values submitted in the option.
+ *
+ * For clients
+ * - Logging is done as errors. After all, the server shouldn't
+ * return rubbish.
+ * - The handler returns if there is a serious problem with the
+ * values submitted in the option.
+ * - Sending the EBADOP packets is done by the handler.
+ */
+
+int
+option_tsize(int peer __unused, struct tftphdr *tp __unused, int mode,
+ struct stat *stbuf)
+{
+
+ if (options[OPT_TSIZE].o_request == NULL)
+ return (0);
+
+ if (mode == RRQ)
+ asprintf(&options[OPT_TSIZE].o_reply,
+ "%ju", stbuf->st_size);
+ else
+ /* XXX Allows writes of all sizes. */
+ options[OPT_TSIZE].o_reply =
+ strdup(options[OPT_TSIZE].o_request);
+ return (0);
+}
+
+int
+option_timeout(int peer)
+{
+ int to;
+
+ if (options[OPT_TIMEOUT].o_request == NULL)
+ return (0);
+
+ to = atoi(options[OPT_TIMEOUT].o_request);
+ if (to < TIMEOUT_MIN || to > TIMEOUT_MAX) {
+ tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING,
+ "Received bad value for timeout. "
+ "Should be between %d and %d, received %d",
+ TIMEOUT_MIN, TIMEOUT_MAX, to);
+ send_error(peer, EBADOP);
+ if (acting_as_client)
+ return (1);
+ exit(1);
+ } else {
+ timeoutpacket = to;
+ options[OPT_TIMEOUT].o_reply =
+ strdup(options[OPT_TIMEOUT].o_request);
+ }
+ settimeouts(timeoutpacket, timeoutnetwork, maxtimeouts);
+
+ if (debug&DEBUG_OPTIONS)
+ tftp_log(LOG_DEBUG, "Setting timeout to '%s'",
+ options[OPT_TIMEOUT].o_reply);
+
+ return (0);
+}
+
+int
+option_rollover(int peer)
+{
+
+ if (options[OPT_ROLLOVER].o_request == NULL)
+ return (0);
+
+ if (strcmp(options[OPT_ROLLOVER].o_request, "0") != 0
+ && strcmp(options[OPT_ROLLOVER].o_request, "1") != 0) {
+ tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING,
+ "Bad value for rollover, "
+ "should be either 0 or 1, received '%s', "
+ "ignoring request",
+ options[OPT_ROLLOVER].o_request);
+ if (acting_as_client) {
+ send_error(peer, EBADOP);
+ return (1);
+ }
+ return (0);
+ }
+ options[OPT_ROLLOVER].o_reply =
+ strdup(options[OPT_ROLLOVER].o_request);
+
+ if (debug&DEBUG_OPTIONS)
+ tftp_log(LOG_DEBUG, "Setting rollover to '%s'",
+ options[OPT_ROLLOVER].o_reply);
+
+ return (0);
+}
+
+int
+option_blksize(int peer)
+{
+ u_long maxdgram;
+ size_t len;
+
+ if (options[OPT_BLKSIZE].o_request == NULL)
+ return (0);
+
+ /* maximum size of an UDP packet according to the system */
+ len = sizeof(maxdgram);
+ if (sysctlbyname("net.inet.udp.maxdgram",
+ &maxdgram, &len, NULL, 0) < 0) {
+ tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram");
+ return (acting_as_client ? 1 : 0);
+ }
+
+ int size = atoi(options[OPT_BLKSIZE].o_request);
+ if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) {
+ if (acting_as_client) {
+ tftp_log(LOG_ERR,
+ "Invalid blocksize (%d bytes), aborting",
+ size);
+ send_error(peer, EBADOP);
+ return (1);
+ } else {
+ tftp_log(LOG_WARNING,
+ "Invalid blocksize (%d bytes), ignoring request",
+ size);
+ return (0);
+ }
+ }
+
+ if (size > (int)maxdgram) {
+ if (acting_as_client) {
+ tftp_log(LOG_ERR,
+ "Invalid blocksize (%d bytes), "
+ "net.inet.udp.maxdgram sysctl limits it to "
+ "%ld bytes.\n", size, maxdgram);
+ send_error(peer, EBADOP);
+ return (1);
+ } else {
+ tftp_log(LOG_WARNING,
+ "Invalid blocksize (%d bytes), "
+ "net.inet.udp.maxdgram sysctl limits it to "
+ "%ld bytes.\n", size, maxdgram);
+ size = maxdgram;
+ /* No reason to return */
+ }
+ }
+
+ asprintf(&options[OPT_BLKSIZE].o_reply, "%d", size);
+ segsize = size;
+ pktsize = size + 4;
+ if (debug&DEBUG_OPTIONS)
+ tftp_log(LOG_DEBUG, "Setting blksize to '%s'",
+ options[OPT_BLKSIZE].o_reply);
+
+ return (0);
+}
+
+int
+option_blksize2(int peer __unused)
+{
+ u_long maxdgram;
+ int size, i;
+ size_t len;
+
+ int sizes[] = {
+ 8, 16, 32, 64, 128, 256, 512, 1024,
+ 2048, 4096, 8192, 16384, 32768, 0
+ };
+
+ if (options[OPT_BLKSIZE2].o_request == NULL)
+ return (0);
+
+ /* maximum size of an UDP packet according to the system */
+ len = sizeof(maxdgram);
+ if (sysctlbyname("net.inet.udp.maxdgram",
+ &maxdgram, &len, NULL, 0) < 0) {
+ tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram");
+ return (acting_as_client ? 1 : 0);
+ }
+
+ size = atoi(options[OPT_BLKSIZE2].o_request);
+ for (i = 0; sizes[i] != 0; i++) {
+ if (size == sizes[i]) break;
+ }
+ if (sizes[i] == 0) {
+ tftp_log(LOG_INFO,
+ "Invalid blocksize2 (%d bytes), ignoring request", size);
+ return (acting_as_client ? 1 : 0);
+ }
+
+ if (size > (int)maxdgram) {
+ for (i = 0; sizes[i+1] != 0; i++) {
+ if ((int)maxdgram < sizes[i+1]) break;
+ }
+ tftp_log(LOG_INFO,
+ "Invalid blocksize2 (%d bytes), net.inet.udp.maxdgram "
+ "sysctl limits it to %ld bytes.\n", size, maxdgram);
+ size = sizes[i];
+ /* No need to return */
+ }
+
+ asprintf(&options[OPT_BLKSIZE2].o_reply, "%d", size);
+ segsize = size;
+ pktsize = size + 4;
+ if (debug&DEBUG_OPTIONS)
+ tftp_log(LOG_DEBUG, "Setting blksize2 to '%s'",
+ options[OPT_BLKSIZE2].o_reply);
+
+ return (0);
+}
+
+/*
+ * Append the available options to the header
+ */
+uint16_t
+make_options(int peer __unused, char *buffer, uint16_t size) {
+ int i;
+ char *value;
+ const char *option;
+ uint16_t length;
+ uint16_t returnsize = 0;
+
+ if (!options_rfc_enabled) return (0);
+
+ for (i = 0; options[i].o_type != NULL; i++) {
+ if (options[i].rfc == 0 && !options_extra_enabled)
+ continue;
+
+ option = options[i].o_type;
+ if (acting_as_client)
+ value = options[i].o_request;
+ else
+ value = options[i].o_reply;
+ if (value == NULL)
+ continue;
+
+ length = strlen(value) + strlen(option) + 2;
+ if (size <= length) {
+ tftp_log(LOG_ERR,
+ "Running out of option space for "
+ "option '%s' with value '%s': "
+ "needed %d bytes, got %d bytes",
+ option, value, size, length);
+ continue;
+ }
+
+ sprintf(buffer, "%s%c%s%c", option, '\000', value, '\000');
+ size -= length;
+ buffer += length;
+ returnsize += length;
+ }
+
+ return (returnsize);
+}
+
+/*
+ * Parse the received options in the header
+ */
+int
+parse_options(int peer, char *buffer, uint16_t size)
+{
+ int i, options_failed;
+ char *c, *cp, *option, *value;
+
+ if (!options_rfc_enabled) return (0);
+
+ /* Parse the options */
+ cp = buffer;
+ options_failed = 0;
+ while (size > 0) {
+ option = cp;
+ i = get_field(peer, cp, size);
+ cp += i;
+
+ value = cp;
+ i = get_field(peer, cp, size);
+ cp += i;
+
+ /* We are at the end */
+ if (*option == '\0') break;
+
+ if (debug&DEBUG_OPTIONS)
+ tftp_log(LOG_DEBUG,
+ "option: '%s' value: '%s'", option, value);
+
+ for (c = option; *c; c++)
+ if (isupper(*c))
+ *c = tolower(*c);
+ for (i = 0; options[i].o_type != NULL; i++) {
+ if (strcmp(option, options[i].o_type) == 0) {
+ if (!acting_as_client)
+ options[i].o_request = value;
+ if (!options_extra_enabled && !options[i].rfc) {
+ tftp_log(LOG_INFO,
+ "Option '%s' with value '%s' found "
+ "but it is not an RFC option",
+ option, value);
+ continue;
+ }
+ if (options[i].o_handler)
+ options_failed +=
+ (options[i].o_handler)(peer);
+ break;
+ }
+ }
+ if (options[i].o_type == NULL)
+ tftp_log(LOG_WARNING,
+ "Unknown option: '%s'", option);
+
+ size -= strlen(option) + strlen(value) + 2;
+ }
+
+ return (options_failed);
+}
+
+/*
+ * Set some default values in the options
+ */
+void
+init_options(void)
+{
+
+ options[OPT_ROLLOVER].o_request = strdup("0");
+}
diff --git a/libexec/tftpd/tftp-options.h b/libexec/tftpd/tftp-options.h
new file mode 100644
index 0000000..d8bd2fc
--- /dev/null
+++ b/libexec/tftpd/tftp-options.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008 Edwin Groothuis. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Options
+ */
+
+void init_options(void);
+uint16_t make_options(int peer, char *buffer, uint16_t size);
+int parse_options(int peer, char *buffer, uint16_t size);
+
+/* Call back functions */
+int option_tsize(int peer, struct tftphdr *, int, struct stat *);
+int option_timeout(int peer);
+int option_blksize(int peer);
+int option_blksize2(int peer);
+int option_rollover(int peer);
+
+extern int options_extra_enabled;
+extern int options_rfc_enabled;
+
+struct options {
+ const char *o_type;
+ char *o_request;
+ char *o_reply;
+ int (*o_handler)(int peer);
+ int rfc;
+};
+
+extern struct options options[];
+enum opt_enum {
+ OPT_TSIZE = 0,
+ OPT_TIMEOUT,
+ OPT_BLKSIZE,
+ OPT_BLKSIZE2,
+ OPT_ROLLOVER,
+};
diff --git a/libexec/tftpd/tftp-transfer.c b/libexec/tftpd/tftp-transfer.c
new file mode 100644
index 0000000..d0c8a95
--- /dev/null
+++ b/libexec/tftpd/tftp-transfer.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2008 Edwin Groothuis. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include "tftp-file.h"
+#include "tftp-io.h"
+#include "tftp-utils.h"
+#include "tftp-options.h"
+#include "tftp-transfer.h"
+
+/*
+ * Send a file via the TFTP data session.
+ */
+void
+tftp_send(int peer, uint16_t *block, struct tftp_stats *ts)
+{
+ struct tftphdr *rp;
+ int size, n_data, n_ack, try;
+ uint16_t oldblock;
+ char sendbuffer[MAXPKTSIZE];
+ char recvbuffer[MAXPKTSIZE];
+
+ rp = (struct tftphdr *)recvbuffer;
+ *block = 1;
+ ts->amount = 0;
+ do {
+ if (debug&DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG, "Sending block %d", *block);
+
+ size = read_file(sendbuffer, segsize);
+ if (size < 0) {
+ tftp_log(LOG_ERR, "read_file returned %d", size);
+ send_error(peer, errno + 100);
+ goto abort;
+ }
+
+ for (try = 0; ; try++) {
+ n_data = send_data(peer, *block, sendbuffer, size);
+ if (n_data > 0) {
+ if (try == maxtimeouts) {
+ tftp_log(LOG_ERR,
+ "Cannot send DATA packet #%d, "
+ "giving up", *block);
+ return;
+ }
+ tftp_log(LOG_ERR,
+ "Cannot send DATA packet #%d, trying again",
+ *block);
+ continue;
+ }
+
+ n_ack = receive_packet(peer, recvbuffer,
+ MAXPKTSIZE, NULL, timeoutpacket);
+ if (n_ack < 0) {
+ if (n_ack == RP_TIMEOUT) {
+ if (try == maxtimeouts) {
+ tftp_log(LOG_ERR,
+ "Timeout #%d send ACK %d "
+ "giving up", try, *block);
+ return;
+ }
+ tftp_log(LOG_WARNING,
+ "Timeout #%d on ACK %d",
+ try, *block);
+ continue;
+ }
+
+ /* Either read failure or ERROR packet */
+ if (debug&DEBUG_SIMPLE)
+ tftp_log(LOG_ERR, "Aborting: %s",
+ rp_strerror(n_ack));
+ goto abort;
+ }
+ if (rp->th_opcode == ACK) {
+ ts->blocks++;
+ if (rp->th_block == *block) {
+ ts->amount += size;
+ break;
+ }
+
+ /* Re-synchronize with the other side */
+ (void) synchnet(peer);
+ if (rp->th_block == (*block - 1)) {
+ ts->retries++;
+ continue;
+ }
+ }
+
+ }
+ oldblock = *block;
+ (*block)++;
+ if (oldblock > *block) {
+ if (options[OPT_ROLLOVER].o_request == NULL) {
+ /*
+ * "rollover" option not specified in
+ * tftp client. Default to rolling block
+ * counter to 0.
+ */
+ *block = 0;
+ } else {
+ *block = atoi(options[OPT_ROLLOVER].o_request);
+ }
+
+ ts->rollovers++;
+ }
+ gettimeofday(&(ts->tstop), NULL);
+ } while (size == segsize);
+abort:
+ return;
+}
+
+/*
+ * Receive a file via the TFTP data session.
+ *
+ * - It could be that the first block has already arrived while
+ * trying to figure out if we were receiving options or not. In
+ * that case it is passed to this function.
+ */
+void
+tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts,
+ struct tftphdr *firstblock, size_t fb_size)
+{
+ struct tftphdr *rp;
+ uint16_t oldblock;
+ int n_data, n_ack, writesize, i, retry;
+ char recvbuffer[MAXPKTSIZE];
+
+ ts->amount = 0;
+
+ if (firstblock != NULL) {
+ writesize = write_file(firstblock->th_data, fb_size);
+ ts->amount += writesize;
+ for (i = 0; ; i++) {
+ n_ack = send_ack(peer, *block);
+ if (n_ack > 0) {
+ if (i == maxtimeouts) {
+ tftp_log(LOG_ERR,
+ "Cannot send ACK packet #%d, "
+ "giving up", *block);
+ return;
+ }
+ tftp_log(LOG_ERR,
+ "Cannot send ACK packet #%d, trying again",
+ *block);
+ continue;
+ }
+
+ break;
+ }
+
+ if (fb_size != segsize) {
+ gettimeofday(&(ts->tstop), NULL);
+ return;
+ }
+ }
+
+ rp = (struct tftphdr *)recvbuffer;
+ do {
+ oldblock = *block;
+ (*block)++;
+ if (oldblock > *block) {
+ if (options[OPT_ROLLOVER].o_request == NULL) {
+ /*
+ * "rollover" option not specified in
+ * tftp client. Default to rolling block
+ * counter to 0.
+ */
+ *block = 0;
+ } else {
+ *block = atoi(options[OPT_ROLLOVER].o_request);
+ }
+
+ ts->rollovers++;
+ }
+
+ for (retry = 0; ; retry++) {
+ if (debug&DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG,
+ "Receiving DATA block %d", *block);
+
+ n_data = receive_packet(peer, recvbuffer,
+ MAXPKTSIZE, NULL, timeoutpacket);
+ if (n_data < 0) {
+ if (retry == maxtimeouts) {
+ tftp_log(LOG_ERR,
+ "Timeout #%d on DATA block %d, "
+ "giving up", retry, *block);
+ return;
+ }
+ if (n_data == RP_TIMEOUT) {
+ tftp_log(LOG_WARNING,
+ "Timeout #%d on DATA block %d",
+ retry, *block);
+ send_ack(peer, oldblock);
+ continue;
+ }
+
+ /* Either read failure or ERROR packet */
+ if (debug&DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG, "Aborting: %s",
+ rp_strerror(n_data));
+ goto abort;
+ }
+ if (rp->th_opcode == DATA) {
+ ts->blocks++;
+
+ if (rp->th_block == *block)
+ break;
+
+ tftp_log(LOG_WARNING,
+ "Expected DATA block %d, got block %d",
+ *block, rp->th_block);
+
+ /* Re-synchronize with the other side */
+ (void) synchnet(peer);
+ if (rp->th_block == (*block-1)) {
+ tftp_log(LOG_INFO, "Trying to sync");
+ *block = oldblock;
+ ts->retries++;
+ goto send_ack; /* rexmit */
+ }
+
+ } else {
+ tftp_log(LOG_WARNING,
+ "Expected DATA block, got %s block",
+ packettype(rp->th_opcode));
+ }
+ }
+
+ if (n_data > 0) {
+ writesize = write_file(rp->th_data, n_data);
+ ts->amount += writesize;
+ if (writesize <= 0) {
+ tftp_log(LOG_ERR,
+ "write_file returned %d", writesize);
+ if (writesize < 0)
+ send_error(peer, errno + 100);
+ else
+ send_error(peer, ENOSPACE);
+ goto abort;
+ }
+ }
+
+send_ack:
+ for (i = 0; ; i++) {
+ n_ack = send_ack(peer, *block);
+ if (n_ack > 0) {
+
+ if (i == maxtimeouts) {
+ tftp_log(LOG_ERR,
+ "Cannot send ACK packet #%d, "
+ "giving up", *block);
+ return;
+ }
+
+ tftp_log(LOG_ERR,
+ "Cannot send ACK packet #%d, trying again",
+ *block);
+ continue;
+ }
+
+ break;
+ }
+ gettimeofday(&(ts->tstop), NULL);
+ } while (n_data == segsize);
+
+ /* Don't do late packet management for the client implementation */
+ if (acting_as_client)
+ return;
+
+ for (i = 0; ; i++) {
+ n_data = receive_packet(peer, (char *)rp, pktsize,
+ NULL, timeoutpacket);
+ if (n_data <= 0)
+ break;
+ if (n_data > 0 &&
+ rp->th_opcode == DATA && /* and got a data block */
+ *block == rp->th_block) /* then my last ack was lost */
+ send_ack(peer, *block); /* resend final ack */
+ }
+
+abort:
+ return;
+}
diff --git a/libexec/tftpd/tftp-transfer.h b/libexec/tftpd/tftp-transfer.h
new file mode 100644
index 0000000..2cfa2df
--- /dev/null
+++ b/libexec/tftpd/tftp-transfer.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2008 Edwin Groothuis. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+void tftp_send(int peer, uint16_t *block, struct tftp_stats *tp);
+void tftp_receive(int peer, uint16_t *block, struct tftp_stats *tp,
+ struct tftphdr *firstblock, size_t fb_size);
diff --git a/libexec/tftpd/tftp-utils.c b/libexec/tftpd/tftp-utils.c
new file mode 100644
index 0000000..c55f120
--- /dev/null
+++ b/libexec/tftpd/tftp-utils.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2008 Edwin Groothuis. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <arpa/tftp.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "tftp-utils.h"
+#include "tftp-io.h"
+
+/*
+ * Default values, can be changed later via the TFTP Options
+ */
+int timeoutpacket = TIMEOUT;
+int timeoutnetwork = MAX_TIMEOUTS * TIMEOUT;
+int maxtimeouts = MAX_TIMEOUTS;
+uint16_t segsize = SEGSIZE;
+uint16_t pktsize = SEGSIZE + 4;
+
+int acting_as_client;
+
+
+/*
+ * Set timeout values for packet reception. The idea is that you
+ * get 'maxtimeouts' of 5 seconds between 'timeoutpacket' (i.e. the
+ * first timeout) to 'timeoutnetwork' (i.e. the last timeout)
+ */
+int
+settimeouts(int _timeoutpacket, int _timeoutnetwork, int _maxtimeouts __unused)
+{
+ int i;
+
+ /* We cannot do impossible things */
+ if (_timeoutpacket >= _timeoutnetwork)
+ return (0);
+
+ maxtimeouts = 0;
+ i = _timeoutpacket;
+ while (i < _timeoutnetwork || maxtimeouts < MIN_TIMEOUTS) {
+ maxtimeouts++;
+ i += 5;
+ }
+
+ timeoutpacket = _timeoutpacket;
+ timeoutnetwork = i;
+ return (1);
+}
+
+/* translate IPv4 mapped IPv6 address to IPv4 address */
+void
+unmappedaddr(struct sockaddr_in6 *sin6)
+{
+ struct sockaddr_in *sin4;
+ u_int32_t addr;
+ int port;
+
+ if (sin6->sin6_family != AF_INET6 ||
+ !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
+ return;
+ sin4 = (struct sockaddr_in *)sin6;
+ memcpy(&addr, &sin6->sin6_addr.s6_addr[12], sizeof(addr));
+ port = sin6->sin6_port;
+ memset(sin4, 0, sizeof(struct sockaddr_in));
+ sin4->sin_addr.s_addr = addr;
+ sin4->sin_port = port;
+ sin4->sin_family = AF_INET;
+ sin4->sin_len = sizeof(struct sockaddr_in);
+}
+
+/* Get a field from a \0 separated string */
+ssize_t
+get_field(int peer, char *buffer, ssize_t size)
+{
+ char *cp = buffer;
+
+ while (cp < buffer + size) {
+ if (*cp == '\0') break;
+ cp++;
+ }
+ if (*cp != '\0') {
+ tftp_log(LOG_ERR, "Bad option - no trailing \\0 found");
+ send_error(peer, EBADOP);
+ exit(1);
+ }
+ return (cp - buffer + 1);
+}
+
+/*
+ * Logging functions
+ */
+static int _tftp_logtostdout = 1;
+
+void
+tftp_openlog(const char *ident, int logopt, int facility)
+{
+
+ _tftp_logtostdout = (ident == NULL);
+ if (_tftp_logtostdout == 0)
+ openlog(ident, logopt, facility);
+}
+
+void
+tftp_closelog(void)
+{
+
+ if (_tftp_logtostdout == 0)
+ closelog();
+}
+
+void
+tftp_log(int priority, const char *message, ...)
+{
+ va_list ap;
+ char *s;
+
+ va_start(ap, message);
+ if (_tftp_logtostdout == 0) {
+ vasprintf(&s, message, ap);
+ syslog(priority, "%s", s);
+ } else {
+ vprintf(message, ap);
+ printf("\n");
+ }
+ va_end(ap);
+}
+
+/*
+ * Packet types
+ */
+struct packettypes packettypes[] = {
+ { RRQ, "RRQ" },
+ { WRQ, "WRQ" },
+ { DATA, "DATA" },
+ { ACK, "ACK" },
+ { ERROR, "ERROR" },
+ { OACK, "OACK" },
+ { 0, NULL },
+};
+
+const char *
+packettype(int type)
+{
+ static char failed[100];
+ int i = 0;
+
+ while (packettypes[i].name != NULL) {
+ if (packettypes[i].value == type)
+ break;
+ i++;
+ }
+ if (packettypes[i].name != NULL)
+ return packettypes[i].name;
+ sprintf(failed, "unknown (type: %d)", type);
+ return (failed);
+}
+
+/*
+ * Debugs
+ */
+int debug = DEBUG_NONE;
+struct debugs debugs[] = {
+ { DEBUG_PACKETS, "packet", "Packet debugging" },
+ { DEBUG_SIMPLE, "simple", "Simple debugging" },
+ { DEBUG_OPTIONS, "options", "Options debugging" },
+ { DEBUG_ACCESS, "access", "TCPd access debugging" },
+ { DEBUG_NONE, NULL, "No debugging" },
+};
+int packetdroppercentage = 0;
+
+int
+debug_find(char *s)
+{
+ int i = 0;
+
+ while (debugs[i].name != NULL) {
+ if (strcasecmp(debugs[i].name, s) == 0)
+ break;
+ i++;
+ }
+ return (debugs[i].value);
+}
+
+int
+debug_finds(char *s)
+{
+ int i = 0;
+ char *ps = s;
+
+ while (s != NULL) {
+ ps = strchr(s, ' ');
+ if (ps != NULL)
+ *ps = '\0';
+ i += debug_find(s);
+ if (ps != NULL)
+ *ps = ' ';
+ s = ps;
+ }
+ return (i);
+}
+
+const char *
+debug_show(int d)
+{
+ static char s[100];
+ int i = 0;
+
+ s[0] = '\0';
+ while (debugs[i].name != NULL) {
+ if (d&debugs[i].value) {
+ if (s[0] != '\0')
+ strcat(s, " ");
+ strcat(s, debugs[i].name);
+ }
+ i++;
+ }
+ if (s[0] != '\0')
+ return (s);
+ return ("none");
+}
+
+/*
+ * RP_
+ */
+struct rp_errors rp_errors[] = {
+ { RP_TIMEOUT, "Network timeout" },
+ { RP_TOOSMALL, "Not enough data bytes" },
+ { RP_WRONGSOURCE, "Invalid IP address of UDP port" },
+ { RP_ERROR, "Error packet" },
+ { RP_RECVFROM, "recvfrom() complained" },
+ { RP_TOOBIG, "Too many data bytes" },
+ { RP_NONE, NULL }
+};
+
+char *
+rp_strerror(int error)
+{
+ static char s[100];
+ int i = 0;
+
+ while (rp_errors[i].desc != NULL) {
+ if (rp_errors[i].error == error) {
+ strcpy(s, rp_errors[i].desc);
+ }
+ i++;
+ }
+ if (s[0] == '\0')
+ sprintf(s, "unknown (error=%d)", error);
+ return (s);
+}
+
+/*
+ * Performance figures
+ */
+
+void
+stats_init(struct tftp_stats *ts)
+{
+
+ ts->amount = 0;
+ ts->rollovers = 0;
+ ts->retries = 0;
+ ts->blocks = 0;
+ ts->amount = 0;
+ gettimeofday(&(ts->tstart), NULL);
+}
+
+void
+printstats(const char *direction, int verbose, struct tftp_stats *ts)
+{
+ double delta; /* compute delta in 1/10's second units */
+
+ delta = ((ts->tstop.tv_sec*10.)+(ts->tstop.tv_usec/100000)) -
+ ((ts->tstart.tv_sec*10.)+(ts->tstart.tv_usec/100000));
+ delta = delta/10.; /* back to seconds */
+
+ printf("%s %zu bytes during %.1f seconds in %u blocks",
+ direction, ts->amount, delta, ts->blocks);
+
+ if (ts->rollovers != 0)
+ printf(" with %d rollover%s",
+ ts->rollovers, ts->rollovers != 1 ? "s" : "");
+
+ if (verbose)
+ printf(" [%.0f bits/sec]", (ts->amount*8.)/delta);
+ putchar('\n');
+}
diff --git a/libexec/tftpd/tftp-utils.h b/libexec/tftpd/tftp-utils.h
new file mode 100644
index 0000000..c1becf3
--- /dev/null
+++ b/libexec/tftpd/tftp-utils.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2008 Edwin Groothuis. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ */
+#define TIMEOUT 5
+#define MAX_TIMEOUTS 5
+
+/* Generic values */
+#define MAXSEGSIZE 65464 /* Maximum size of the data segment */
+#define MAXPKTSIZE (MAXSEGSIZE + 4) /* Maximum size of the packet */
+
+/* For the blksize option */
+#define BLKSIZE_MIN 8 /* Minimum size of the data segment */
+#define BLKSIZE_MAX MAXSEGSIZE /* Maximum size of the data segment */
+
+/* For the timeout option */
+#define TIMEOUT_MIN 0 /* Minimum timeout value */
+#define TIMEOUT_MAX 255 /* Maximum timeout value */
+#define MIN_TIMEOUTS 3
+
+extern int timeoutpacket;
+extern int timeoutnetwork;
+extern int maxtimeouts;
+int settimeouts(int timeoutpacket, int timeoutnetwork, int maxtimeouts);
+
+extern uint16_t segsize;
+extern uint16_t pktsize;
+
+extern int acting_as_client;
+
+/*
+ */
+void unmappedaddr(struct sockaddr_in6 *sin6);
+ssize_t get_field(int peer, char *buffer, ssize_t size);
+
+/*
+ * Packet types
+ */
+struct packettypes {
+ int value;
+ const char *const name;
+};
+extern struct packettypes packettypes[];
+const char *packettype(int);
+
+/*
+ * RP_
+ */
+struct rp_errors {
+ int error;
+ const char *const desc;
+};
+extern struct rp_errors rp_errors[];
+char *rp_strerror(int error);
+
+/*
+ * Debug features
+ */
+#define DEBUG_NONE 0x0000
+#define DEBUG_PACKETS 0x0001
+#define DEBUG_SIMPLE 0x0002
+#define DEBUG_OPTIONS 0x0004
+#define DEBUG_ACCESS 0x0008
+struct debugs {
+ int value;
+ const char *const name;
+ const char *const desc;
+};
+extern int debug;
+extern struct debugs debugs[];
+extern int packetdroppercentage;
+int debug_find(char *s);
+int debug_finds(char *s);
+const char *debug_show(int d);
+
+/*
+ * Log routines
+ */
+#define DEBUG(s) tftp_log(LOG_DEBUG, "%s", s)
+extern int tftp_logtostdout;
+void tftp_openlog(const char *ident, int logopt, int facility);
+void tftp_closelog(void);
+void tftp_log(int priority, const char *message, ...) __printflike(2, 3);
+
+/*
+ * Performance figures
+ */
+struct tftp_stats {
+ size_t amount;
+ int rollovers;
+ uint32_t blocks;
+ int retries;
+ struct timeval tstart;
+ struct timeval tstop;
+};
+
+void stats_init(struct tftp_stats *ts);
+void printstats(const char *direction, int verbose, struct tftp_stats *ts);
diff --git a/libexec/tftpd/tftpd.8 b/libexec/tftpd/tftpd.8
new file mode 100644
index 0000000..0071264
--- /dev/null
+++ b/libexec/tftpd/tftpd.8
@@ -0,0 +1,306 @@
+.\" 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. 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
+.\" $FreeBSD$
+.\"
+.Dd June 22, 2011
+.Dt TFTPD 8
+.Os
+.Sh NAME
+.Nm tftpd
+.Nd Internet Trivial File Transfer Protocol server
+.Sh SYNOPSIS
+.Nm tftpd
+.Op Fl cdClnow
+.Op Fl F Ar strftime-format
+.Op Fl s Ar directory
+.Op Fl u Ar user
+.Op Fl U Ar umask
+.Op Ar directory ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is a server which supports the
+Internet Trivial File Transfer
+Protocol
+.Pq Tn RFC 1350 .
+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
+will allow only publicly readable files to be
+accessed.
+Files containing the string
+.Dq Li "/../"
+or starting with
+.Dq Li "../"
+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
+with a list of directories by including up to 20 pathnames
+as server program arguments in
+.Xr inetd.conf 5 .
+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
+.Fl s
+option provides additional security by changing
+the root directory of
+.Nm ,
+thereby prohibiting accesses to outside of the specified
+.Ar directory .
+Because
+.Xr chroot 2
+requires super-user privileges,
+.Nm
+must be run as
+.Li root .
+However, after performing the
+.Xr chroot 2
+call,
+.Nm
+will set its user ID to that of the specified
+.Ar user ,
+or
+.Dq Li nobody
+if no
+.Fl u
+option is specified.
+.Pp
+The options are:
+.Bl -tag -width Ds
+.It Fl c
+Changes the default root directory of a connecting host via
+.Xr chroot 2
+based on the connecting IP address.
+This prevents multiple clients from writing to the same file at the same time.
+If the directory does not exist, the client connection is refused.
+The
+.Fl s
+option is required for
+.Fl c
+and the specified
+.Ar directory
+is used as a base.
+.It Fl C
+Operates the same as
+.Fl c
+except it falls back to
+.Ar directory
+specified via
+.Fl s
+if a directory does not exist for the client's IP.
+.It Fl F
+Use this
+.Xr strftime 3
+compatible format string for the creation of the suffix if
+.Fl W
+is specified.
+By default the string "%Y%m%d" is used.
+.It Fl d, d Ar [value]
+Enables debug output.
+If
+.Ar value
+is not specified, then the debug level is increased by one
+for each instance of
+.Fl d
+which is specified.
+.Pp
+If
+.Ar value
+is specified, then the debug level is set to
+.Ar value .
+The debug level is a bitmask implemented in
+.Pa src/libexec/tftpd/tftp-utils.h .
+Valid values are 0 (DEBUG_NONE), 1 (DEBUG_PACKETS), 2, (DEBUG_SIMPLE),
+4 (DEBUG_OPTIONS), and 8 (DEBUG_ACCESS). Multiple debug values can be combined
+in the bitmask by logically OR'ing the values. For example, specifying
+.Fl d
+.Ar 15
+will enable all the debug values.
+.It Fl l
+Log all requests using
+.Xr syslog 3
+with the facility of
+.Dv LOG_FTP .
+.Sy Note :
+Logging of
+.Dv LOG_FTP
+messages
+must also be enabled in the syslog configuration file,
+.Xr syslog.conf 5 .
+.It Fl n
+Suppress negative acknowledgement of requests for nonexistent
+relative filenames.
+.It Fl o
+Disable support for RFC2347 style TFTP Options.
+.It Fl s Ar directory
+Cause
+.Nm
+to change its root directory to
+.Ar directory .
+After doing that but before accepting commands,
+.Nm
+will switch credentials to an unprivileged user.
+.It Fl u Ar user
+Switch credentials to
+.Ar user
+(default
+.Dq Li nobody )
+when the
+.Fl s
+option is used.
+The user must be specified by name, not a numeric UID.
+.It Fl U Ar umask
+Set the
+.Ar umask
+for newly created files.
+The default is 022
+.Pq Dv S_IWGRP | S_IWOTH .
+.It Fl w
+Allow write requests to create new files.
+By default
+.Nm
+requires that the file specified in a write request exist.
+Note that this only works in directories writable by the user
+specified with
+.Fl u
+option
+.It Fl W
+As
+.Fl w
+but append a YYYYMMDD.nn sequence number to the end of the filename.
+Note that the string YYYYMMDD can be changed with the
+.Fl F
+option.
+.El
+.Sh SEE ALSO
+.Xr tftp 1 ,
+.Xr chroot 2 ,
+.Xr syslog 3 ,
+.Xr inetd.conf 5 ,
+.Xr services 5 ,
+.Xr syslog.conf 5 ,
+.Xr inetd 8
+.Pp
+The following RFC's are supported:
+.Rs
+.%T RFC 1350: The TFTP Protocol (Revision 2)
+.Re
+.Rs
+.%T RFC 2347: TFTP Option Extension
+.Re
+.Rs
+.%T RFC 2348: TFTP Blocksize Option
+.Re
+.Rs
+.%T RFC 2349: TFTP Timeout Interval and Transfer Size Options
+.Re
+.Pp
+The non-standard
+.Cm rollover
+and
+.Cm blksize2
+TFTP options are mentioned here:
+.Rs
+.%T Extending TFTP
+.%U http://www.compuphase.com/tftp.htm
+.Re
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 ;
+the
+.Fl s
+option was introduced in
+.Fx 2.2 ,
+the
+.Fl u
+option was introduced in
+.Fx 4.2 ,
+the
+.Fl c
+option was introduced in
+.Fx 4.3 ,
+and the
+.Fl F
+and
+.Fl W
+options were introduced in
+.Fx 7.4 .
+.Pp
+Support for Timeout Interval and Transfer Size Options (RFC2349)
+was introduced in
+.Fx 5.0 ,
+support for the TFTP Blocksize Option (RFC2348) and the blksize2 option
+was introduced in
+.Fx 7.4 .
+.Pp
+Edwin Groothuis <edwin@FreeBSD.org> performed a major rewrite of the
+.Nm
+and
+.Xr tftp 1
+code to support RFC2348.
+.Sh NOTES
+Files larger than 33,553,919 octets (65535 blocks, last one <512
+octets) cannot be correctly transferred without client and server
+supporting blocksize negotiation (RFCs 2347 and 2348),
+or the non-standard TFTP rollover option.
+As a kludge,
+.Nm
+accepts a sequence of block number which wrap to zero after 65535,
+even if the rollover option is not specified.
+.Pp
+Many tftp clients will not transfer files over 16,776,703 octets
+(32767 blocks), as they incorrectly count the block number using
+a signed rather than unsigned 16-bit integer.
diff --git a/libexec/tftpd/tftpd.c b/libexec/tftpd/tftpd.c
new file mode 100644
index 0000000..571fa59
--- /dev/null
+++ b/libexec/tftpd/tftpd.c
@@ -0,0 +1,837 @@
+/*
+ * 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. 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 const char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * 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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <tcpd.h>
+#include <unistd.h>
+
+#include "tftp-file.h"
+#include "tftp-io.h"
+#include "tftp-utils.h"
+#include "tftp-transfer.h"
+#include "tftp-options.h"
+
+static void tftp_wrq(int peer, char *, ssize_t);
+static void tftp_rrq(int peer, char *, ssize_t);
+
+/*
+ * 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 {
+ const char *name;
+ int len;
+} dirs[MAXDIRS+1];
+static int suppress_naks;
+static int logging;
+static int ipchroot;
+static int create_new = 0;
+static const char *newfile_format = "%Y%m%d";
+static int increase_name = 0;
+static mode_t mask = S_IWGRP | S_IWOTH;
+
+struct formats;
+static void tftp_recvfile(int peer, const char *mode);
+static void tftp_xmitfile(int peer, const char *mode);
+static int validate_access(int peer, char **, int);
+static char peername[NI_MAXHOST];
+
+static FILE *file;
+
+static struct formats {
+ const char *f_mode;
+ int f_convert;
+} formats[] = {
+ { "netascii", 1 },
+ { "octet", 0 },
+ { NULL, 0 }
+};
+
+int
+main(int argc, char *argv[])
+{
+ struct tftphdr *tp;
+ int peer;
+ socklen_t peerlen, len;
+ ssize_t n;
+ int ch;
+ char *chroot_dir = NULL;
+ struct passwd *nobody;
+ const char *chuser = "nobody";
+ char recvbuffer[MAXPKTSIZE];
+ int allow_ro = 1, allow_wo = 1;
+
+ tzset(); /* syslog in localtime */
+ acting_as_client = 0;
+
+ tftp_openlog("tftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
+ while ((ch = getopt(argc, argv, "cCd:F:lnoOp:s:u:U:wW")) != -1) {
+ switch (ch) {
+ case 'c':
+ ipchroot = 1;
+ break;
+ case 'C':
+ ipchroot = 2;
+ break;
+ case 'd':
+ if (atoi(optarg) != 0)
+ debug += atoi(optarg);
+ else
+ debug |= debug_finds(optarg);
+ break;
+ case 'F':
+ newfile_format = optarg;
+ break;
+ case 'l':
+ logging = 1;
+ break;
+ case 'n':
+ suppress_naks = 1;
+ break;
+ case 'o':
+ options_rfc_enabled = 0;
+ break;
+ case 'O':
+ options_extra_enabled = 0;
+ break;
+ case 'p':
+ packetdroppercentage = atoi(optarg);
+ tftp_log(LOG_INFO,
+ "Randomly dropping %d out of 100 packets",
+ packetdroppercentage);
+ break;
+ case 's':
+ chroot_dir = optarg;
+ break;
+ case 'u':
+ chuser = optarg;
+ break;
+ case 'U':
+ mask = strtol(optarg, NULL, 0);
+ break;
+ case 'w':
+ create_new = 1;
+ break;
+ case 'W':
+ create_new = 1;
+ increase_name = 1;
+ break;
+ default:
+ tftp_log(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++;
+ }
+ }
+ }
+ else if (chroot_dir) {
+ dirs->name = "/";
+ dirs->len = 1;
+ }
+ if (ipchroot > 0 && chroot_dir == NULL) {
+ tftp_log(LOG_ERR, "-c requires -s");
+ exit(1);
+ }
+
+ umask(mask);
+
+ {
+ int on = 1;
+ if (ioctl(0, FIONBIO, &on) < 0) {
+ tftp_log(LOG_ERR, "ioctl(FIONBIO): %s", strerror(errno));
+ exit(1);
+ }
+ }
+
+ /* Find out who we are talking to and what we are going to do */
+ peerlen = sizeof(peer_sock);
+ n = recvfrom(0, recvbuffer, MAXPKTSIZE, 0,
+ (struct sockaddr *)&peer_sock, &peerlen);
+ if (n < 0) {
+ tftp_log(LOG_ERR, "recvfrom: %s", strerror(errno));
+ exit(1);
+ }
+ getnameinfo((struct sockaddr *)&peer_sock, peer_sock.ss_len,
+ peername, sizeof(peername), NULL, 0, NI_NUMERICHOST);
+
+ /*
+ * 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 i, pid;
+
+ 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.
+ */
+ peerlen = sizeof peer_sock;
+ i = recvfrom(0, recvbuffer, MAXPKTSIZE, 0,
+ (struct sockaddr *)&peer_sock, &peerlen);
+ if (i > 0) {
+ n = i;
+ }
+ } else {
+ break;
+ }
+ }
+ if (pid < 0) {
+ tftp_log(LOG_ERR, "fork: %s", strerror(errno));
+ exit(1);
+ } else if (pid != 0) {
+ exit(0);
+ }
+ }
+
+ /*
+ * See if the client is allowed to talk to me.
+ * (This needs to be done before the chroot())
+ */
+ {
+ struct request_info req;
+
+ request_init(&req, RQ_CLIENT_ADDR, peername, 0);
+ request_set(&req, RQ_DAEMON, "tftpd", 0);
+
+ if (hosts_access(&req) == 0) {
+ if (debug&DEBUG_ACCESS)
+ tftp_log(LOG_WARNING,
+ "Access denied by 'tftpd' entry "
+ "in /etc/hosts.allow");
+
+ /*
+ * Full access might be disabled, but maybe the
+ * client is allowed to do read-only access.
+ */
+ request_set(&req, RQ_DAEMON, "tftpd-ro", 0);
+ allow_ro = hosts_access(&req);
+
+ request_set(&req, RQ_DAEMON, "tftpd-wo", 0);
+ allow_wo = hosts_access(&req);
+
+ if (allow_ro == 0 && allow_wo == 0) {
+ tftp_log(LOG_WARNING,
+ "Unauthorized access from %s", peername);
+ exit(1);
+ }
+
+ if (debug&DEBUG_ACCESS) {
+ if (allow_ro)
+ tftp_log(LOG_WARNING,
+ "But allowed readonly access "
+ "via 'tftpd-ro' entry");
+ if (allow_wo)
+ tftp_log(LOG_WARNING,
+ "But allowed writeonly access "
+ "via 'tftpd-wo' entry");
+ }
+ } else
+ if (debug&DEBUG_ACCESS)
+ tftp_log(LOG_WARNING,
+ "Full access allowed"
+ "in /etc/hosts.allow");
+ }
+
+ /*
+ * Since we exit here, we should do that only after the above
+ * recvfrom to keep inetd from constantly forking should there
+ * be a problem. See the above comment about system clogging.
+ */
+ if (chroot_dir) {
+ if (ipchroot > 0) {
+ char *tempchroot;
+ struct stat sb;
+ int statret;
+ struct sockaddr_storage ss;
+ char hbuf[NI_MAXHOST];
+
+ statret = -1;
+ memcpy(&ss, &peer_sock, peer_sock.ss_len);
+ unmappedaddr((struct sockaddr_in6 *)&ss);
+ getnameinfo((struct sockaddr *)&ss, ss.ss_len,
+ hbuf, sizeof(hbuf), NULL, 0,
+ NI_NUMERICHOST);
+ asprintf(&tempchroot, "%s/%s", chroot_dir, hbuf);
+ if (ipchroot == 2)
+ statret = stat(tempchroot, &sb);
+ if (ipchroot == 1 ||
+ (statret == 0 && (sb.st_mode & S_IFDIR)))
+ chroot_dir = tempchroot;
+ }
+ /* Must get this before chroot because /etc might go away */
+ if ((nobody = getpwnam(chuser)) == NULL) {
+ tftp_log(LOG_ERR, "%s: no such user", chuser);
+ exit(1);
+ }
+ if (chroot(chroot_dir)) {
+ tftp_log(LOG_ERR, "chroot: %s: %s",
+ chroot_dir, strerror(errno));
+ exit(1);
+ }
+ chdir("/");
+ setgroups(1, &nobody->pw_gid);
+ if (setuid(nobody->pw_uid) != 0) {
+ tftp_log(LOG_ERR, "setuid failed");
+ exit(1);
+ }
+ }
+
+ len = sizeof(me_sock);
+ if (getsockname(0, (struct sockaddr *)&me_sock, &len) == 0) {
+ switch (me_sock.ss_family) {
+ case AF_INET:
+ ((struct sockaddr_in *)&me_sock)->sin_port = 0;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *)&me_sock)->sin6_port = 0;
+ break;
+ default:
+ /* unsupported */
+ break;
+ }
+ } else {
+ memset(&me_sock, 0, sizeof(me_sock));
+ me_sock.ss_family = peer_sock.ss_family;
+ me_sock.ss_len = peer_sock.ss_len;
+ }
+ close(0);
+ close(1);
+ peer = socket(peer_sock.ss_family, SOCK_DGRAM, 0);
+ if (peer < 0) {
+ tftp_log(LOG_ERR, "socket: %s", strerror(errno));
+ exit(1);
+ }
+ if (bind(peer, (struct sockaddr *)&me_sock, me_sock.ss_len) < 0) {
+ tftp_log(LOG_ERR, "bind: %s", strerror(errno));
+ exit(1);
+ }
+
+ tp = (struct tftphdr *)recvbuffer;
+ tp->th_opcode = ntohs(tp->th_opcode);
+ if (tp->th_opcode == RRQ) {
+ if (allow_ro)
+ tftp_rrq(peer, tp->th_stuff, n - 1);
+ else {
+ tftp_log(LOG_WARNING,
+ "%s read access denied", peername);
+ exit(1);
+ }
+ }
+ if (tp->th_opcode == WRQ) {
+ if (allow_wo)
+ tftp_wrq(peer, tp->th_stuff, n - 1);
+ else {
+ tftp_log(LOG_WARNING,
+ "%s write access denied", peername);
+ exit(1);
+ }
+ }
+ exit(1);
+}
+
+static void
+reduce_path(char *fn)
+{
+ char *slash, *ptr;
+
+ /* Reduce all "/+./" to "/" (just in case we've got "/./../" later */
+ while ((slash = strstr(fn, "/./")) != NULL) {
+ for (ptr = slash; ptr > fn && ptr[-1] == '/'; ptr--)
+ ;
+ slash += 2;
+ while (*slash)
+ *++ptr = *++slash;
+ }
+
+ /* Now reduce all "/something/+../" to "/" */
+ while ((slash = strstr(fn, "/../")) != NULL) {
+ if (slash == fn)
+ break;
+ for (ptr = slash; ptr > fn && ptr[-1] == '/'; ptr--)
+ ;
+ for (ptr--; ptr >= fn; ptr--)
+ if (*ptr == '/')
+ break;
+ if (ptr < fn)
+ break;
+ slash += 3;
+ while (*slash)
+ *++ptr = *++slash;
+ }
+}
+
+static char *
+parse_header(int peer, char *recvbuffer, ssize_t size,
+ char **filename, char **mode)
+{
+ char *cp;
+ int i;
+ struct formats *pf;
+
+ *mode = NULL;
+ cp = recvbuffer;
+
+ i = get_field(peer, recvbuffer, size);
+ if (i >= PATH_MAX) {
+ tftp_log(LOG_ERR, "Bad option - filename too long");
+ send_error(peer, EBADOP);
+ exit(1);
+ }
+ *filename = recvbuffer;
+ tftp_log(LOG_INFO, "Filename: '%s'", *filename);
+ cp += i;
+
+ i = get_field(peer, cp, size);
+ *mode = cp;
+ cp += i;
+
+ /* Find the file transfer mode */
+ 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 == NULL) {
+ tftp_log(LOG_ERR,
+ "Bad option - Unknown transfer mode (%s)", *mode);
+ send_error(peer, EBADOP);
+ exit(1);
+ }
+ tftp_log(LOG_INFO, "Mode: '%s'", *mode);
+
+ return (cp + 1);
+}
+
+/*
+ * WRQ - receive a file from the client
+ */
+void
+tftp_wrq(int peer, char *recvbuffer, ssize_t size)
+{
+ char *cp;
+ int has_options = 0, ecode;
+ char *filename, *mode;
+ char fnbuf[PATH_MAX];
+
+ cp = parse_header(peer, recvbuffer, size, &filename, &mode);
+ size -= (cp - recvbuffer) + 1;
+
+ strcpy(fnbuf, filename);
+ reduce_path(fnbuf);
+ filename = fnbuf;
+
+ if (size > 0) {
+ if (options_rfc_enabled)
+ has_options = !parse_options(peer, cp, size);
+ else
+ tftp_log(LOG_INFO, "Options found but not enabled");
+ }
+
+ ecode = validate_access(peer, &filename, WRQ);
+ if (ecode == 0) {
+ if (has_options)
+ send_oack(peer);
+ else
+ send_ack(peer, 0);
+ }
+ if (logging) {
+ tftp_log(LOG_INFO, "%s: write request for %s: %s", peername,
+ filename, errtomsg(ecode));
+ }
+
+ tftp_recvfile(peer, mode);
+ exit(0);
+}
+
+/*
+ * RRQ - send a file to the client
+ */
+void
+tftp_rrq(int peer, char *recvbuffer, ssize_t size)
+{
+ char *cp;
+ int has_options = 0, ecode;
+ char *filename, *mode;
+ char fnbuf[PATH_MAX];
+
+ cp = parse_header(peer, recvbuffer, size, &filename, &mode);
+ size -= (cp - recvbuffer) + 1;
+
+ strcpy(fnbuf, filename);
+ reduce_path(fnbuf);
+ filename = fnbuf;
+
+ if (size > 0) {
+ if (options_rfc_enabled)
+ has_options = !parse_options(peer, cp, size);
+ else
+ tftp_log(LOG_INFO, "Options found but not enabled");
+ }
+
+ ecode = validate_access(peer, &filename, RRQ);
+ if (ecode == 0) {
+ if (has_options) {
+ int n;
+ char lrecvbuffer[MAXPKTSIZE];
+ struct tftphdr *rp = (struct tftphdr *)lrecvbuffer;
+
+ send_oack(peer);
+ n = receive_packet(peer, lrecvbuffer, MAXPKTSIZE,
+ NULL, timeoutpacket);
+ if (n < 0) {
+ if (debug&DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG, "Aborting: %s",
+ rp_strerror(n));
+ return;
+ }
+ if (rp->th_opcode != ACK) {
+ if (debug&DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG,
+ "Expected ACK, got %s on OACK",
+ packettype(rp->th_opcode));
+ return;
+ }
+ }
+ }
+
+ if (logging)
+ tftp_log(LOG_INFO, "%s: read request for %s: %s", peername,
+ 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);
+ send_error(peer, ecode);
+ exit(1);
+ }
+ tftp_xmitfile(peer, mode);
+}
+
+/*
+ * Find the next value for YYYYMMDD.nn when the file to be written should
+ * be unique. Due to the limitations of nn, we will fail if nn reaches 100.
+ * Besides, that is four updates per hour on a file, which is kind of
+ * execessive anyway.
+ */
+static int
+find_next_name(char *filename, int *fd)
+{
+ int i;
+ time_t tval;
+ size_t len;
+ struct tm lt;
+ char yyyymmdd[MAXPATHLEN];
+ char newname[MAXPATHLEN];
+
+ /* Create the YYYYMMDD part of the filename */
+ time(&tval);
+ lt = *localtime(&tval);
+ len = strftime(yyyymmdd, sizeof(yyyymmdd), newfile_format, &lt);
+ if (len == 0) {
+ syslog(LOG_WARNING,
+ "Filename suffix too long (%d characters maximum)",
+ MAXPATHLEN);
+ return (EACCESS);
+ }
+
+ /* Make sure the new filename is not too long */
+ if (strlen(filename) > MAXPATHLEN - len - 5) {
+ syslog(LOG_WARNING,
+ "Filename too long (%zd characters, %zd maximum)",
+ strlen(filename), MAXPATHLEN - len - 5);
+ return (EACCESS);
+ }
+
+ /* Find the first file which doesn't exist */
+ for (i = 0; i < 100; i++) {
+ sprintf(newname, "%s.%s.%02d", filename, yyyymmdd, i);
+ *fd = open(newname,
+ O_WRONLY | O_CREAT | O_EXCL,
+ S_IRUSR | S_IWUSR | S_IRGRP |
+ S_IWGRP | S_IROTH | S_IWOTH);
+ if (*fd > 0)
+ return 0;
+ }
+
+ return (EEXIST);
+}
+
+/*
+ * 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(int peer, char **filep, int mode)
+{
+ struct stat stbuf;
+ int fd;
+ int error;
+ 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++) {
+ snprintf(pathname, sizeof(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)
+ *filep = filename = pathname;
+ else if (mode == RRQ)
+ return (err);
+ }
+
+ /*
+ * This option is handled here because it (might) require(s) the
+ * size of the file.
+ */
+ option_tsize(peer, NULL, mode, &stbuf);
+
+ if (mode == RRQ)
+ fd = open(filename, O_RDONLY);
+ else {
+ if (create_new) {
+ if (increase_name) {
+ error = find_next_name(filename, &fd);
+ if (error > 0)
+ return (error + 100);
+ } else
+ fd = open(filename,
+ O_WRONLY | O_TRUNC | O_CREAT,
+ S_IRUSR | S_IWUSR | S_IRGRP |
+ S_IWGRP | S_IROTH | S_IWOTH );
+ } else
+ fd = open(filename, O_WRONLY | O_TRUNC);
+ }
+ if (fd < 0)
+ return (errno + 100);
+ file = fdopen(fd, (mode == RRQ)? "r":"w");
+ if (file == NULL) {
+ close(fd);
+ return (errno + 100);
+ }
+ return (0);
+}
+
+static void
+tftp_xmitfile(int peer, const char *mode)
+{
+ uint16_t block;
+ time_t now;
+ struct tftp_stats ts;
+
+ now = time(NULL);
+ if (debug&DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG, "Transmitting file");
+
+ read_init(0, file, mode);
+ block = 1;
+ tftp_send(peer, &block, &ts);
+ read_close();
+ if (debug&DEBUG_SIMPLE)
+ tftp_log(LOG_INFO, "Sent %jd bytes in %jd seconds",
+ (intmax_t)ts.amount, (intmax_t)time(NULL) - now);
+}
+
+static void
+tftp_recvfile(int peer, const char *mode)
+{
+ uint16_t block;
+ struct timeval now1, now2;
+ struct tftp_stats ts;
+
+ gettimeofday(&now1, NULL);
+ if (debug&DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG, "Receiving file");
+
+ write_init(0, file, mode);
+
+ block = 0;
+ tftp_receive(peer, &block, &ts, NULL, 0);
+
+ write_close();
+ gettimeofday(&now2, NULL);
+
+ if (debug&DEBUG_SIMPLE) {
+ double f;
+ if (now1.tv_usec > now2.tv_usec) {
+ now2.tv_usec += 1000000;
+ now2.tv_sec--;
+ }
+
+ f = now2.tv_sec - now1.tv_sec +
+ (now2.tv_usec - now1.tv_usec) / 100000.0;
+ tftp_log(LOG_INFO,
+ "Download of %jd bytes in %d blocks completed after %0.1f seconds\n",
+ (intmax_t)ts.amount, block, f);
+ }
+
+ return;
+}
diff --git a/libexec/ulog-helper/Makefile b/libexec/ulog-helper/Makefile
new file mode 100644
index 0000000..764f0de
--- /dev/null
+++ b/libexec/ulog-helper/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG= ulog-helper
+BINOWN= root
+BINMODE=4555
+MAN=
+
+DPADD= ${LIBULOG}
+LDADD= -lulog
+
+.include <bsd.prog.mk>
diff --git a/libexec/ulog-helper/ulog-helper.c b/libexec/ulog-helper/ulog-helper.c
new file mode 100644
index 0000000..99cd5d2
--- /dev/null
+++ b/libexec/ulog-helper/ulog-helper.c
@@ -0,0 +1,97 @@
+/*-
+ * Copyright (c) 2009 Ed Schouten <ed@FreeBSD.org>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <pwd.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <ulog.h>
+
+/*
+ * This setuid helper utility writes user login records to disk.
+ * Unprivileged processes are not capable of writing records to utmpx,
+ * but we do want to allow this for pseudo-terminals. Because a file
+ * descriptor to a pseudo-terminal master device can only be obtained by
+ * processes using the pseudo-terminal, we expect such a descriptor on
+ * stdin.
+ *
+ * It uses the real user ID of the calling process to determine the
+ * username. It does allow users to log arbitrary hostnames.
+ */
+
+static const char *
+get_username(void)
+{
+ const struct passwd *pw;
+ const char *login;
+ uid_t uid;
+
+ /*
+ * Attempt to determine the username corresponding to this login
+ * session. First, validate the results of getlogin() against
+ * the password database. If getlogin() returns invalid data,
+ * return an arbitrary username corresponding to this uid.
+ */
+ uid = getuid();
+ if ((login = getlogin()) != NULL && (pw = getpwnam(login)) != NULL &&
+ pw->pw_uid == uid)
+ return (login);
+ if ((pw = getpwuid(uid)) != NULL)
+ return (pw->pw_name);
+ return (NULL);
+}
+
+int
+main(int argc, char *argv[])
+{
+ const char *line, *user, *host;
+
+ /* Device line name. */
+ if ((line = ptsname(STDIN_FILENO)) == NULL)
+ return (EX_USAGE);
+
+ if ((argc == 2 || argc == 3) && strcmp(argv[1], "login") == 0) {
+ /* Username. */
+ user = get_username();
+ if (user == NULL)
+ return (EX_OSERR);
+
+ /* Hostname. */
+ host = argc == 3 ? argv[2] : NULL;
+
+ ulog_login(line, user, host);
+ return (EX_OK);
+ } else if (argc == 2 && strcmp(argv[1], "logout") == 0) {
+ ulog_logout(line);
+ return (EX_OK);
+ }
+
+ return (EX_USAGE);
+}
diff --git a/libexec/ypxfr/Makefile b/libexec/ypxfr/Makefile
new file mode 100644
index 0000000..476a296
--- /dev/null
+++ b/libexec/ypxfr/Makefile
@@ -0,0 +1,47 @@
+# $FreeBSD$
+
+PROG= ypxfr
+SRCS= yp_dblookup.c yp_dbwrite.c yp_error.c \
+ ypxfr_getmap.c ypxfr_main.c ypxfr_misc.c \
+ ypxfrd_getmap.c \
+ ${GENSRCS}
+GENSRCS=yp.h yp_clnt.c ypxfr_clnt.c
+
+.PATH: ${.CURDIR}/../../usr.sbin/ypserv
+
+MAN= ypxfr.8
+
+CFLAGS+= -I.
+
+WARNS?= 2
+WFORMAT=0
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+CLEANFILES= ${GENSRCS}
+
+RPCDIR= ${.CURDIR}/../../include/rpcsvc
+RPCGEN= RPCGEN_CPP=${CPP:Q} rpcgen -I -C
+
+ypxfr_clnt.c: ${RPCDIR}/yp.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -DYPPUSH_ONLY -l -o ${.TARGET} ${RPCDIR}/yp.x
+
+yp_clnt.c: ${RPCDIR}/yp.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -DYPSERV_ONLY -l -o ${.TARGET} ${RPCDIR}/yp.x
+
+yp.h: ${RPCDIR}/yp.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/yp.x
+
+# ypxfrd_xdr.c: ${RPCDIR}/ypxfrd.x
+# rm -f ${.TARGET}
+# ${RPCGEN} -c -o ${.TARGET} ${RPCDIR}/ypxfrd.x
+
+ypxfrd.h: ${RPCDIR}/ypxfrd.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/ypxfrd.x
+
+.include <bsd.prog.mk>
diff --git a/libexec/ypxfr/yp_dbwrite.c b/libexec/ypxfr/yp_dbwrite.c
new file mode 100644
index 0000000..488f9e4
--- /dev/null
+++ b/libexec/ypxfr/yp_dbwrite.c
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <db.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <rpcsvc/yp.h>
+#include "ypxfr_extern.h"
+
+#define PERM_SECURE (S_IRUSR|S_IWUSR)
+
+/*
+ * Open a DB database read/write
+ */
+DB *
+yp_open_db_rw(const char *domain, const char *map, const int flags)
+{
+ DB *dbp;
+ char buf[1025];
+
+
+ yp_errno = YP_TRUE;
+
+ if (map[0] == '.' || strchr(map, '/')) {
+ yp_errno = YP_BADARGS;
+ return (NULL);
+ }
+
+#define FLAGS O_RDWR|O_EXLOCK|O_EXCL|O_CREAT
+
+ snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map);
+ dbp = dbopen(buf,flags ? flags : FLAGS,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(DB *dbp, DBT *key, DBT *data, int allow_overwrite)
+{
+ int rval;
+
+ if ((rval = (dbp->put)(dbp,key,data, allow_overwrite ? 0 :
+ R_NOOVERWRITE))) {
+ switch (rval) {
+ case 1:
+ return(YP_FALSE);
+ break;
+ case -1:
+ default:
+ (void)(dbp->close)(dbp);
+ return(YP_BADDB);
+ break;
+ }
+ }
+
+ return(YP_TRUE);
+}
diff --git a/libexec/ypxfr/ypxfr.8 b/libexec/ypxfr/ypxfr.8
new file mode 100644
index 0000000..3e07586
--- /dev/null
+++ b/libexec/ypxfr/ypxfr.8
@@ -0,0 +1,308 @@
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.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 /usr/libexec/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
+The
+.Nm
+utility copies an
+.Tn NIS
+database (or
+.Pa map )
+from one
+.Tn NIS
+server to another using
+.Tn NIS
+services.
+In
+.Fx ,
+.Nm
+is generally invoked by
+.Xr ypserv 8
+when it receives a map transfer request from
+.Xr yppush 8 .
+The
+.Nm
+utility is used primarily in environments where several
+.Tn NIS
+servers are in use in a single domain.
+One server, the
+.Tn NIS
+master, maintains
+the canonical copies of all
+.Tn NIS
+maps, and all the other servers,
+the
+.Tn 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
+creates a temporary database file in
+.Pa /var/yp/[domainname] ,
+and fills it with the contents of
+.Ar mapname
+as supplied by the specified
+.Ar source host .
+When the entire map has been transferred,
+.Nm
+deletes the original copy of
+.Ar mapname
+and moves the temporary copy into its place.
+When the transfer is
+complete,
+.Nm
+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
+are owner readable and writable only for security reasons.
+Since the
+.Tn NIS
+maps and the directory in which they reside are normally owned by
+root, this prevents non-privileged users from making unauthorized
+modifications.
+.Pp
+In order to maintain consistency across all
+.Tn NIS
+servers,
+.Nm
+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
+.Tn 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
+.Tn NIS
+master server, however it is good practice
+on large networks where possible outages could cause
+.Tn NIS
+servers to fall out of sync with each other.
+.Pp
+When
+.Nm
+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 NOTES
+The
+.Fx
+version of
+.Nm
+has support for a special map transfer protocol which works in
+conjunction with the
+.Fx
+.Xr rpc.ypxfrd 8
+server.
+This protocol allows it to transfer raw map database files from
+the
+.Tn NIS
+master server and can be many times faster than the standard
+transfer method, particularly for very large
+.Tn NIS
+maps.
+The
+.Nm
+utility will check to see if the
+.Xr rpc.ypxfrd 8
+server is registered on the
+.Tn NIS
+master server and attempt to use
+it if it is present.
+If it is not it will fall back to the standard
+transfer method, copying the map contents from
+.Xr ypserv 8
+and creating new maps instead.
+.Pp
+Note that while the
+.Fx
+ypxfrd protocol is conceptually similar
+to the SunOS ypxfrd protocol,
+the
+.Fx
+protocol is not compatible with
+Sun's, therefore it will not work with Sun's ypxfrd server.
+.Fx
+slave systems can still transfer maps from any
+.No non- Ns Fx
+.Tn NIS
+server,
+however they will only be able to take advantage of the faster protocol
+if the master server is also running
+.Fx .
+.Sh OPTIONS
+The following options and flags are supported by
+.Nm :
+.Bl -tag -width indent
+.It Fl f
+Force a map transfer.
+Normally,
+.Nm
+will not transfer a map if it determines that the
+.Tn 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
+manually on a machine that is not yet running
+.Xr ypserv 8 .
+Without this flag, failure to contact the local
+.Tn NIS
+server will cause
+.Nm
+to abort the transfer.
+.It Fl d Ar target domain
+Specify a target domain other than the current
+.Tn NIS
+domain.
+.It Fl h Ar source host
+Specify the name of the host from which to copy the
+.Tn NIS
+maps.
+This option
+is used to ensure that
+.Nm
+only copies maps from the
+.Tn 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
+.Tn NIS
+domains.
+.It Fl p Ar path
+Specify the top level directory containing the
+.Tn 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
+.Tn NIS
+maps in a different part of the file system.
+The
+.Tn NIS
+server,
+.Xr ypserv 8 ,
+passes this flag to
+.Nm
+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
+is invoked by
+.Xr ypserv 8
+in response to a map transfer request initiated by
+.Xr yppush 8 .
+In this instance,
+.Nm
+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
+.Tn NIS
+maps for a particular
+.Tn NIS
+domain.
+.El
+.Sh SEE ALSO
+.Xr yp 8 ,
+.Xr yppush 8 ,
+.Xr ypserv 8
+.Sh AUTHORS
+.An Bill Paul Aq Mt wpaul@ctr.columbia.edu
diff --git a/libexec/ypxfr/ypxfr_extern.h b/libexec/ypxfr/ypxfr_extern.h
new file mode 100644
index 0000000..f843b64
--- /dev/null
+++ b/libexec/ypxfr/ypxfr_extern.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+#include <sys/types.h>
+#include <limits.h>
+#include <paths.h>
+#include <db.h>
+#include <rpcsvc/yp.h>
+
+extern HASHINFO openinfo;
+extern BTREEINFO openinfo_b;
+
+#ifndef _PATH_YP
+#define _PATH_YP "/var/yp/"
+#endif
+
+extern char *yp_dir;
+extern int debug;
+extern enum ypstat yp_errno;
+extern void yp_error(const char *, ...);
+extern int _yp_check(char **);
+extern const char *ypxfrerr_string(ypxfrstat);
+extern DB *yp_open_db_rw(const char *, const char *, const int);
+extern void yp_init_dbs(void);
+extern int yp_put_record(DB *, DBT *, DBT *, int);
+extern int yp_get_record(const char *, const char *, const DBT *, DBT *, int);
+extern int ypxfr_get_map(char *, char *, char *, int (*)(int, char *, int, char *, int, char*));
+extern char *ypxfr_get_master(char *, char *, char *, const int);
+extern unsigned long ypxfr_get_order(char *, char *, char *, const int);
+extern int ypxfr_match(char *, char *, char *, char *, unsigned long);
+extern char *ypxfxerr_string(ypxfrstat);
+extern int ypxfrd_get_map(char *, char *, char *, char *);
diff --git a/libexec/ypxfr/ypxfr_getmap.c b/libexec/ypxfr/ypxfr_getmap.c
new file mode 100644
index 0000000..4b8794f
--- /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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <time.h>
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpcsvc/yp.h>
+#include "ypxfr_extern.h"
+
+extern bool_t xdr_ypresp_all_seq(XDR *, unsigned long *);
+
+int (*ypresp_allfn)();
+void *ypresp_data;
+extern DB *specdbp;
+extern enum ypstat 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(char *map, char *domain, char *host,
+ int (*callback)(int, char *, int, char *, int, char*))
+{
+ 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 \
+create tcp handle"));
+ yp_errno = (enum ypstat)YPXFR_YPERR;
+ return(1);
+ }
+
+ req.domain = domain;
+ req.map = map;
+ ypresp_allfn = callback;
+ ypresp_data = NULL;
+
+ (void)clnt_call(clnt, YPPROC_ALL, (xdrproc_t)xdr_ypreq_nokey, &req,
+ (xdrproc_t)xdr_ypresp_all_seq, &status, timeout);
+
+ clnt_destroy(clnt);
+
+ if (status == YP_NOMORE)
+ return(0);
+
+ if (status != YP_TRUE) {
+ yp_errno = (enum ypstat)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..70fd1f5
--- /dev/null
+++ b/libexec/ypxfr/ypxfr_main.c
@@ -0,0 +1,577 @@
+/*
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.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 <rpc/clnt.h>
+#include <rpcsvc/yp.h>
+#include <rpcsvc/ypclnt.h>
+#include <rpcsvc/ypxfrd.h>
+#include "ypxfr_extern.h"
+
+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(ypxfrstat retval, char *temp)
+{
+ CLIENT *clnt;
+ int sock = RPC_ANYSOCK;
+ struct timeval timeout;
+
+ /* Clean up no matter what happened previously. */
+ if (temp != NULL) {
+ if (dbp != NULL)
+ (void)(dbp->close)(dbp);
+ if (unlink(temp) == -1) {
+ yp_error("failed to unlink %s",strerror(errno));
+ }
+ }
+
+ if (ypxfr_prognum) {
+ 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 = (yppush_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(void)
+{
+ if (_rpcpmstart) {
+ ypxfr_exit(YPXFR_BADARGS,NULL);
+ } else {
+ fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: ypxfr [-f] [-c] [-d target domain] [-h source host]",
+ " [-s source domain] [-p path]",
+ " [-C taskid program-number ipaddr port] mapname");
+ exit(1);
+ }
+}
+
+int
+ypxfr_foreach(int status, char *key, int keylen, char *val, int vallen,
+ char *data)
+{
+ DBT dbkey, dbval;
+
+ if (status != YP_TRUE)
+ return (status);
+
+ /*
+ * XXX Do not attempt to write zero-length keys or
+ * data into a Berkeley DB hash database. It causes a
+ * strange failure mode where sequential searches get
+ * caught in an infinite loop.
+ */
+ if (keylen) {
+ dbkey.data = key;
+ dbkey.size = keylen;
+ } else {
+ dbkey.data = "";
+ dbkey.size = 1;
+ }
+ if (vallen) {
+ dbval.data = val;
+ dbval.size = vallen;
+ } else {
+ dbval.data = "";
+ dbval.size = 1;
+ }
+
+ if (yp_put_record(dbp, &dbkey, &dbval, 0) != YP_TRUE)
+ return(yp_errno);
+
+ return (0);
+}
+
+int
+main(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;
+ int remoteport;
+ int interdom = 0;
+ int secure = 0;
+
+ debug = 1;
+
+ if (!isatty(fileno(stderr))) {
+ openlog("ypxfr", LOG_PID, LOG_DAEMON);
+ _rpcpmstart = 1;
+ }
+
+ if (argc < 2)
+ usage();
+
+ while ((ch = getopt(argc, argv, "fcd:h:s:p:C:")) != -1) {
+ 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: %s",
+ ypxfr_mapname, ypxfr_source_domain,
+ ypxfrerr_string((ypxfrstat)yp_errno));
+ 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;
+
+ /*
+ * Don't talk to ypservs on unprivileged ports.
+ */
+ remoteport = getrpcport(ypxfr_source_host, YPPROG, YPVERS, IPPROTO_UDP);
+ if (remoteport >= IPPORT_RESERVED) {
+ yp_error("ypserv on %s not running on reserved port",
+ ypxfr_source_host);
+ ypxfr_exit(YPXFR_REFUSED, NULL);
+ }
+
+ if ((ypxfr_order = ypxfr_get_order(ypxfr_source_domain,
+ ypxfr_mapname,
+ ypxfr_master, 0)) == 0) {
+ yp_error("failed to get order number of %s: %s",
+ ypxfr_mapname, yp_errno == YPXFR_SUCC ?
+ "map has order 0" :
+ ypxfrerr_string((ypxfrstat)yp_errno));
+ ypxfr_exit(YPXFR_YPERR,NULL);
+ }
+
+ if (ypxfr_match(ypxfr_master, ypxfr_source_domain, ypxfr_mapname,
+ "YP_INTERDOMAIN", sizeof("YP_INTERDOMAIN") - 1))
+ interdom++;
+
+ if (ypxfr_match(ypxfr_master, ypxfr_source_domain, ypxfr_mapname,
+ "YP_SECURE", sizeof("YP_SECURE") - 1))
+ secure++;
+
+ 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);
+
+ if ((remoteport = getrpcport(ypxfr_source_host, YPXFRD_FREEBSD_PROG,
+ YPXFRD_FREEBSD_VERS, IPPROTO_TCP))) {
+
+ /* Don't talk to rpc.ypxfrds on unprovileged ports. */
+ if (remoteport >= IPPORT_RESERVED) {
+ yp_error("rpc.ypxfrd on %s not using privileged port",
+ ypxfr_source_host);
+ ypxfr_exit(YPXFR_REFUSED, NULL);
+ }
+
+ /* Try to send using ypxfrd. If it fails, use old method. */
+ if (!ypxfrd_get_map(ypxfr_source_host, ypxfr_mapname,
+ ypxfr_source_domain, ypxfr_temp_map))
+ goto leave;
+ }
+
+ /* Open the temporary map read/write. */
+ if ((dbp = yp_open_db_rw(ypxfr_dest_domain, tempmap, 0)) == 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), "%lu", ypxfr_order);
+ data.data = buf;
+ data.size = strlen(buf);
+
+ if (yp_put_record(dbp, &key, &data, 0) != 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, 0) != 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, 0) != 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, 0) != 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, 0) != YP_TRUE) {
+ yp_error("failed to write output name to database");
+ ypxfr_exit(YPXFR_DBM,ypxfr_temp_map);
+ }
+
+ if (interdom) {
+ key.data = "YP_INTERDOMAIN";
+ key.size = sizeof("YP_INTERDOMAIN") - 1;
+ data.data = "";
+ data.size = 0;
+
+ if (yp_put_record(dbp, &key, &data, 0) != YP_TRUE) {
+ yp_error("failed to add interdomain flag to database");
+ ypxfr_exit(YPXFR_DBM,ypxfr_temp_map);
+ }
+ }
+
+ if (secure) {
+ key.data = "YP_SECURE";
+ key.size = sizeof("YP_SECURE") - 1;
+ data.data = "";
+ data.size = 0;
+
+ if (yp_put_record(dbp, &key, &data, 0) != YP_TRUE) {
+ yp_error("failed to add secure flag 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);
+ dbp = NULL; /* <- yes, it seems this is necessary. */
+
+leave:
+
+ snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, ypxfr_dest_domain,
+ ypxfr_mapname);
+
+ /* 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: %s",
+ ypxfr_mapname, yp_errno == YPXFR_SUCC ?
+ "map has order 0" :
+ ypxfrerr_string((ypxfrstat)yp_errno));
+ 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.
+ */
+ if (ypxfr_clear) {
+ char in = 0;
+ char *out = NULL;
+ int stat;
+ if ((stat = callrpc("localhost",YPPROG,YPVERS,YPPROC_CLEAR,
+ (xdrproc_t)xdr_void, (void *)&in,
+ (xdrproc_t)xdr_void, (void *)out)) != RPC_SUCCESS) {
+ yp_error("failed to send 'clear' to local ypserv: %s",
+ clnt_sperrno((enum clnt_stat) stat));
+ ypxfr_exit(YPXFR_CLEAR, ypxfr_temp_map);
+ }
+ }
+
+ /*
+ * Put the new map in place immediately. I'm not sure if the
+ * kernel does an unlink() and rename() atomically in the event
+ * that we move a new copy of a map over the top of an existing
+ * one, but there's less chance of a race condition happening
+ * than if we were to do the unlink() ourselves.
+ */
+ 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..323fd53
--- /dev/null
+++ b/libexec/ypxfr/ypxfr_misc.c
@@ -0,0 +1,293 @@
+/*
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/yp.h>
+#include <rpcsvc/ypclnt.h>
+#include "ypxfr_extern.h"
+
+const char *
+ypxfrerr_string(ypxfrstat code)
+{
+ switch (code) {
+ case YPXFR_SUCC:
+ return ("Map successfully transferred");
+ 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).
+ */
+
+/*
+ * Careful: yp_master() returns a pointer to a dynamically allocated
+ * buffer. Calling ypproc_master_2() ourselves also returns a pointer
+ * to dynamically allocated memory, though this time it's memory
+ * allocated by the XDR routines. We have to rememver to free() or
+ * xdr_free() the memory as required to avoid leaking memory.
+ */
+char *
+ypxfr_get_master(char *domain, char *map, char *source, const int yplib)
+{
+ static char mastername[MAXPATHLEN + 2];
+
+ bzero((char *)&mastername, sizeof(mastername));
+
+ if (yplib) {
+ int res;
+ char *master;
+ if ((res = yp_master(domain, map, &master))) {
+ switch (res) {
+ case YPERR_DOMAIN:
+ yp_errno = (enum ypstat)YPXFR_NODOM;
+ break;
+ case YPERR_MAP:
+ yp_errno = (enum ypstat)YPXFR_NOMAP;
+ break;
+ case YPERR_YPERR:
+ default:
+ yp_errno = (enum ypstat)YPXFR_YPERR;
+ break;
+ }
+ return(NULL);
+ } else {
+ snprintf(mastername, sizeof(mastername), "%s", master);
+ free(master);
+ return((char *)&mastername);
+ }
+ } 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 = (enum ypstat)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 = (enum ypstat)YPXFR_RPC;
+ return(NULL);
+ }
+ clnt_destroy(clnt);
+ if (resp->stat != YP_TRUE) {
+ switch (resp->stat) {
+ case YP_NODOM:
+ yp_errno = (enum ypstat)YPXFR_NODOM;
+ break;
+ case YP_NOMAP:
+ yp_errno = (enum ypstat)YPXFR_NOMAP;
+ break;
+ case YP_YPERR:
+ default:
+ yp_errno = (enum ypstat)YPXFR_YPERR;
+ break;
+ }
+ return(NULL);
+ }
+ snprintf(mastername, sizeof(mastername), "%s", resp->peer);
+/* xdr_free(xdr_ypresp_master, (char *)&resp); */
+ return((char *)&mastername);
+ }
+}
+
+unsigned long
+ypxfr_get_order(char *domain, char *map, char *source, const int yplib)
+{
+ if (yplib) {
+ unsigned int order;
+ int res;
+ if ((res = yp_order(domain, map, &order))) {
+ switch (res) {
+ case YPERR_DOMAIN:
+ yp_errno = (enum ypstat)YPXFR_NODOM;
+ break;
+ case YPERR_MAP:
+ yp_errno = (enum ypstat)YPXFR_NOMAP;
+ break;
+ case YPERR_YPERR:
+ default:
+ yp_errno = (enum ypstat)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 = (enum ypstat)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 = (enum ypstat)YPXFR_RPC;
+ return(0);
+ }
+ clnt_destroy(clnt);
+ if (resp->stat != YP_TRUE) {
+ switch (resp->stat) {
+ case YP_NODOM:
+ yp_errno = (enum ypstat)YPXFR_NODOM;
+ break;
+ case YP_NOMAP:
+ yp_errno = (enum ypstat)YPXFR_NOMAP;
+ break;
+ case YP_YPERR:
+ default:
+ yp_errno = (enum ypstat)YPXFR_YPERR;
+ break;
+ }
+ return(0);
+ }
+ return(resp->ordernum);
+ }
+}
+
+int
+ypxfr_match(char *server, char *domain, char *map, char *key,
+ unsigned long keylen)
+{
+ ypreq_key ypkey;
+ ypresp_val *ypval;
+ CLIENT *clnt;
+ static char buf[YPMAXRECORD + 2];
+
+ bzero(buf, sizeof(buf));
+
+ if ((clnt = clnt_create(server, YPPROG,YPVERS,"udp")) == NULL) {
+ yp_error("failed to create UDP handle: %s",
+ clnt_spcreateerror(server));
+ return(0);
+ }
+
+ ypkey.domain = domain;
+ ypkey.map = map;
+ ypkey.key.keydat_len = keylen;
+ ypkey.key.keydat_val = key;
+
+ if ((ypval = ypproc_match_2(&ypkey, clnt)) == NULL) {
+ clnt_destroy(clnt);
+ yp_error("%s: %s", server,
+ clnt_sperror(clnt,"YPPROC_MATCH failed"));
+ return(0);
+ }
+
+ clnt_destroy(clnt);
+
+ if (ypval->stat != YP_TRUE) {
+ xdr_free((xdrproc_t)xdr_ypresp_val, ypval);
+ return(0);
+ }
+
+ xdr_free((xdrproc_t)xdr_ypresp_val, ypval);
+
+ return(1);
+}
diff --git a/libexec/ypxfr/ypxfrd_getmap.c b/libexec/ypxfr/ypxfrd_getmap.c
new file mode 100644
index 0000000..b1424ac
--- /dev/null
+++ b/libexec/ypxfr/ypxfrd_getmap.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 1995, 1996
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <rpcsvc/ypxfrd.h>
+#include <rpcsvc/yp.h>
+#include <rpc/rpc.h>
+#include <sys/uio.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "ypxfr_extern.h"
+
+int fp = 0;
+
+static bool_t
+xdr_my_xfr(register XDR *xdrs, xfr *objp)
+{
+ while (1) {
+ if (!xdr_xfr(xdrs, objp))
+ return(FALSE);
+ if (objp->ok == TRUE) {
+ if (write(fp, objp->xfr_u.xfrblock_buf.xfrblock_buf_val,
+ objp->xfr_u.xfrblock_buf.xfrblock_buf_len) == -1) {
+ yp_error("write failed: %s", strerror(errno));
+ return(FALSE);
+ }
+ }
+ xdr_free((xdrproc_t)xdr_xfr, (char *)objp);
+ if (objp->ok == FALSE) {
+ switch (objp->xfr_u.xfrstat) {
+ case(XFR_DONE):
+ return(TRUE);
+ break;
+ case(XFR_READ_ERR):
+ yp_error("got read error from rpc.ypxfrd");
+ return(FALSE);
+ break;
+ case(XFR_ACCESS):
+ yp_error("rpc.ypxfrd couldn't access the map");
+ return(FALSE);
+ break;
+ case(XFR_DENIED):
+ yp_error("access to map denied by rpc.ypxfrd");
+ return(FALSE);
+ break;
+ case(XFR_DB_TYPE_MISMATCH):
+ yp_error("client/server DB type mismatch");
+ return(FALSE);
+ break;
+ case(XFR_DB_ENDIAN_MISMATCH):
+ yp_error("client/server byte order mismatch");
+ return(FALSE);
+ break;
+ default:
+ yp_error("got unknown status from rpc.ypxfrd");
+ return(FALSE);
+ break;
+ }
+ }
+ }
+}
+
+#define PERM_SECURE (S_IRUSR|S_IWUSR)
+
+int
+ypxfrd_get_map(char *host, char *map, char *domain, char *tmpname)
+{
+ CLIENT *clnt;
+ struct ypxfr_mapname req;
+ struct xfr resp;
+ struct timeval timeout = { 0, 25 };
+ int status = 0;
+
+ req.xfrmap = map;
+ req.xfrdomain = domain;
+ req.xfrmap_filename = "";
+ req.xfr_db_type = XFR_DB_BSD_HASH; /*
+ req.xfr_byte_order = XFR_ENDIAN_ANY; * Berkeley DB isn't
+ * byte-order sensitive.
+ */
+
+ bzero((char *)&resp, sizeof(resp));
+
+ if ((clnt = clnt_create(host, YPXFRD_FREEBSD_PROG,
+ YPXFRD_FREEBSD_VERS, "tcp")) == NULL) {
+ return(1);
+ }
+
+ if ((fp = open(tmpname, O_RDWR|O_CREAT, PERM_SECURE)) == -1) {
+ clnt_destroy(clnt);
+ yp_error("couldn't open %s: %s", tmpname, strerror(errno));
+ return(1);
+ }
+
+ if (clnt_call(clnt,YPXFRD_GETMAP,
+ (xdrproc_t)xdr_ypxfr_mapname, (char *)&req,
+ (xdrproc_t)xdr_my_xfr, (char *)&resp,
+ timeout) != RPC_SUCCESS) {
+ yp_error("%s", clnt_sperror(clnt,"call to rpc.ypxfrd failed"));
+ status++;
+ unlink(tmpname);
+ }
+
+ clnt_destroy(clnt);
+ close(fp);
+ return(status);
+}
OpenPOWER on IntegriCloud