diff options
Diffstat (limited to 'libexec')
221 files changed, 32132 insertions, 3343 deletions
diff --git a/libexec/Makefile b/libexec/Makefile index 90d75f0..b38f970 100644 --- a/libexec/Makefile +++ b/libexec/Makefile @@ -1,10 +1,28 @@ # @(#)Makefile 8.1 (Berkeley) 6/4/93 +# $Id: Makefile,v 1.24 1997/02/22 14:20:52 peter Exp $ -SUBDIR= bugfiler comsat fingerd ftpd getNAME getty kpasswdd lfs_cleanerd \ - mail.local makekey rexecd rlogind rshd talkd telnetd tftpd uucpd +SUBDIR= atrun bootpd comsat fingerd ftpd getNAME getty lfs_cleanerd \ + mail.local makekey mknetid named-xfer revnetgroup rexecd rlogind \ + rpc.rquotad \ + rpc.rstatd rpc.rusersd rpc.rwalld rpc.sprayd rshd talkd tftpd uucpd \ + xtend ypxfr + +.if !exists(${.CURDIR}/../eBones) || defined(NOSECURE) || !defined(MAKE_EBONES) +SUBDIR+=telnetd +.else +.if defined(RELEASEDIR) +# make release needs both +SUBDIR+=telnetd +.endif +SUBDIR+= ../eBones/libexec/telnetd +.endif + +# Present but disabled: kpasswdd .if ${MACHINE} == "hp300" SUBDIR+=rbootd +.elif ${MACHINE} == "i386" +SUBDIR+=rbootd .elif ${MACHINE} == "luna68k" SUBDIR+=rbootd .endif diff --git a/libexec/atrun/LEGAL b/libexec/atrun/LEGAL new file mode 100644 index 0000000..2cd9453 --- /dev/null +++ b/libexec/atrun/LEGAL @@ -0,0 +1,31 @@ +# $Id$ + +-----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..118bed1 --- /dev/null +++ b/libexec/atrun/Makefile @@ -0,0 +1,26 @@ +# $Id$ + +MAINSRC= ${.CURDIR}/../../usr.bin/at + +.include "$(MAINSRC)/Makefile.inc" + +PROG= atrun +MAN8= atrun.8 +SRCS= atrun.c gloadavg.c + +BINDIR= $(ATLIB_DIR) +MANSRC= . +CLEANFILES += ${MAN8} +MANDEPEND = ${MAN8} + +CFLAGS+= -I$(MAINSRC) -I${.CURDIR} + +${MAN8}: atrun.man + sed -e \ + "s@_ATSPOOL_DIR@$(ATSPOOL_DIR)@g; \ + s@_ATJOB_DIR@$(ATJOB_DIR)@g; \ + s@_ATLIB_DIR@$(ATLIB_DIR)@g; \ + s@_LOADAVG_MX@$(LOADAVG_MX)@g;" \ + < $? > $@ + +.include <bsd.prog.mk> diff --git a/libexec/atrun/atrun.c b/libexec/atrun/atrun.c new file mode 100644 index 0000000..b96969d --- /dev/null +++ b/libexec/atrun/atrun.c @@ -0,0 +1,486 @@ +/* + * atrun.c - run jobs queued by at; run with root privileges. + * Copyright (C) 1993, 1994 Thomas Koenig + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author(s) may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* System Headers */ + +#include <sys/fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/param.h> +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <pwd.h> +#include <grp.h> +#include <signal.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <syslog.h> +#include <utmp.h> +#ifdef __FreeBSD__ +#include <paths.h> +#else +#include <getopt.h> +#endif + +#if (MAXLOGNAME-1) > UT_NAMESIZE +#define LOGNAMESIZE UT_NAMESIZE +#else +#define LOGNAMESIZE (MAXLOGNAME-1) +#endif + +/* Local headers */ + +#include "gloadavg.h" +#define MAIN +#include "privs.h" + +/* Macros */ + +#ifndef ATJOB_DIR +#define ATJOB_DIR "/usr/spool/atjobs/" +#endif + +#ifndef ATSPOOL_DIR +#define ATSPOOL_DIR "/usr/spool/atspool/" +#endif + +#ifndef LOADAVG_MX +#define LOADAVG_MX 1.5 +#endif + +/* File scope variables */ + +static char *namep; +static char rcsid[] = "$Id: atrun.c,v 1.9 1997/03/28 15:48:03 imp Exp $"; +static debug = 0; + +void perr(const char *a); + +/* Local functions */ +static int +write_string(int fd, const char* a) +{ + return write(fd, a, strlen(a)); +} + +#undef DEBUG_FORK +#ifdef DEBUG_FORK +static pid_t +myfork() +{ + pid_t res; + res = fork(); + if (res == 0) + kill(getpid(),SIGSTOP); + return res; +} + +#define fork myfork +#endif + +static void +run_file(const char *filename, uid_t uid, gid_t gid) +{ +/* Run a file by by spawning off a process which redirects I/O, + * spawns a subshell, then waits for it to complete and sends + * mail to the user. + */ + pid_t pid; + int fd_out, fd_in; + int queue; + char mailbuf[LOGNAMESIZE + 1], fmt[49]; + char *mailname = NULL; + FILE *stream; + int send_mail = 0; + struct stat buf, lbuf; + off_t size; + struct passwd *pentry; + int fflags; + long nuid; + long ngid; + + + PRIV_START + + if (chmod(filename, S_IRUSR) != 0) + { + perr("Cannot change file permissions"); + } + + PRIV_END + + pid = fork(); + if (pid == -1) + perr("Cannot fork"); + + else if (pid != 0) + return; + + /* Let's see who we mail to. Hopefully, we can read it from + * the command file; if not, send it to the owner, or, failing that, + * to root. + */ + + pentry = getpwuid(uid); + if (pentry == NULL) + { + syslog(LOG_ERR,"Userid %lu not found - aborting job %s", + (unsigned long) uid, filename); + exit(EXIT_FAILURE); + } + PRIV_START + + stream=fopen(filename, "r"); + + PRIV_END + +#ifdef __FreeBSD__ + if (pentry->pw_expire && time(NULL) >= pentry->pw_expire) + { + syslog(LOG_ERR, "Userid %lu is expired - aborting job %s", + (unsigned long) uid, filename); + exit(EXIT_FAILURE); + } +#endif + + if (stream == NULL) + perr("Cannot open input file"); + + if ((fd_in = dup(fileno(stream))) <0) + perr("Error duplicating input file descriptor"); + + if (fstat(fd_in, &buf) == -1) + perr("Error in fstat of input file descriptor"); + + if (lstat(filename, &lbuf) == -1) + perr("Error in fstat of input file"); + + if (S_ISLNK(lbuf.st_mode)) { + syslog(LOG_ERR,"Symbolic link encountered in job %s - aborting", + filename); + exit(EXIT_FAILURE); + } + if ((lbuf.st_dev != buf.st_dev) || (lbuf.st_ino != buf.st_ino) || + (lbuf.st_uid != buf.st_uid) || (lbuf.st_gid != buf.st_gid) || + (lbuf.st_size!=buf.st_size)) { + syslog(LOG_ERR,"Somebody changed files from under us for job %s - " + "aborting",filename); + exit(EXIT_FAILURE); + } + if (buf.st_nlink > 1) { + syslog(LOG_ERR,"Someboy is trying to run a linked script for job %s", + filename); + exit(EXIT_FAILURE); + } + if ((fflags = fcntl(fd_in, F_GETFD)) <0) + perr("Error in fcntl"); + + fcntl(fd_in, F_SETFD, fflags & ~FD_CLOEXEC); + + snprintf(fmt, 49, "#!/bin/sh\n# atrun uid=%%ld gid=%%ld\n# mail %%%ds %%d", + LOGNAMESIZE); + if (fscanf(stream, fmt, &nuid, &ngid, mailbuf, &send_mail) != 4) { + syslog(LOG_ERR,"File %s is in wrong format - aborting", filename); + exit(EXIT_FAILURE); + } + if (mailbuf[0] == '-') { + syslog(LOG_ERR,"illegal mail name %s in %s",mailbuf,filename); + exit(EXIT_FAILURE); + } + mailname = mailbuf; + if (nuid != uid) { + syslog(LOG_ERR,"Job %s - userid %d does not match file uid %d", + filename, nuid, uid); + exit(EXIT_FAILURE); + } + if (ngid != gid) { + syslog(LOG_ERR,"Job %s - groupid %d does not match file gid %d", + filename, ngid, gid); + exit(EXIT_FAILURE); + } + fclose(stream); + if (chdir(ATSPOOL_DIR) < 0) + perr("Cannot chdir to " ATSPOOL_DIR); + + /* Create a file to hold the output of the job we are about to run. + * Write the mail header. + */ + if((fd_out=open(filename, + O_WRONLY | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR)) < 0) + perr("Cannot create output file"); + + write_string(fd_out, "Subject: Output from your job "); + write_string(fd_out, filename); + write_string(fd_out, "\n\n"); + fstat(fd_out, &buf); + size = buf.st_size; + + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + + pid = fork(); + if (pid < 0) + perr("Error in fork"); + + else if (pid == 0) + { + char *nul = NULL; + char **nenvp = &nul; + + /* Set up things for the child; we want standard input from the input file, + * and standard output and error sent to our output file. + */ + + if (lseek(fd_in, (off_t) 0, SEEK_SET) < 0) + perr("Error in lseek"); + + if (dup(fd_in) != STDIN_FILENO) + perr("Error in I/O redirection"); + + if (dup(fd_out) != STDOUT_FILENO) + perr("Error in I/O redirection"); + + if (dup(fd_out) != STDERR_FILENO) + perr("Error in I/O redirection"); + + close(fd_in); + close(fd_out); + if (chdir(ATJOB_DIR) < 0) + perr("Cannot chdir to " ATJOB_DIR); + + queue = *filename; + + PRIV_START + + nice(tolower(queue) - 'a'); + + if (chdir(pentry->pw_dir)) + chdir("/"); + + if (initgroups(pentry->pw_name,pentry->pw_gid)) + perr("Cannot delete saved userids"); + + if (setgid(gid) < 0) + perr("Cannot change group"); + + if (setuid(uid) < 0) + perr("Cannot set user id"); + + if(execle("/bin/sh","sh",(char *) NULL, nenvp) != 0) + perr("Exec failed for /bin/sh"); + + PRIV_END + } + /* We're the parent. Let's wait. + */ + close(fd_in); + close(fd_out); + waitpid(pid, (int *) NULL, 0); + + /* Send mail. Unlink the output file first, so it is deleted after + * the run. + */ + stat(filename, &buf); + if (open(filename, O_RDONLY) != STDIN_FILENO) + perr("Open of jobfile failed"); + + unlink(filename); + if ((buf.st_size != size) || send_mail) + { + PRIV_START + + if (chdir(pentry->pw_dir)) + chdir("/"); + + if (initgroups(pentry->pw_name,pentry->pw_gid)) + perr("Cannot delete saved userids"); + + if (setgid(gid) < 0) + perr("Cannot change group"); + + if (setuid(uid) < 0) + perr("Cannot set user id"); + +#ifdef __FreeBSD__ + execl(_PATH_SENDMAIL, "sendmail", "-F", "Atrun Service", + "-odi", "-oem", + mailname, (char *) NULL); +#else + execl(MAIL_CMD, MAIL_CMD, mailname, (char *) NULL); +#endif + perr("Exec failed for mail command"); + + PRIV_END + } + exit(EXIT_SUCCESS); +} + +/* Global functions */ + +/* Needed in gloadavg.c */ +void +perr(const char *a) +{ + if (debug) + { + perror(a); + } + else + syslog(LOG_ERR, "%s: %m", a); + + exit(EXIT_FAILURE); +} + +int +main(int argc, char *argv[]) +{ +/* Browse through ATJOB_DIR, checking all the jobfiles wether they should + * be executed and or deleted. The queue is coded into the first byte of + * the job filename, the date (in minutes since Eon) as a hex number in the + * following eight bytes, followed by a dot and a serial number. A file + * which has not been executed yet is denoted by its execute - bit set. + * For those files which are to be executed, run_file() is called, which forks + * off a child which takes care of I/O redirection, forks off another child + * for execution and yet another one, optionally, for sending mail. + * Files which already have run are removed during the next invocation. + */ + DIR *spool; + struct dirent *dirent; + struct stat buf; + unsigned long ctm; + unsigned long jobno; + char queue; + time_t now, run_time; + char batch_name[] = "Z2345678901234"; + uid_t batch_uid; + gid_t batch_gid; + int c; + int run_batch; + double load_avg = LOADAVG_MX; + +/* We don't need root privileges all the time; running under uid and gid daemon + * is fine. + */ + + RELINQUISH_PRIVS_ROOT(DAEMON_UID, DAEMON_GID) + + openlog("atrun", LOG_PID, LOG_CRON); + + opterr = 0; + errno = 0; + while((c=getopt(argc, argv, "dl:"))!= -1) + { + switch (c) + { + case 'l': + if (sscanf(optarg, "%lf", &load_avg) != 1) + perr("garbled option -l"); + if (load_avg <= 0.) + load_avg = LOADAVG_MX; + break; + + case 'd': + debug ++; + break; + + case '?': + perr("unknown option"); + break; + + default: + perr("idiotic option - aborted"); + break; + } + } + + namep = argv[0]; + if (chdir(ATJOB_DIR) != 0) + perr("Cannot change to " ATJOB_DIR); + + /* Main loop. Open spool directory for reading and look over all the + * files in there. If the filename indicates that the job should be run + * and the x bit is set, fork off a child which sets its user and group + * id to that of the files and exec a /bin/sh which executes the shell + * script. Unlink older files if they should no longer be run. For + * deletion, their r bit has to be turned on. + * + * Also, pick the oldest batch job to run, at most one per invocation of + * atrun. + */ + if ((spool = opendir(".")) == NULL) + perr("Cannot read " ATJOB_DIR); + + now = time(NULL); + run_batch = 0; + batch_uid = (uid_t) -1; + batch_gid = (gid_t) -1; + + while ((dirent = readdir(spool)) != NULL) { + if (stat(dirent->d_name,&buf) != 0) + perr("Cannot stat in " ATJOB_DIR); + + /* We don't want directories + */ + if (!S_ISREG(buf.st_mode)) + continue; + + if (sscanf(dirent->d_name,"%c%5lx%8lx",&queue,&jobno,&ctm) != 3) + continue; + + run_time = (time_t) ctm*60; + + if ((S_IXUSR & buf.st_mode) && (run_time <=now)) { + if (isupper(queue) && (strcmp(batch_name,dirent->d_name) > 0)) { + run_batch = 1; + strncpy(batch_name, dirent->d_name, sizeof(batch_name)); + batch_uid = buf.st_uid; + batch_gid = buf.st_gid; + } + + /* The file is executable and old enough + */ + if (islower(queue)) + run_file(dirent->d_name, buf.st_uid, buf.st_gid); + } + /* Delete older files + */ + if ((run_time < now) && !(S_IXUSR & buf.st_mode) && (S_IRUSR & buf.st_mode)) + unlink(dirent->d_name); + } + /* run the single batch file, if any + */ + if (run_batch && (gloadavg() < load_avg)) + run_file(batch_name, batch_uid, batch_gid); + + closelog(); + exit(EXIT_SUCCESS); +} diff --git a/libexec/atrun/atrun.man b/libexec/atrun/atrun.man new file mode 100644 index 0000000..b33de68 --- /dev/null +++ b/libexec/atrun/atrun.man @@ -0,0 +1,66 @@ +.\" $Id$ +.Dd April 12, 1995 +.Dt ATRUN 8 +.Os "FreeBSD 2.1" +.Sh NAME +.Nm atrun +.Nd run jobs queued for later execution +.Sh SYNOPSIS +.Nm atrun +.Op Fl l Ar load_avg +.Op Fl d +.Sh DESCRIPTION +.Nm Atrun +runs jobs queued by +.Xr at 1 . +Root's +.Xr crontab 5 +file +.Pa /etc/crontab +has to contain the line +.nf +*/5 * * * * root _ATLIB_DIR/atrun +.fi +so +.Xr atrun 8 +gets called every five minutes. +.Pp +At every invocation, every job in lowercase queues whose starting time +has passed is started. +A maximum of one batch jobs (denoted by uppercase queues) are started +each time +.Nm atrun +is invoked. +.Sh OPTIONS +.Bl -tag -width indent +.It Fl l Ar load_avg +Specifies a limiting load factor, over which batch jobs should +not be run, instead of the compiled \- in value of _LOADAVG_MX. +.It Fl d +Debug; print error messages to standard error instead of using +.Xr syslog 3 . +.El +.Sh WARNINGS +For +.Nm atrun +to work, you have to start up a +.Xr cron 8 +daemon. +.Sh FILES +.Bl -tag -width _ATSPOOL_DIR -compact +.It Pa _ATSPOOL_DIR +Directory containing output spool files +.It Pa _ATJOB_DIR +Directory containing job files +.El +.Sh SEE ALSO +.Xr cron 8 , +.Xr crontab 1 , +.Xr crontab 5 , +.Xr at 1 , +.Xr syslog 3 . +.Sh BUGS +The functionality of +.Nm atrun +should be merged into +.Xr cron 8 . diff --git a/libexec/atrun/gloadavg.c b/libexec/atrun/gloadavg.c new file mode 100644 index 0000000..289fa29 --- /dev/null +++ b/libexec/atrun/gloadavg.c @@ -0,0 +1,69 @@ +/* + * gloadavg.c - get load average for Linux + * Copyright (C) 1993 Thomas Koenig + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author(s) may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __FreeBSD__ +#define _POSIX_SOURCE 1 + +/* System Headers */ + +#include <stdio.h> +#else +#include <stdlib.h> +#endif + +/* Local headers */ + +#include "gloadavg.h" + +/* File scope variables */ + +static char rcsid[] = "$Id$"; + +/* Global functions */ + +double +gloadavg(void) +/* return the current load average as a floating point number, or <0 for + * error + */ +{ + double result; +#ifndef __FreeBSD__ + FILE *fp; + + if((fp=fopen(PROC_DIR "loadavg","r")) == NULL) + result = -1.0; + else + { + if(fscanf(fp,"%lf",&result) != 1) + result = -1.0; + fclose(fp); + } +#else + if (getloadavg(&result, 1) != 1) + perr("Error in getloadavg"); +#endif + return result; +} diff --git a/libexec/atrun/gloadavg.h b/libexec/atrun/gloadavg.h new file mode 100644 index 0000000..24c4e12 --- /dev/null +++ b/libexec/atrun/gloadavg.h @@ -0,0 +1,29 @@ +/* + * gloadavg.h - header for atrun(8) + * Copyright (C) 1993 Thomas Koenig + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author(s) may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +double gloadavg(void); +#if 0 +static char atrun_h_rcsid[] = "$Id$"; +#endif diff --git a/libexec/bootpd/Announce b/libexec/bootpd/Announce new file mode 100644 index 0000000..a605c6a --- /dev/null +++ b/libexec/bootpd/Announce @@ -0,0 +1,65 @@ +# $Id$ + +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..23310e1 --- /dev/null +++ b/libexec/bootpd/Changes @@ -0,0 +1,294 @@ +# $Id$ + +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..bc07225 --- /dev/null +++ b/libexec/bootpd/Makefile @@ -0,0 +1,17 @@ +# bootpd/Makefile +# $Id$ + +PROG= bootpd +CFLAGS+= -DETC_ETHERS +CFLAGS+= -DSYSLOG -DDEBUG -DVEND_CMU + +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 + +MAN5= bootptab.5 +MAN8= 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..50187da --- /dev/null +++ b/libexec/bootpd/Makefile.UNIX @@ -0,0 +1,204 @@ +# $Id$ +# +# 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..d055f52 --- /dev/null +++ b/libexec/bootpd/Makefile.inc @@ -0,0 +1,3 @@ +# $Id$ + +BINDIR?= /usr/libexec diff --git a/libexec/bootpd/Problems b/libexec/bootpd/Problems new file mode 100644 index 0000000..bec9ac0 --- /dev/null +++ b/libexec/bootpd/Problems @@ -0,0 +1,66 @@ +# $Id$ + +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..ddfaebe --- /dev/null +++ b/libexec/bootpd/README @@ -0,0 +1,136 @@ +# $Id$ + +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..88f7ed2 --- /dev/null +++ b/libexec/bootpd/bootp.h @@ -0,0 +1,147 @@ +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + +/* + * Bootstrap Protocol (BOOTP). RFC951 and RFC1395. + * + * $Id$ + * + * + * This file specifies the "implementation-independent" BOOTP protocol + * information which is common to both client and server. + * + */ + +#include "bptypes.h" /* for int32, u_int32 */ + +#define BP_CHADDR_LEN 16 +#define BP_SNAME_LEN 64 +#define BP_FILE_LEN 128 +#define BP_VEND_LEN 64 +#define BP_MINPKTSZ 300 /* to check sizeof(struct bootp) */ + +struct bootp { + unsigned char bp_op; /* packet opcode type */ + unsigned char bp_htype; /* hardware addr type */ + unsigned char bp_hlen; /* hardware addr length */ + unsigned char bp_hops; /* gateway hops */ + unsigned int32 bp_xid; /* transaction ID */ + unsigned short bp_secs; /* seconds since boot began */ + unsigned short bp_flags; /* RFC1532 broadcast, etc. */ + struct in_addr bp_ciaddr; /* client IP address */ + struct in_addr bp_yiaddr; /* 'your' IP address */ + struct in_addr bp_siaddr; /* server IP address */ + struct in_addr bp_giaddr; /* gateway IP address */ + unsigned char bp_chaddr[BP_CHADDR_LEN]; /* client hardware address */ + char bp_sname[BP_SNAME_LEN]; /* server host name */ + char bp_file[BP_FILE_LEN]; /* boot file name */ + unsigned char bp_vend[BP_VEND_LEN]; /* vendor-specific area */ + /* note that bp_vend can be longer, extending to end of packet. */ +}; + +/* + * UDP port numbers, server and client. + */ +#define IPPORT_BOOTPS 67 +#define IPPORT_BOOTPC 68 + +#define BOOTREPLY 2 +#define BOOTREQUEST 1 + +/* + * Hardware types from Assigned Numbers RFC. + */ +#define HTYPE_ETHERNET 1 +#define HTYPE_EXP_ETHERNET 2 +#define HTYPE_AX25 3 +#define HTYPE_PRONET 4 +#define HTYPE_CHAOS 5 +#define HTYPE_IEEE802 6 +#define HTYPE_ARCNET 7 + +/* + * Vendor magic cookie (v_magic) for CMU + */ +#define VM_CMU "CMU" + +/* + * Vendor magic cookie (v_magic) for RFC1048 + */ +#define VM_RFC1048 { 99, 130, 83, 99 } + + + +/* + * Tag values used to specify what information is being supplied in + * the vendor (options) data area of the packet. + */ +/* RFC 1048 */ +#define TAG_END ((unsigned char) 255) +#define TAG_PAD ((unsigned char) 0) +#define TAG_SUBNET_MASK ((unsigned char) 1) +#define TAG_TIME_OFFSET ((unsigned char) 2) +#define TAG_GATEWAY ((unsigned char) 3) +#define TAG_TIME_SERVER ((unsigned char) 4) +#define TAG_NAME_SERVER ((unsigned char) 5) +#define TAG_DOMAIN_SERVER ((unsigned char) 6) +#define TAG_LOG_SERVER ((unsigned char) 7) +#define TAG_COOKIE_SERVER ((unsigned char) 8) +#define TAG_LPR_SERVER ((unsigned char) 9) +#define TAG_IMPRESS_SERVER ((unsigned char) 10) +#define TAG_RLP_SERVER ((unsigned char) 11) +#define TAG_HOST_NAME ((unsigned char) 12) +#define TAG_BOOT_SIZE ((unsigned char) 13) +/* RFC 1395 */ +#define TAG_DUMP_FILE ((unsigned char) 14) +#define TAG_DOMAIN_NAME ((unsigned char) 15) +#define TAG_SWAP_SERVER ((unsigned char) 16) +#define TAG_ROOT_PATH ((unsigned char) 17) +/* RFC 1497 */ +#define TAG_EXTEN_FILE ((unsigned char) 18) +/* RFC 1533 */ +#define TAG_NIS_DOMAIN ((unsigned char) 40) +#define TAG_NIS_SERVER ((unsigned char) 41) +#define TAG_NTP_SERVER ((unsigned char) 42) +/* DHCP maximum message size. */ +#define TAG_MAX_MSGSZ ((unsigned char) 57) + +/* XXX - Add new tags here */ + + +/* + * "vendor" data permitted for CMU bootp clients. + */ + +struct cmu_vend { + char v_magic[4]; /* magic number */ + unsigned int32 v_flags; /* flags/opcodes, etc. */ + struct in_addr v_smask; /* Subnet mask */ + struct in_addr v_dgate; /* Default gateway */ + struct in_addr v_dns1, v_dns2; /* Domain name servers */ + struct in_addr v_ins1, v_ins2; /* IEN-116 name servers */ + struct in_addr v_ts1, v_ts2; /* Time servers */ + int32 v_unused[6]; /* currently unused */ +}; + + +/* v_flags values */ +#define VF_SMASK 1 /* Subnet mask field contains valid data */ diff --git a/libexec/bootpd/bootpd.8 b/libexec/bootpd/bootpd.8 new file mode 100644 index 0000000..5e0069c --- /dev/null +++ b/libexec/bootpd/bootpd.8 @@ -0,0 +1,305 @@ +.\" Copyright (c) 1988, 1989, 1991 Carnegie Mellon University +.\" +.\" $Header: /home/ncvs/src/libexec/bootpd/bootpd.8,v 1.4 1996/09/19 08:21:18 phk Exp $ +.\" +.TH BOOTPD 8 "November 06, 1993" "Carnegie Mellon University" +.SH NAME +bootpd, bootpgw \- Internet Boot Protocol server/gateway +.SH SYNOPSIS +.B bootpd +[ +.B \-i +.B \-s +.B \-t +timeout +.B \-d +level +.B \-c +chdir\-path +] +[ +.I bootptab +[ +.I dumpfile +] ] +.br +.B bootpgw +[ +.B \-i +.B \-s +.B \-t +timeout +.B \-d +level +] server +.SH DESCRIPTION +.I Bootpd +implements an Internet Bootstrap Protocol (BOOTP) server as defined in +RFC951, RFC1532, and RFC1533. +.I Bootpgw +implements a simple BOOTP gateway which can be used to forward +requests and responses between clients on one subnet and a +BOOTP server (i.e. +.IR bootpd ) +on another subnet. While either +.I bootpd +or +.I bootpgw +will forward BOOTREPLY packets, only +.I bootpgw +will forward BOOTREQUEST packets. +.PP +One host on each network segment is normally configured to run either +.I bootpd +or +.I bootpgw +from +.I inetd +by including one of the following lines in the file +.IR /etc/inetd.conf : +.IP +bootps dgram udp wait root /usr/libexec/bootpd bootpd /etc/bootptab +.br +bootps dgram udp wait root /usr/libexec/bootpgw bootpgw server +.PP +This mode of operation is referred to as "inetd mode" and causes +.I bootpd +(or +.IR bootpgw ) +to be started only when a boot request arrives. If it does not +receive another packet within fifteen minutes of the last one +it received, it will exit to conserve system resources. The +.B \-t +option controls this timeout (see OPTIONS). +.PP +It is also possible to run +.I bootpd +(or +.IR bootpgw ) +in "standalone mode" (without +.IR inetd ) +by simply invoking it from a shell like any other regular command. +Standalone mode is particularly useful when +.I bootpd +is used with a large configuration database, where the start up +delay might otherwise prevent timely response to client requests. +(Automatic start up in standalone mode can be done by invoking +.I bootpd +from within +.IR /etc/rc.local , +for example.) +Standalone mode is less useful for +.I bootgw +which +has very little start up delay because +it does not read a configuration file. +.PP +Either program automatically detects whether it was invoked from inetd +or from a shell and automatically selects the appropriate mode. +The +.B \-s +or +.B \-i +option may be used to force standalone or inetd mode respectively +(see OPTIONS). +.SH OPTIONS +.TP +.BI \-t \ timeout +Specifies the +.I timeout +value (in minutes) that a +.I bootpd +or +.I bootpgw +process will wait for a BOOTP packet before exiting. +If no packets are recieved for +.I timeout +seconds, then the program will exit. +A timeout value of zero means "run forever". +In standalone mode, this option is forced to zero. +.TP +.BI \-d \ debug\-level +Sets the +.I debug\-level +variable that controls the amount of debugging messages generated. +For example, -d4 or -d 4 will set the debugging level to 4. +For compatibility with older versions of +.IR bootpd , +omitting the numeric parameter (i.e. just -d) will +simply increment the debug level by one. +.TP +.BI \-c \ chdir\-path +Sets the current directory used by +.I bootpd +while checking the existence and size of client boot files. This is +useful when client boot files are specified as relative pathnames, and +.I bootpd +needs to use the same current directory as the TFTP server +(typically /tftpboot). This option is not recoginzed by +.IR bootpgw . +.TP +.B \-i +Force inetd mode. This option is obsolete, but remains for +compatibility with older versions of +.IR bootpd . +.TP +.B \-s +Force standalone mode. This option is obsolete, but remains for +compatibility with older versions of +.IR bootpd . +.TP +.I bootptab +Specifies the name of the configuration file from which +.I bootpd +loads its database of known clients and client options +.RI ( bootpd +only). +.TP +.I dumpfile +Specifies the name of the file that +.I bootpd +will dump its internal database into when it receives a +SIGUSR1 signal +.RI ( bootpd +only). This option is only recognized if +.I bootpd +was compiled with the -DDEBUG flag. +.TP +.I server +Specifies the name of a BOOTP server to which +.I bootpgw +will forward all BOOTREQUEST packets it receives +.RI ( bootpgw +only). +.SH OPERATION +.PP +Both +.I bootpd +and +.I bootpgw +operate similarly in that both listen for any packets sent to the +.I bootps +port, and both simply forward any BOOTREPLY packets. +They differ in their handling of BOOTREQUEST packets. +.PP +When +.I bootpgw +is started, it determines the address of a BOOTP server +whose name is provided as a command line parameter. When +.I bootpgw +receives a BOOTREQUEST packet, it sets the "gateway address" +and "hop count" fields in the packet and forwards the packet +to the BOOTP server at the address determined earlier. +Requests are forwarded only if they indicate that +the client has been waiting for at least three seconds. +.PP +When +.I bootpd +is started it reads a configuration file, (normally +.IR /etc/bootptab ) +that initializes the internal database of known clients and client +options. This internal database is reloaded +from the configuration file when +.I bootpd +receives a hangup signal (SIGHUP) or when it discovers that the +configuration file has changed. +.PP +When +.I bootpd +receives a BOOTREQUEST packet, it +.\" checks the modification time of the +.\" configuration file and reloads the database if necessary. Then it +looks for a database entry matching the client request. +If the client is known, +.I bootpd +composes a BOOTREPLY packet using the database entry found above, +and sends the reply to the client (possibly using a gateway). +If the client is unknown, the request is discarded +(with a notice if debug > 0). +.PP +If +.I bootpd +is compiled with the -DDEBUG option, receipt of a SIGUSR1 signal causes +it to dump its internal database to the file +.I /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 +.I getservbyname +(which nomally uses +.IR /etc/services). +Two service names (and port numbers) are used: +.IP +bootps \- BOOTP Server listening port +.br +bootpc \- BOOTP Client destination port +.LP +If the port numbers cannot +be determined using +.I getservbyname +then the values default to bootps=67 and bootpc=68. +.SH FILES +.TP 20 +/etc/bootptab +Database file read by +.IR bootpd . +.TP +/tmp/bootpd.dump +Debugging dump file created by +.IR bootpd . +.TP +/etc/services +Internet service numbers. +.TP +/tftpboot +Current directory typically used by the TFTP server and +.IR bootpd . + +.SH BUGS +Individual host entries must not exceed 1024 characters. + +.SH CREDITS +.PP +This distribution is currently maintained by +Walter L. Wimer <walt+@cmu.edu>. +.PP +The original BOOTP server was created by +Bill Croft at Stanford University in January 1986. +.PP +The current version of +.I bootpd +is primarily the work of David Kovar, +Drew D. Perkins, and Walter L. Wimer, +at Carnegie Mellon University. +.TP +Enhancements and bug\-fixes have been contributed by: +(in alphabetical order) +.br +Danny Backx <db@sunbim.be> +.br +John Brezak <brezak@ch.hp.com> +.br +Frank da Cruz <fdc@cc.columbia.edu> +.br +David R. Linn <drl@vuse.vanderbilt.edu> +.br +Jim McKim <mckim@lerc.nasa.gov> +.br +Gordon W. Ross <gwr@mc.com> +.br +Jason Zions <jazz@hal.com> +.SH "SEE ALSO" +.LP +bootptab(5), inetd(8), tftpd(8) +.LP +DARPA Internet Request For Comments: +.TP 10 +RFC951 +Bootstrap Protocol +.TP 10 +RFC1532 +Clarifications and Extensions for the Bootstrap Protocol +.TP 10 +RFC1533 +DHCP Options and BOOTP Vendor Extensions diff --git a/libexec/bootpd/bootpd.c b/libexec/bootpd/bootpd.c new file mode 100644 index 0000000..0fe07e1 --- /dev/null +++ b/libexec/bootpd/bootpd.c @@ -0,0 +1,1406 @@ +/************************************************************************ + 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. + + $Id: bootpd.c,v 1.6 1997/02/22 14:21:02 peter Exp $ + +************************************************************************/ + +/* + * BOOTP (bootstrap protocol) server daemon. + * + * Answers BOOTP request packets from booting client machines. + * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol. + * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions. + * See RFC 1395 for option tags 14-17. + * See accompanying man page -- bootpd.8 + * + * HISTORY + * See ./Changes + * + * BUGS + * See ./ToDo + */ + + + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <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 <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 "hash.h" +#include "hwaddr.h" +#include "bootpd.h" +#include "dovend.h" +#include "getif.h" +#include "readfile.h" +#include "report.h" +#include "tzone.h" +#include "patchlevel.h" + +#ifndef CONFIG_FILE +#define CONFIG_FILE "/etc/bootptab" +#endif +#ifndef DUMPTAB_FILE +#define DUMPTAB_FILE "/tmp/bootpd.dump" +#endif + + + +/* + * Externals, forward declarations, and global variables + */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern void dumptab P((char *)); + +PRIVATE void catcher P((int)); +PRIVATE int chk_access P((char *, int32 *)); +#ifdef VEND_CMU +PRIVATE void dovend_cmu P((struct bootp *, struct host *)); +#endif +PRIVATE void dovend_rfc1048 P((struct bootp *, struct host *, int32)); +PRIVATE void handle_reply P((void)); +PRIVATE void handle_request P((void)); +PRIVATE void sendreply P((int forward, int32 dest_override)); +PRIVATE void usage P((void)); + +#undef P + +/* + * IP port numbers for client and server obtained from /etc/services + */ + +u_short bootps_port, bootpc_port; + + +/* + * Internet socket and interface config structures + */ + +struct sockaddr_in bind_addr; /* Listening */ +struct sockaddr_in recv_addr; /* Packet source */ +struct sockaddr_in send_addr; /* destination */ + + +/* + * option defaults + */ +int debug = 0; /* Debugging flag (level) */ +struct timeval actualtimeout = +{ /* fifteen minutes */ + 15 * 60L, /* tv_sec */ + 0 /* tv_usec */ +}; + +/* + * General + */ + +int s; /* Socket file descriptor */ +char *pktbuf; /* Receive packet buffer */ +int pktlen; +char *progname; +char *chdir_path; +struct in_addr my_ip_addr; + +struct utsname my_uname; +char *hostname; + +/* Flags set by signal catcher. */ +PRIVATE int do_readtab = 0; +PRIVATE int do_dumptab = 0; + +/* + * Globals below are associated with the bootp database file (bootptab). + */ + +char *bootptab = CONFIG_FILE; +char *bootpd_dump = DUMPTAB_FILE; + + + +/* + * Initialization such as command-line processing is done and then the + * main server loop is started. + */ + +void +main(argc, argv) + int argc; + char **argv; +{ + struct timeval *timeout; + struct bootp *bp; + struct servent *servp; + struct hostent *hep; + char *stmp; + int n, ba_len, ra_len; + int nfound, readfds; + int standalone; +#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 (uname(&my_uname) < 0) { + report(LOG_ERR, "bootpd: can't get hostname\n"); + exit(1); + } + hostname = my_uname.nodename; + + /* + * 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("/dev/tty", O_RDWR); + if (n >= 0) { + ioctl(n, TIOCNOTTY, (char *) 0); + (void) close(n); + } +#endif /* TIOCNOTTY */ +#else /* SETSID */ + if (setsid() < 0) + perror("setsid"); +#endif /* SETSID */ + } /* if debug < 3 */ + + /* + * Nuke any timeout value + */ + timeout = NULL; + + } /* if standalone (1st) */ + + /* Set the cwd (i.e. to /tftpboot) */ + if (chdir_path) { + if (chdir(chdir_path) < 0) + report(LOG_ERR, "%s: chdir failed", chdir_path); + } + + /* Get the timezone. */ + tzone_init(); + + /* Allocate hash tables. */ + rdtab_init(); + + /* + * Read the bootptab file. + */ + readtab(1); /* force read */ + + if (standalone) { + + /* + * Create a socket. + */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + report(LOG_ERR, "socket: %s", get_network_errmsg()); + exit(1); + } + + /* + * Get server's listening port number + */ + servp = getservbyname("bootps", "udp"); + if (servp) { + bootps_port = ntohs((u_short) servp->s_port); + } else { + bootps_port = (u_short) IPPORT_BOOTPS; + report(LOG_ERR, + "udp/bootps: unknown service -- assuming port %d", + bootps_port); + } + + /* + * Bind socket to BOOTPS port. + */ + bind_addr.sin_family = AF_INET; + bind_addr.sin_addr.s_addr = INADDR_ANY; + bind_addr.sin_port = htons(bootps_port); + if (bind(s, (struct sockaddr *) &bind_addr, + sizeof(bind_addr)) < 0) + { + report(LOG_ERR, "bind: %s", get_network_errmsg()); + exit(1); + } + } /* if standalone (2nd)*/ + + /* + * Get destination port number so we can reply to client + */ + servp = getservbyname("bootpc", "udp"); + if (servp) { + bootpc_port = ntohs(servp->s_port); + } else { + report(LOG_ERR, + "udp/bootpc: unknown service -- assuming port %d", + IPPORT_BOOTPC); + bootpc_port = (u_short) IPPORT_BOOTPC; + } + + /* + * Set up signals to read or dump the table. + */ +#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. + */ + 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()); + } + /* + * Call readtab() or dumptab() here to avoid the + * dangers of doing I/O from a signal handler. + */ + if (do_readtab) { + do_readtab = 0; + readtab(1); /* force read */ + } + if (do_dumptab) { + do_dumptab = 0; + dumptab(bootpd_dump); + } + continue; + } + if (!(readfds & (1 << s))) { + if (debug > 1) + report(LOG_INFO, "exiting after %ld minutes of inactivity", + actualtimeout.tv_sec / 60); + exit(0); + } + ra_len = sizeof(recv_addr); + n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, + (struct sockaddr *) &recv_addr, &ra_len); + if (n <= 0) { + continue; + } + if (debug > 1) { + report(LOG_INFO, "recvd pkt from IP addr %s", + inet_ntoa(recv_addr.sin_addr)); + } + if (n < sizeof(struct bootp)) { + if (debug) { + report(LOG_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; + } + } +} + + + + +/* + * Print "usage" message and exit + */ + +PRIVATE void +usage() +{ + fprintf(stderr, + "usage: bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n"); + fprintf(stderr, "\t -c n\tset current directory\n"); + fprintf(stderr, "\t -d n\tset debug level\n"); + fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); + fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); + fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); + exit(1); +} + +/* Signal catchers */ +PRIVATE void +catcher(sig) + int sig; +{ + if (sig == SIGHUP) + do_readtab = 1; + if (sig == SIGUSR1) + do_dumptab = 1; +#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; + + /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ + + /* + * If the servername field is set, compare it against us. + * If we're not being addressed, ignore this request. + * If the server name field is null, throw in our name. + */ + if (strlen(bp->bp_sname)) { + if (strcmp(bp->bp_sname, hostname)) { + if (debug) + report(LOG_INFO, "\ +ignoring request for server %s from client at %s address %s", + bp->bp_sname, netname(bp->bp_htype), + haddrtoa(bp->bp_chaddr, bp->bp_hlen)); + /* XXX - Is it correct to ignore such a request? -gwr */ + return; + } + } else { + strcpy(bp->bp_sname, hostname); + } + + /* Convert the request into a reply. */ + bp->bp_op = BOOTREPLY; + if (bp->bp_ciaddr.s_addr == 0) { + /* + * client doesnt know his IP address, + * search by hardware address. + */ + if (debug > 1) { + report(LOG_INFO, "request from %s address %s", + netname(bp->bp_htype), + haddrtoa(bp->bp_chaddr, bp->bp_hlen)); + } + hlen = haddrlength(bp->bp_htype); + if (hlen != bp->bp_hlen) { + report(LOG_NOTICE, "bad addr len from from %s address %s", + netname(bp->bp_htype), + haddrtoa(bp->bp_chaddr, hlen)); + } + dummyhost.htype = bp->bp_htype; + bcopy(bp->bp_chaddr, dummyhost.haddr, hlen); + hashcode = hash_HashFunction(bp->bp_chaddr, hlen); + hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp, + &dummyhost); + if (hp == NULL && + bp->bp_htype == HTYPE_IEEE802) + { + /* Try again with address in "canonical" form. */ + haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen); + if (debug > 1) { + report(LOG_INFO, "\ +HW addr type is IEEE 802. convert to %s and check again\n", + haddrtoa(dummyhost.haddr, bp->bp_hlen)); + } + hashcode = hash_HashFunction(dummyhost.haddr, hlen); + hp = (struct host *) hash_Lookup(hwhashtable, hashcode, + hwlookcmp, &dummyhost); + } + if (hp == NULL) { + /* + * XXX - Add dynamic IP address assignment? + */ + if (debug) + 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) { + strcpy(realpath, hp->tftpdir->string); + clntpath = &realpath[strlen(realpath)]; + } else { + realpath[0] = '\0'; + clntpath = realpath; + } + + /* + * Determine client's requested homedir and bootfile. + */ + homedir = NULL; + bootfile = NULL; + if (bp->bp_file[0]) { + homedir = bp->bp_file; + bootfile = strrchr(homedir, '/'); + if (bootfile) { + if (homedir == bootfile) + homedir = NULL; + *bootfile++ = '\0'; + } else { + /* no "/" in the string */ + bootfile = homedir; + homedir = NULL; + } + if (debug > 2) { + report(LOG_INFO, "requested path=\"%s\" file=\"%s\"", + (homedir) ? homedir : "", + (bootfile) ? bootfile : ""); + } + } + + /* + * Specifications in bootptab override client requested values. + */ + if (hp->flags.homedir) + homedir = hp->homedir->string; + if (hp->flags.bootfile) + bootfile = hp->bootfile->string; + + /* + * Construct bootfile path. + */ + if (homedir) { + if (homedir[0] != '/') + strcat(clntpath, "/"); + strcat(clntpath, homedir); + homedir = NULL; + } + if (bootfile) { + if (bootfile[0] != '/') + strcat(clntpath, "/"); + strcat(clntpath, bootfile); + bootfile = NULL; + } + + /* + * First try to find the file with a ".host" suffix + */ + n = strlen(clntpath); + strcat(clntpath, "."); + strcat(clntpath, hp->hostname->string); + if (chk_access(realpath, &bootsize) < 0) { + clntpath[n] = 0; /* Try it without the suffix */ + if (chk_access(realpath, &bootsize) < 0) { + /* neither "file.host" nor "file" was found */ +#ifdef CHECK_FILE_ACCESS + + if (bp->bp_file[0]) { + /* + * Client wanted specific file + * and we didn't have it. + */ + report(LOG_NOTICE, + "requested file not found: \"%s\"", clntpath); + return; + } + /* + * Client didn't ask for a specific file and we couldn't + * access the default file, so just zero-out the bootfile + * field in the packet and continue processing the reply. + */ + bzero(bp->bp_file, sizeof(bp->bp_file)); + goto null_file_name; + +#else /* CHECK_FILE_ACCESS */ + + /* Complain only if boot file size was needed. */ + if (hp->flags.bootsize_auto) { + report(LOG_ERR, "can not determine size of file \"%s\"", + clntpath); + } + +#endif /* CHECK_FILE_ACCESS */ + } + } + strncpy(bp->bp_file, clntpath, BP_FILE_LEN); + if (debug > 2) + report(LOG_INFO, "bootfile=\"%s\"", clntpath); + +#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 compatiblity) + * then send the response to that address. Otherwise, + * act in accordance with RFC951: + * If the client IP address is specified, use that + * else if gateway IP address is specified, use that + * else make a temporary arp cache entry for the client's + * NEW IP/hardware address and use that. + */ + if (dst_override) { + dst.s_addr = dst_override; + if (debug > 1) { + report(LOG_INFO, "reply address override: %s", + inet_ntoa(dst)); + } + } else if (bp->bp_ciaddr.s_addr) { + dst = bp->bp_ciaddr; + } else if (bp->bp_giaddr.s_addr && forward == 0) { + dst = bp->bp_giaddr; + port = bootps_port; + if (debug > 1) { + report(LOG_INFO, "sending reply to gateway %s", + inet_ntoa(dst)); + } + } else { + dst = bp->bp_yiaddr; + ha = bp->bp_chaddr; + len = bp->bp_hlen; + if (len > MAXHADDRLEN) + len = MAXHADDRLEN; + 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 publically readable. A value of -1 is returned if + * access is not permitted or an error occurs. Successful calls also + * return the file size in bytes using the long pointer "filesize". + * + * The read permission bit for "other" users is checked. This bit must be + * set for tftpd(8) to allow clients to read the file. + */ + +PRIVATE int +chk_access(path, filesize) + char *path; + int32 *filesize; +{ + struct stat st; + + if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) { + *filesize = (int32) st.st_size; + return 0; + } else { + return -1; + } +} + + +/* + * Now in dumptab.c : + * dumptab() + * dump_host() + * list_ipaddresses() + */ + +#ifdef VEND_CMU + +/* + * Insert the CMU "vendor" data for the host pointed to by "hp" into the + * bootp packet pointed to by "bp". + */ + +PRIVATE void +dovend_cmu(bp, hp) + struct bootp *bp; + struct host *hp; +{ + struct cmu_vend *vendp; + struct in_addr_list *taddr; + + /* + * Initialize the entire vendor field to zeroes. + */ + bzero(bp->bp_vend, sizeof(bp->bp_vend)); + + /* + * Fill in vendor information. Subnet mask, default gateway, + * domain name server, ien name server, time server + */ + vendp = (struct cmu_vend *) bp->bp_vend; + strcpy(vendp->v_magic, (char *)vm_cmu); + if (hp->flags.subnet_mask) { + (vendp->v_smask).s_addr = hp->subnet_mask.s_addr; + (vendp->v_flags) |= VF_SMASK; + if (hp->flags.gateway) { + (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr; + } + } + if (hp->flags.domain_server) { + taddr = hp->domain_server; + if (taddr->addrcount > 0) { + (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr; + if (taddr->addrcount > 1) { + (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr; + } + } + } + if (hp->flags.name_server) { + taddr = hp->name_server; + if (taddr->addrcount > 0) { + (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr; + if (taddr->addrcount > 1) { + (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr; + } + } + } + if (hp->flags.time_server) { + taddr = hp->time_server; + if (taddr->addrcount > 0) { + (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr; + if (taddr->addrcount > 1) { + (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr; + } + } + } + /* Log message now done by caller. */ +} /* dovend_cmu */ + +#endif /* VEND_CMU */ + + + +/* + * Insert the RFC1048 vendor data for the host pointed to by "hp" into the + * bootp packet pointed to by "bp". + */ +#define NEED(LEN, MSG) do \ + if (bytesleft < (LEN)) { \ + report(LOG_NOTICE, noroom, \ + hp->hostname->string, MSG); \ + return; \ + } while (0) +PRIVATE void +dovend_rfc1048(bp, hp, bootsize) + struct bootp *bp; + struct host *hp; + int32 bootsize; +{ + int bytesleft, len; + byte *vp; + + static char noroom[] = "%s: No room for \"%s\" option"; + + vp = bp->bp_vend; + + if (hp->flags.msg_size) { + pktlen = hp->msg_size; + } else { + /* + * If the request was longer than the official length, build + * a response of that same length where the additional length + * is assumed to be part of the bp_vend (options) area. + */ + if (pktlen > sizeof(*bp)) { + if (debug > 1) + report(LOG_INFO, "request message length=%d", pktlen); + } + /* + * Check whether the request contains the option: + * Maximum DHCP Message Size (RFC1533 sec. 9.8) + * and if so, override the response length with its value. + * This request must lie within the first BP_VEND_LEN + * bytes of the option space. + */ + { + byte *p, *ep; + byte tag, len; + short msgsz = 0; + + p = vp + 4; + ep = p + BP_VEND_LEN - 4; + while (p < ep) { + tag = *p++; + /* Check for tags with no data first. */ + if (tag == TAG_PAD) + continue; + if (tag == TAG_END) + break; + /* Now scan the length byte. */ + len = *p++; + switch (tag) { + case TAG_MAX_MSGSZ: + if (len == 2) { + bcopy(p, (char*)&msgsz, 2); + msgsz = ntohs(msgsz); + } + break; + case TAG_SUBNET_MASK: + /* XXX - Should preserve this if given... */ + break; + } /* swtich */ + p += len; + } + + if (msgsz > sizeof(*bp)) { + if (debug > 1) + report(LOG_INFO, "request has DHCP msglen=%d", msgsz); + pktlen = msgsz; + } + } + } + + if (pktlen < sizeof(*bp)) { + report(LOG_ERR, "invalid response length=%d", pktlen); + pktlen = sizeof(*bp); + } + bytesleft = ((byte*)bp + pktlen) - vp; + if (pktlen > sizeof(*bp)) { + if (debug > 1) + report(LOG_INFO, "extended reply, length=%d, options=%d", + pktlen, bytesleft); + } + + /* Copy in the magic cookie */ + bcopy(vm_rfc1048, vp, 4); + vp += 4; + bytesleft -= 4; + + if (hp->flags.subnet_mask) { + /* always enough room here. */ + *vp++ = TAG_SUBNET_MASK;/* -1 byte */ + *vp++ = 4; /* -1 byte */ + insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */ + bytesleft -= 6; /* Fix real count */ + if (hp->flags.gateway) { + (void) insert_ip(TAG_GATEWAY, + hp->gateway, + &vp, &bytesleft); + } + } + if (hp->flags.bootsize) { + /* always enough room here */ + bootsize = (hp->flags.bootsize_auto) ? + ((bootsize + 511) / 512) : (hp->bootsize); /* Round up */ + *vp++ = TAG_BOOT_SIZE; + *vp++ = 2; + *vp++ = (byte) ((bootsize >> 8) & 0xFF); + *vp++ = (byte) (bootsize & 0xFF); + bytesleft -= 4; /* Tag, length, and 16 bit blocksize */ + } + /* + * This one is special: Remaining options go in the ext file. + * Only the subnet_mask, bootsize, and gateway should precede. + */ + if (hp->flags.exten_file) { + /* + * Check for room for exten_file. Add 3 to account for + * TAG_EXTEN_FILE, length, and TAG_END. + */ + len = strlen(hp->exten_file->string); + NEED((len + 3), "ef"); + *vp++ = TAG_EXTEN_FILE; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->exten_file->string, vp, len); + vp += len; + *vp++ = TAG_END; + bytesleft -= len + 3; + return; /* no more options here. */ + } + /* + * The remaining options are inserted by the following + * function (which is shared with bootpef.c). + * Keep back one byte for the TAG_END. + */ + len = dovend_rfc1497(hp, vp, bytesleft - 1); + vp += len; + bytesleft -= len; + + /* There should be at least one byte left. */ + NEED(1, "(end)"); + *vp++ = TAG_END; + bytesleft--; + + /* Log message done by caller. */ + if (bytesleft > 0) { + /* + * Zero out any remaining part of the vendor area. + */ + bzero(vp, bytesleft); + } +} /* dovend_rfc1048 */ +#undef NEED + + +/* + * Now in readfile.c: + * hwlookcmp() + * iplookcmp() + */ + +/* haddrtoa() - now in hwaddr.c */ +/* + * Now in dovend.c: + * insert_ip() + * insert_generic() + * insert_u_long() + */ + +/* get_errmsg() - now in report.c */ + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/bootpd.h b/libexec/bootpd/bootpd.h new file mode 100644 index 0000000..e5ce341 --- /dev/null +++ b/libexec/bootpd/bootpd.h @@ -0,0 +1,211 @@ +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + + +/* + * bootpd.h -- common header file for all the modules of the bootpd program. + */ + +#include "bptypes.h" +#include "hash.h" +#include "hwaddr.h" + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef PRIVATE +#define PRIVATE static +#endif + +#ifndef SIGUSR1 +#define SIGUSR1 30 /* From 4.3 <signal.h> */ +#endif + +#define MAXSTRINGLEN 80 /* Max string length */ + +/* Local definitions: */ +#define MAX_MSG_SIZE (3*512) /* Maximum packet size */ + + +/* + * Return pointer to static string which gives full network error message. + */ +#define get_network_errmsg get_errmsg + + +/* + * Data structure used to hold an arbitrary-lengthed list of IP addresses. + * The list may be shared among multiple hosts by setting the linkcount + * appropriately. + */ + +struct in_addr_list { + unsigned int linkcount, addrcount; + struct in_addr addr[1]; /* Dynamically extended */ +}; + + +/* + * Data structures used to hold shared strings and shared binary data. + * The linkcount must be set appropriately. + */ + +struct shared_string { + unsigned int linkcount; + char string[1]; /* Dynamically extended */ +}; + +struct shared_bindata { + unsigned int linkcount, length; + byte data[1]; /* Dynamically extended */ +}; + + +/* + * Flag structure which indicates which symbols have been defined for a + * given host. This information is used to determine which data should or + * should not be reported in the bootp packet vendor info field. + */ + +struct flag { + unsigned bootfile :1, + bootserver :1, + bootsize :1, + bootsize_auto :1, + cookie_server :1, + domain_server :1, + gateway :1, + generic :1, + haddr :1, + homedir :1, + htype :1, + impress_server :1, + iaddr :1, + log_server :1, + lpr_server :1, + name_server :1, + name_switch :1, + rlp_server :1, + send_name :1, + subnet_mask :1, + tftpdir :1, + time_offset :1, + time_server :1, + dump_file :1, + domain_name :1, + swap_server :1, + root_path :1, + exten_file :1, + reply_addr :1, + nis_domain :1, + nis_server :1, + ntp_server :1, + exec_file :1, + msg_size :1, + min_wait :1, + /* XXX - Add new tags here */ + vm_cookie :1; +}; + + + +/* + * The flags structure contains TRUE flags for all the fields which + * are considered valid, regardless of whether they were explicitly + * specified or indirectly inferred from another entry. + * + * The gateway and the various server fields all point to a shared list of + * IP addresses. + * + * The hostname, home directory, and bootfile are all shared strings. + * + * The generic data field is a shared binary data structure. It is used to + * hold future RFC1048 vendor data until bootpd is updated to understand it. + * + * The vm_cookie field specifies the four-octet vendor magic cookie to use + * if it is desired to always send the same response to a given host. + * + * Hopefully, the rest is self-explanatory. + */ + +struct host { + unsigned linkcount; /* hash list inserts */ + struct flag flags; /* ALL valid fields */ + struct in_addr_list *cookie_server, + *domain_server, + *gateway, + *impress_server, + *log_server, + *lpr_server, + *name_server, + *rlp_server, + *time_server, + *nis_server, + *ntp_server; + struct shared_string *bootfile, + *hostname, + *domain_name, + *homedir, + *tftpdir, + *dump_file, + *exten_file, + *root_path, + *nis_domain, + *exec_file; + struct shared_bindata *generic; + byte vm_cookie[4], + htype, /* RFC826 says this should be 16-bits but + RFC951 only allocates 1 byte. . . */ + haddr[MAXHADDRLEN]; + int32 time_offset; + unsigned int32 bootsize, + msg_size, + min_wait; + struct in_addr bootserver, + iaddr, + swap_server, + reply_addr, + subnet_mask; + /* XXX - Add new tags here (or above as appropriate) */ +}; + + + +/* + * Variables shared among modules. + */ + +extern int debug; +extern char *bootptab; +extern char *progname; + +extern u_char vm_cmu[4]; +extern u_char vm_rfc1048[4]; + +extern hash_tbl *hwhashtable; +extern hash_tbl *iphashtable; +extern hash_tbl *nmhashtable; + diff --git a/libexec/bootpd/bootpgw/Makefile b/libexec/bootpd/bootpgw/Makefile new file mode 100644 index 0000000..04f02eb --- /dev/null +++ b/libexec/bootpd/bootpgw/Makefile @@ -0,0 +1,12 @@ +# Makefile +# $Id$ + +PROG= bootpgw +NOMAN= true +SRCS= bootpgw.c getif.c hwaddr.c report.c rtmsg.c + +SRCDIR= ${.CURDIR}/.. +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..c289352 --- /dev/null +++ b/libexec/bootpd/bootpgw/bootpgw.c @@ -0,0 +1,688 @@ +/* + * 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/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 <syslog.h> +#include <assert.h> + +#ifdef NO_SETSID +# include <fcntl.h> /* for O_RDONLY, etc */ +#endif + +#ifndef USE_BFUNCS +# include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +# define bcopy(a,b,c) memcpy(b,a,c) +# define bzero(p,l) memset(p,0,l) +# define bcmp(a,b,c) memcmp(a,b,c) +#endif + +#include "bootp.h" +#include "getif.h" +#include "hwaddr.h" +#include "report.h" +#include "patchlevel.h" + +/* Local definitions: */ +#define MAX_MSG_SIZE (3*512) /* Maximum packet size */ +#define TRUE 1 +#define FALSE 0 +#define get_network_errmsg get_errmsg + + + +/* + * Externals, forward declarations, and global variables + */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +static void usage P((void)); +static void handle_reply P((void)); +static void handle_request P((void)); + +#undef P + +/* + * IP port numbers for client and server obtained from /etc/services + */ + +u_short bootps_port, bootpc_port; + + +/* + * Internet socket and interface config structures + */ + +struct sockaddr_in bind_addr; /* Listening */ +struct sockaddr_in recv_addr; /* Packet source */ +struct sockaddr_in send_addr; /* destination */ + + +/* + * option defaults + */ +int debug = 0; /* Debugging flag (level) */ +struct timeval actualtimeout = +{ /* fifteen minutes */ + 15 * 60L, /* tv_sec */ + 0 /* tv_usec */ +}; +u_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. + */ + +void +main(argc, argv) + int argc; + char **argv; +{ + struct timeval *timeout; + struct bootp *bp; + struct servent *servp; + struct hostent *hep; + char *stmp; + int n, ba_len, ra_len; + int nfound, readfds; + int standalone; + + progname = strrchr(argv[0], '/'); + if (progname) progname++; + else progname = argv[0]; + + /* + * Initialize logging. + */ + report_init(0); /* uses progname */ + + /* + * Log startup + */ + report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL); + + /* Debugging for compilers with struct padding. */ + assert(sizeof(struct bootp) == BP_MINPKTSZ); + + /* Get space for receiving packets and composing replies. */ + pktbuf = malloc(MAX_MSG_SIZE); + if (!pktbuf) { + report(LOG_ERR, "malloc failed"); + exit(1); + } + bp = (struct bootp *) pktbuf; + + /* + * Check to see if a socket was passed to us from inetd. + * + * Use getsockname() to determine if descriptor 0 is indeed a socket + * (and thus we are probably a child of inetd) or if it is instead + * something else and we are running standalone. + */ + s = 0; + ba_len = sizeof(bind_addr); + bzero((char *) &bind_addr, ba_len); + errno = 0; + standalone = TRUE; + if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) { + /* + * Descriptor 0 is a socket. Assume we are a child of inetd. + */ + if (bind_addr.sin_family == AF_INET) { + standalone = FALSE; + bootps_port = ntohs(bind_addr.sin_port); + } else { + /* Some other type of socket? */ + report(LOG_INFO, "getsockname: not an INET socket"); + } + } + /* + * Set defaults that might be changed by option switches. + */ + stmp = NULL; + timeout = &actualtimeout; + + if (uname(&my_uname) < 0) { + fprintf(stderr, "bootpgw: can't get hostname\n"); + exit(1); + } + 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)) { + fprintf(stderr, + "%s: invalid debug level\n", progname); + break; + } + debug = n; + break; + + case 'h': /* hop count limit */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || + (n < 0) || (n > 16)) + { + fprintf(stderr, + "bootpgw: invalid hop count limit\n"); + break; + } + maxhops = (u_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)) { + fprintf(stderr, + "%s: invalid timeout specification\n", progname); + break; + } + actualtimeout.tv_sec = (int32) (60 * n); + /* + * If the actual timeout is zero, pass a NULL pointer + * to select so it blocks indefinitely, otherwise, + * point to the actual timeout value. + */ + timeout = (n > 0) ? &actualtimeout : NULL; + break; + + case 'w': /* wait time */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || + (n < 0) || (n > 60)) + { + fprintf(stderr, + "bootpgw: invalid wait time\n"); + break; + } + minwait = (u_int)n; + break; + + default: + fprintf(stderr, "%s: unknown switch: -%c\n", + progname, argv[0][1]); + usage(); + break; + + } /* switch */ + } /* for args */ + + /* Make sure server name argument is suplied. */ + servername = argv[0]; + if (!servername) { + fprintf(stderr, "bootpgw: missing server name\n"); + usage(); + } + /* + * Get address of real bootp server. + */ + if (isdigit(servername[0])) + server_ipa = inet_addr(servername); + else { + hep = gethostbyname(servername); + if (!hep) { + fprintf(stderr, "bootpgw: can't get addr for %s\n", servername); + exit(1); + } + bcopy(hep->h_addr, (char *)&server_ipa, sizeof(server_ipa)); + } + + if (standalone) { + /* + * Go into background and disassociate from controlling terminal. + * XXX - This is not the POSIX way (Should use setsid). -gwr + */ + if (debug < 3) { + if (fork()) + exit(0); +#ifdef NO_SETSID + setpgrp(0,0); +#ifdef TIOCNOTTY + n = open("/dev/tty", O_RDWR); + if (n >= 0) { + ioctl(n, TIOCNOTTY, (char *) 0); + (void) close(n); + } +#endif /* TIOCNOTTY */ +#else /* SETSID */ + if (setsid() < 0) + perror("setsid"); +#endif /* SETSID */ + } /* if debug < 3 */ + /* + * Nuke any timeout value + */ + timeout = NULL; + + /* + * Here, bootpd would do: + * chdir + * tzone_init + * rdtab_init + * readtab + */ + + /* + * Create a socket. + */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + report(LOG_ERR, "socket: %s", get_network_errmsg()); + exit(1); + } + /* + * Get server's listening port number + */ + servp = getservbyname("bootps", "udp"); + if (servp) { + bootps_port = ntohs((u_short) servp->s_port); + } else { + bootps_port = (u_short) IPPORT_BOOTPS; + report(LOG_ERR, + "udp/bootps: unknown service -- assuming port %d", + bootps_port); + } + + /* + * Bind socket to BOOTPS port. + */ + bind_addr.sin_family = AF_INET; + bind_addr.sin_port = htons(bootps_port); + bind_addr.sin_addr.s_addr = INADDR_ANY; + if (bind(s, (struct sockaddr *) &bind_addr, + sizeof(bind_addr)) < 0) + { + report(LOG_ERR, "bind: %s", get_network_errmsg()); + exit(1); + } + } /* if standalone */ + /* + * Get destination port number so we can reply to client + */ + servp = getservbyname("bootpc", "udp"); + if (servp) { + bootpc_port = ntohs(servp->s_port); + } else { + report(LOG_ERR, + "udp/bootpc: unknown service -- assuming port %d", + IPPORT_BOOTPC); + bootpc_port = (u_short) IPPORT_BOOTPC; + } + + /* no signal catchers */ + + /* + * Process incoming requests. + */ + for (;;) { + 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", + actualtimeout.tv_sec / 60); + exit(0); + } + ra_len = sizeof(recv_addr); + n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, + (struct sockaddr *) &recv_addr, &ra_len); + if (n <= 0) { + continue; + } + if (debug > 3) { + report(LOG_INFO, "recvd pkt from IP addr %s", + inet_ntoa(recv_addr.sin_addr)); + } + if (n < sizeof(struct bootp)) { + if (debug) { + report(LOG_INFO, "received short packet"); + } + continue; + } + pktlen = n; + + switch (bp->bp_op) { + case BOOTREQUEST: + handle_request(); + break; + case BOOTREPLY: + handle_reply(); + break; + } + } +} + + + + +/* + * Print "usage" message and exit + */ + +static void +usage() +{ + fprintf(stderr, + "usage: bootpgw [-d level] [-i] [-s] [-t timeout] server\n"); + fprintf(stderr, "\t -d n\tset debug level\n"); + fprintf(stderr, "\t -h n\tset max hop count\n"); + fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); + fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); + fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); + fprintf(stderr, "\t -w n\tset min wait time (secs)\n"); + exit(1); +} + + + +/* + * Process BOOTREQUEST packet. + * + * Note, this just forwards the request to a real server. + */ +static void +handle_request() +{ + struct bootp *bp = (struct bootp *) pktbuf; + 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, "reqest 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..c79116a --- /dev/null +++ b/libexec/bootpd/bootptab.5 @@ -0,0 +1,395 @@ +.\" Copyright (c) 1988, 1989, 1991 Carnegie Mellon University +.\" +.\" $Header: $ +.\" +.TH BOOTPTAB 5 "October 31, 1991" "Carnegie Mellon University" +.UC 6 + +.SH NAME +bootptab \- Internet Bootstrap Protocol server database +.SH DESCRIPTION +The +.I bootptab +file is the configuration database file for +.IR bootpd , +the Internet Bootstrap Protocol server. +It's format is similar to that of +.IR termcap (5) +in which two-character case-sensitive tag symbols are used to +represent host parameters. These parameter declarations are separated by +colons (:), with a general format of: +.PP +.I " hostname:tg=value. . . :tg=value. . . :tg=value. . . ." +.PP +where +.I hostname +is the actual name of a bootp client (or a "dummy entry"), and +.I tg +is a two-character tag symbol. Dummy entries have an invalid hostname +(one with a "." as the first character) and are used to provide +default values used by other entries via the +.B tc=.dummy-entry +mechanism. Most tags must be followed by an equals-sign +and a value as above. Some may also appear in a boolean form with no +value (i.e. +.RI : tg :). +The currently recognized tags are: +.PP +.br + bf Bootfile +.br + bs Bootfile size in 512-octet blocks +.br + cs Cookie server address list +.br + df Merit dump file +.br + dn Domain name +.br + ds Domain name server address list +.br + ef Extension file +.br + gw Gateway address list +.br + ha Host hardware address +.br + hd Bootfile home directory +.br + hn Send client's hostname to client +.br + ht Host hardware type (see Assigned Numbers RFC) +.br + im Impress server address list +.br + ip Host IP address +.br + lg Log server address list +.br + lp LPR server address list +.br + ns IEN-116 name server address list +.br + nt NTP (time) Server (RFC 1129) +.br + ra Reply address override +.br + rl Resource location protocol server address list +.br + rp Root path to mount as root +.br + sa TFTP server address client should use +.br + sm Host subnet mask +.br + sw Swap server address +.br + tc Table continuation (points to similar "template" host entry) +.br + td TFTP root directory used by "secure" TFTP servers +.br + to Time offset in seconds from UTC +.br + ts Time server address list +.br + vm Vendor magic cookie selector +.br + yd YP (NIS) domain name +.br + ys YP (NIS) server address + +.PP +There is also a generic tag, +.RI T n , +where +.I n +is an RFC1084 vendor field tag number. Thus it is possible to immediately +take advantage of future extensions to RFC1084 without being forced to modify +.I bootpd +first. Generic data may be represented as either a stream of hexadecimal +numbers or as a quoted string of ASCII characters. The length of the generic +data is automatically determined and inserted into the proper field(s) of the +RFC1084-style bootp reply. +.PP +The following tags take a whitespace-separated list of IP addresses: +.BR cs , +.BR ds , +.BR gw , +.BR im , +.BR lg , +.BR lp , +.BR ns , +.BR nt , +.BR ra , +.BR rl , +and +.BR ts . +The +.BR ip , +.BR sa , +.BR sw , +.BR sm , +and +.B ys +tags each take a single IP address. +All IP addresses are specified in standard Internet "dot" notation +and may use decimal, octal, or hexadecimal numbers +(octal numbers begin with 0, hexadecimal numbers begin with '0x' or '0X'). +Any IP addresses may alternatively be specified as a hostname, causing +.I bootpd +to lookup the IP address for that host name using gethostbyname(3). +If the +.B ip +tag is not specified, +.I bootpd +will determine the IP address using the entry name as the host name. +(Dummy entries use an invalid host name to avoid automatic IP lookup.) +.PP +The +.B ht +tag specifies the hardware type code as either an unsigned decimal, octal, or +hexadecimal integer or one of the following symbolic names: +.B ethernet +or +.B ether +for 10Mb Ethernet, +.B ethernet3 +or +.B ether3 +for 3Mb experimental Ethernet, +.BR ieee802 , +.BR tr , +or +.B token-ring +for IEEE 802 networks, +.B pronet +for Proteon ProNET Token Ring, or +.BR chaos , +.BR arcnet , +or +.B ax.25 +for Chaos, ARCNET, and AX.25 Amateur Radio networks, respectively. +The +.B ha +tag takes a hardware address which may be specified as a host name +or in numeric form. Note that the numeric form +.I must +be specified in hexadecimal; optional periods and/or a leading '0x' may be +included for readability. The +.B ha +tag must be preceded by the +.B ht +tag (either explicitly or implicitly; see +.B tc +below). +If the hardware address is not specified and the type is specified +as either "ethernet" or "ieee802", then +.I bootpd +will try to determine the hardware address using ether_hton(3). +.PP +The hostname, home directory, and bootfile are ASCII strings which may be +optionally surrounded by double quotes ("). The client's request and the +values of the +.B hd +and +.B bf +symbols determine how the server fills in the bootfile field of the bootp +reply packet. +.PP +If the client provides a file name it is left as is. +Otherwise, if the +.B bf +option is specified its value is copied into the reply packet. +If the +.B hd +option is specified as well, its value is prepended to the +boot file copied into the reply packet. +The existence of the boot file is checked only if the +.BR bs =auto +option is used (to determine the boot file size). +A reply may be sent whether or not the boot file exists. +.PP +Some newer versions of +.I tftpd +provide a security feature to change their root directory using +the +.IR chroot (2) +system call. +The +.B td +tag may be used to inform +.I bootpd +of this special root directory used by +.IR tftpd . +(One may alternatively use the +.I bootpd +"-c chdir" option.) +The +.B hd +tag is actually relative to the root directory specified by the +.B td +tag. +For example, if the real absolute path to your BOOTP client bootfile is +/tftpboot/bootfiles/bootimage, and +.IR tftpd +uses /tftpboot as its "secure" directory, then specify the following in +.IR bootptab : +.PP +.br + :td=/tftpboot:hd=/bootfiles:bf=bootimage: +.PP +If your bootfiles are located directly in /tftpboot, use: +.PP +.br + :td=/tftpboot:hd=/:bf=bootimage: +.PP +The +.B sa +tag may be used to specify the IP address of the particular TFTP server +you wish the client to use. In the absence of this tag, +.I bootpd +will tell the client to perform TFTP to the same machine +.I bootpd +is running on. +.PP +The time offset +.B to +may be either a signed decimal integer specifying the client's +time zone offset in seconds from UTC, or the keyword +.B auto +which uses the server's time zone offset. Specifying the +.B to +symbol as a boolean has the same effect as specifying +.B auto +as its value. +.PP +The bootfile size +.B bs +may be either a decimal, octal, or hexadecimal integer specifying the size of +the bootfile in 512-octet blocks, or the keyword +.B auto +which causes the server to automatically calculate the bootfile size at each +request. As with the time offset, specifying the +.B bs +symbol as a boolean has the same effect as specifying +.B auto +as its value. +.PP +The vendor magic cookie selector (the +.B vm +tag) may take one of the following keywords: +.B auto +(indicating that vendor information is determined by the client's request), +.B rfc1048 +or +.B rfc1084 +(which always forces an RFC1084-style reply), or +.B cmu +(which always forces a CMU-style reply). +.PP +The +.B hn +tag is strictly a boolean tag; it does not take the usual equals-sign and +value. It's presence indicates that the hostname should be sent to RFC1084 +clients. +.I Bootpd +attempts to send the entire hostname as it is specified in the configuration +file; if this will not fit into the reply packet, the name is shortened to +just the host field (up to the first period, if present) and then tried. +In no case is an arbitrarily-truncated hostname sent (if nothing reasonable +will fit, nothing is sent). +.PP +Often, many host entries share common values for certain tags (such as name +servers, etc.). Rather than repeatedly specifying these tags, a full +specification can be listed for one host entry and shared by others via the +.B tc +(table continuation) mechanism. +Often, the template entry is a dummy host which doesn't actually exist and +never sends bootp requests. This feature is similar to the +.B tc +feature of +.IR termcap (5) +for similar terminals. Note that +.I bootpd +allows the +.B tc +tag symbol to appear anywhere in the host entry, unlike +.I termcap +which requires it to be the last tag. Information explicitly specified for a +host always overrides information implied by a +.B tc +tag symbol, regardless of its location within the entry. The +value of the +.B tc +tag may be the hostname or IP address of any host entry +previously listed in the configuration file. +.PP +Sometimes it is necessary to delete a specific tag after it has been inferred +via +.BR tc . +This can be done using the construction +.IB tag @ +which removes the effect of +.I tag +as in +.IR termcap (5). +For example, to completely undo an IEN-116 name server specification, use +":ns@:" at an appropriate place in the configuration entry. After removal +with +.BR @ , +a tag is eligible to be set again through the +.B tc +mechanism. +.PP +Blank lines and lines beginning with "#" are ignored in the configuration +file. Host entries are separated from one another by newlines; a single host +entry may be extended over multiple lines if the lines end with a backslash +(\\). It is also acceptable for lines to be longer than 80 characters. Tags +may appear in any order, with the following exceptions: the hostname must be +the very first field in an entry, and the hardware type must precede the +hardware address. +.PP +An example +.I /etc/bootptab +file follows: +.PP +.nf + # Sample bootptab file (domain=andrew.cmu.edu) + + .default:\\ + :hd=/usr/boot:bf=null:\\ + :ds=netserver, lancaster:\\ + :ns=pcs2, pcs1:\\ + :ts=pcs2, pcs1:\\ + :sm=255.255.255.0:\\ + :gw=gw.cs.cmu.edu:\\ + :hn:to=-18000: + + carnegie:ht=6:ha=7FF8100000AF:tc=.default: + baldwin:ht=1:ha=0800200159C3:tc=.default: + wylie:ht=1:ha=00DD00CADF00:tc=.default: + arnold:ht=1:ha=0800200102AD:tc=.default: + bairdford:ht=1:ha=08002B02A2F9:tc=.default: + bakerstown:ht=1:ha=08002B0287C8:tc=.default: + + # Special domain name server and option tags for next host + butlerjct:ha=08002001560D:ds=128.2.13.42:\\ + :T37=0x12345927AD3BCF:\\ + :T99="Special ASCII string":\\ + :tc=.default: + + gastonville:ht=6:ha=7FFF81000A47:tc=.default: + hahntown:ht=6:ha=7FFF81000434:tc=.default: + hickman:ht=6:ha=7FFF810001BA:tc=.default: + lowber:ht=1:ha=00DD00CAF000:tc=.default: + mtoliver:ht=1:ha=00DD00FE1600:tc=.default: + +.fi +.SH FILES +/etc/bootptab + +.SH "SEE ALSO" +.br +bootpd(8), tftpd(8), +.br +DARPA Internet Request For Comments RFC951, RFC1048, RFC1084, Assigned Numbers diff --git a/libexec/bootpd/bootptab.cmu b/libexec/bootpd/bootptab.cmu new file mode 100644 index 0000000..66212d4 --- /dev/null +++ b/libexec/bootpd/bootptab.cmu @@ -0,0 +1,124 @@ +# /etc/bootptab: database for bootp server (/etc/bootpd) +# (I've hacked on this but can't test it... -gwr) + +# Blank lines and lines beginning with '#' are ignored. +# +# Legend: (see bootptab.5) +# first field -- hostname (not indented) +# bf -- bootfile +# bs -- bootfile size in 512-octet blocks +# cs -- cookie servers +# df -- dump file name +# dn -- domain name +# ds -- domain name servers +# ef -- extension file +# gw -- gateways +# ha -- hardware address +# hd -- home directory for bootfiles +# hn -- host name set for client +# ht -- hardware type +# im -- impress servers +# ip -- host IP address +# lg -- log servers +# lp -- LPR servers +# ns -- IEN-116 name servers +# ra -- reply address +# rl -- resource location protocol servers +# rp -- root path +# sa -- boot server address +# sm -- subnet mask +# sw -- swap server +# tc -- template host (points to similar host entry) +# td -- TFTP directory +# to -- time offset (seconds) +# ts -- time servers +# vm -- vendor magic number +# Tn -- generic option tag n +# +# Be careful about including backslashes where they're needed. Weird (bad) +# things can happen when a backslash is omitted where one is intended. +# Also, note that generic option data must be either a string or a +# sequence of bytes where each byte is a two-digit hex value. + +# First, we define a global entry which specifies the stuff every host uses. +# (Host name lookups are relative to the domain: andrew.cmu.edu) +.default:\ + :hn:dn=cmu.edu:\ + :hd=/usr/boot:\ + :ds=netserver, lancaster:\ + :ns=pcs2, pcs1:\ + :ts=pcs2, pcs1:\ + :sm=255.255.0.0:\ + :gw=gw.cs.cmu.edu:\ + to=auto: + + +# Next, we can define different master entries for each subnet. . . +.subnet13 :sm=255.255.255.0:gw=128.2.13.1 :tc=.default: +.subnet19 :sm=255.255.255.0:gw=128.2.19.1 :tc=.default: +.subnet232 :sm=255.255.255.0:gw=128.2.232.1 :tc=.default: + +# +# We should be able to use as many levels of indirection as desired. Use +# your imagination. . . +# + + +# Individual entries (could also have different servers for some/all of these +# hosts, but we don't really use this feature at CMU): + +carnegie:tc=.subnet13:ht=ieee802:ha=7FF8100000AF: +baldwin:tc=.subnet19:ha=0800200159C3: +wylie:tc=.subnet232:ha=00DD00CADF00: +arnold:tc=.subnet19:ha=0800200102AD: +bairdford:tc=.subnet19:ha=08002B02A2F9: +bakerstown:tc=.subnet19:ha=08002B0287C8: +butlerjct:tc=.subnet232:ha=08002001560D: +gastonville:tc=.subnet232:ht=ieee802:ha=7FFF81000A47: +hahntown:tc=.subnet13:ht=ieee802:ha=7FFF81000434: +hickman:tc=.subnet19:ht=ieee802:ha=7FFF810001BA: +lowber:tc=.subnet13:ha=00DD00CAF000: +mtoliver:tc=.subnet19:ha=00DD00FE1600: +osborne:tc=.subnet232:ha=00DD00CAD600: +russelton:tc=.subnet232:ha=080020017FC3: +thornburg:tc=.subnet13:ha=080020012A33: + + +# Hmmm. . . Let's throw in some whitespace for readability. . . . + +andrew: tc=.subnet19:ha=00DD00C88900: +birdville: tc=.subnet19:ha=00DD00FE2D00: +coudersport: tc=.subnet13:ha=00DD00CB1E00: +bridgeville: tc=.subnet232:ha=080020011394: +franklin: tc=.subnet19:ha=08002B02A5D5: +hollidaysburg: tc=.subnet19:ht=ieee802:ha=7FFF810002C8: +honesdale: tc=.subnet19:ha=08002B02F83F: +huntingdon: tc=.subnet19:ha=08002B02E410: +indiana: tc=.subnet13:ha=08002B029BEC: +jimthorpe: tc=.subnet232:ha=08002B02FBBA: +kittanning: tc=.subnet232:ha=08002B0273FC: +lebanon: tc=.subnet232:ha=08002B037F67: +lewisburg: tc=.subnet19:ha=50005A1A0DE4: +middleburg: tc=.subnet232:ha=00DD00FE1200: +aspinwall: tc=.subnet13:ha=08002B03C163: +berlin: tc=.subnet13:ha=00DD000A4400: +norristown: tc=.subnet13:ha=08002001455B: +pottsville: tc=.subnet13:ha=00DD000A3700: +ridgway: tc=.subnet19:ha=08002B029425: +scranton: tc=.subnet232:ha=0800200113A1: +chalfont: tc=.subnet13:ha=08002001124B: +washington: tc=.subnet19:ha=00DD00656E00: +wellsboro: tc=.subnet13:ha=00DD00CB1C00: +bb1: tc=.subnet19:ha=00DD000A1F00: +adamstown: tc=.subnet13:ha=08002B02D0E6: +beta: tc=.subnet19:ha=02070100B197: +carbondale: tc=.subnet232:ha=08002B022A73: +clairton: tc=.subnet19:ha=080020010FD1: +egypt: tc=.subnet13:ha=00DD00847B00: +fairchance: tc=.subnet232:ha=00DD000AB100: +fairhope: tc=.subnet232:ha=00DD00CB0800: +galeton: tc=.subnet232:ha=08002001138C: +imperial: tc=.subnet232:ha=08002001130C: +kingston: tc=.subnet232:ha=080020011382: +knox: tc=.subnet232:ha=50005A1A0D2A: +lakecity: tc=.subnet13:ha=080020011380: diff --git a/libexec/bootpd/bootptab.mcs b/libexec/bootpd/bootptab.mcs new file mode 100644 index 0000000..a798f8a --- /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. +# +# $Id$ +# +# 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..537da4e --- /dev/null +++ b/libexec/bootpd/bptypes.h @@ -0,0 +1,23 @@ +/* bptypes.h */ + +#ifndef BPTYPES_H +#define BPTYPES_H + +/* + * 32 bit integers are different types on various architectures + */ + +#ifndef int32 +#define int32 long +#endif +typedef unsigned int32 u_int32; + +/* + * Nice typedefs. . . + */ + +typedef int boolean; +typedef unsigned char byte; + + +#endif /* BPTYPES_H */ diff --git a/libexec/bootpd/dovend.c b/libexec/bootpd/dovend.c new file mode 100644 index 0000000..447826f --- /dev/null +++ b/libexec/bootpd/dovend.c @@ -0,0 +1,414 @@ +/* + * dovend.c : Inserts all but the first few vendor options. + * + * $Id$ + */ + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <syslog.h> + +#ifndef USE_BFUNCS +# include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +# define bcopy(a,b,c) memcpy(b,a,c) +# define bzero(p,l) memset(p,0,l) +# define bcmp(a,b,c) memcmp(a,b,c) +# define index strchr +#endif + +#include "bootp.h" +#include "bootpd.h" +#include "report.h" +#include "dovend.h" + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +PRIVATE int insert_generic P((struct shared_bindata *, byte **, int *)); + +/* + * Insert the 2nd part of the options into an option buffer. + * Return amount of space used. + * + * This inserts everything EXCEPT: + * magic cookie, subnet mask, gateway, bootsize, extension file + * Those are handled separately (in bootpd.c) to allow this function + * to be shared between bootpd and bootpef. + * + * When an "extension file" is in use, the options inserted by + * this function go into the exten_file, not the bootp response. + */ + +int +dovend_rfc1497(hp, buf, len) + struct host *hp; + byte *buf; + int len; +{ + int bytesleft = len; + byte *vp = buf; + + static char noroom[] = "%s: No room for \"%s\" option"; +#define NEED(LEN, MSG) do \ + if (bytesleft < (LEN)) { \ + report(LOG_NOTICE, noroom, \ + hp->hostname->string, MSG); \ + return (vp - buf); \ + } while (0) + + /* + * Note that the following have already been inserted: + * magic_cookie, subnet_mask, gateway, bootsize + * + * The remaining options are inserted in order of importance. + * (Of course the importance of each is a matter of opinion.) + * The option insertion order should probably be configurable. + * + * This is the order used in the NetBSD version. Can anyone + * explain why the time_offset and swap_server are first? + * Also, why is the hostname so far down the list? -gwr + */ + + if (hp->flags.time_offset) { + NEED(6, "to"); + *vp++ = TAG_TIME_OFFSET;/* -1 byte */ + *vp++ = 4; /* -1 byte */ + insert_u_long(htonl(hp->time_offset), &vp); /* -4 bytes */ + bytesleft -= 6; + } + /* + * swap server, root path, dump path + */ + if (hp->flags.swap_server) { + NEED(6, "sw"); + /* There is just one SWAP_SERVER, so it is not an iplist. */ + *vp++ = TAG_SWAP_SERVER;/* -1 byte */ + *vp++ = 4; /* -1 byte */ + insert_u_long(hp->swap_server.s_addr, &vp); /* -4 bytes */ + bytesleft -= 6; /* Fix real count */ + } + if (hp->flags.root_path) { + /* + * Check for room for root_path. Add 2 to account for + * TAG_ROOT_PATH and length. + */ + len = strlen(hp->root_path->string); + NEED((len + 2), "rp"); + *vp++ = TAG_ROOT_PATH; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->root_path->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + if (hp->flags.dump_file) { + /* + * Check for room for dump_file. Add 2 to account for + * TAG_DUMP_FILE and length. + */ + len = strlen(hp->dump_file->string); + NEED((len + 2), "df"); + *vp++ = TAG_DUMP_FILE; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->dump_file->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + /* + * DNS server and domain + */ + if (hp->flags.domain_server) { + if (insert_ip(TAG_DOMAIN_SERVER, + hp->domain_server, + &vp, &bytesleft)) + NEED(8, "ds"); + } + if (hp->flags.domain_name) { + /* + * Check for room for domain_name. Add 2 to account for + * TAG_DOMAIN_NAME and length. + */ + len = strlen(hp->domain_name->string); + NEED((len + 2), "dn"); + *vp++ = TAG_DOMAIN_NAME; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->domain_name->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + /* + * NIS (YP) server and domain + */ + if (hp->flags.nis_server) { + if (insert_ip(TAG_NIS_SERVER, + hp->nis_server, + &vp, &bytesleft)) + NEED(8, "ds"); + } + if (hp->flags.nis_domain) { + /* + * Check for room for nis_domain. Add 2 to account for + * TAG_NIS_DOMAIN and length. + */ + len = strlen(hp->nis_domain->string); + NEED((len + 2), "dn"); + *vp++ = TAG_NIS_DOMAIN; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->nis_domain->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + /* IEN 116 name server */ + if (hp->flags.name_server) { + if (insert_ip(TAG_NAME_SERVER, + hp->name_server, + &vp, &bytesleft)) + NEED(8, "ns"); + } + if (hp->flags.rlp_server) { + if (insert_ip(TAG_RLP_SERVER, + hp->rlp_server, + &vp, &bytesleft)) + NEED(8, "rl"); + } + /* Time server (RFC 868) */ + if (hp->flags.time_server) { + if (insert_ip(TAG_TIME_SERVER, + hp->time_server, + &vp, &bytesleft)) + NEED(8, "ts"); + } + /* NTP (time) Server (RFC 1129) */ + if (hp->flags.ntp_server) { + if (insert_ip(TAG_NTP_SERVER, + hp->ntp_server, + &vp, &bytesleft)) + NEED(8, "ts"); + } + /* + * I wonder: If the hostname were "promoted" into the BOOTP + * response part, might these "extension" files possibly be + * shared between several clients? + * + * Also, why not just use longer BOOTP packets with all the + * additional length used as option data. This bootpd version + * already supports that feature by replying with the same + * packet length as the client request packet. -gwr + */ + if (hp->flags.name_switch && hp->flags.send_name) { + /* + * Check for room for hostname. Add 2 to account for + * TAG_HOST_NAME and length. + */ + len = strlen(hp->hostname->string); +#if 0 + /* + * XXX - Too much magic. The user can always set the hostname + * to the short version in the bootptab file. -gwr + */ + if ((len + 2) > bytesleft) { + /* + * Not enough room for full (domain-qualified) hostname, try + * stripping it down to just the first field (host). + */ + 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..b30c982 --- /dev/null +++ b/libexec/bootpd/dovend.h @@ -0,0 +1,13 @@ +/* dovend.h */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern int dovend_rfc1497 P((struct host *hp, u_char *buf, int len)); +extern int insert_ip P((int, struct in_addr_list *, u_char **, int *)); +extern void insert_u_long P((u_int32, u_char **)); + +#undef P diff --git a/libexec/bootpd/dumptab.c b/libexec/bootpd/dumptab.c new file mode 100644 index 0000000..3827fd9 --- /dev/null +++ b/libexec/bootpd/dumptab.c @@ -0,0 +1,384 @@ +/* + * dumptab.c - handles dumping the database + * + * $Id$ + */ + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#include <stdio.h> +#include <stdlib.h> +#include <syslog.h> +#include <time.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#define bcopy(a,b,c) memcpy(b,a,c) +#define bzero(p,l) memset(p,0,l) +#define bcmp(a,b,c) memcmp(a,b,c) +#endif + +#include "bootp.h" +#include "hash.h" +#include "hwaddr.h" +#include "report.h" +#include "patchlevel.h" +#include "bootpd.h" + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +static void dump_generic P((FILE *, struct shared_bindata *)); +static void dump_host P((FILE *, struct host *)); +static void list_ipaddresses P((FILE *, struct in_addr_list *)); + +#undef P + +#ifndef DEBUG +void +dumptab(filename) + char *filename; +{ + report(LOG_INFO, "No dumptab support!"); +} + +#else /* DEBUG */ + +/* + * Dump the internal memory database to bootpd_dump. + */ + +void +dumptab(filename) + char *filename; +{ + int n; + struct host *hp; + FILE *fp; + long t; + /* Print symbols in alphabetical order for reader's convenience. */ + static char legend[] = "#\n# Legend:\t(see bootptab.5)\n\ +#\tfirst field -- hostname (not indented)\n\ +#\tbf -- bootfile\n\ +#\tbs -- bootfile size in 512-octet blocks\n\ +#\tcs -- cookie servers\n\ +#\tdf -- dump file name\n\ +#\tdn -- domain name\n\ +#\tds -- domain name servers\n\ +#\tef -- extension file\n\ +#\tex -- exec file (YORK_EX_OPTION)\n\ +#\tgw -- gateways\n\ +#\tha -- hardware address\n\ +#\thd -- home directory for bootfiles\n\ +#\thn -- host name set for client\n\ +#\tht -- hardware type\n\ +#\tim -- impress servers\n\ +#\tip -- host IP address\n\ +#\tlg -- log servers\n\ +#\tlp -- LPR servers\n\ +#\tms -- message size\n\ +#\tmw -- min wait (secs)\n\ +#\tns -- IEN-116 name servers\n\ +#\tnt -- NTP servers (RFC 1129)\n\ +#\tra -- reply address override\n\ +#\trl -- resource location protocol servers\n\ +#\trp -- root path\n\ +#\tsa -- boot server address\n\ +#\tsm -- subnet mask\n\ +#\tsw -- swap server\n\ +#\ttc -- template host (points to similar host entry)\n\ +#\ttd -- TFTP directory\n\ +#\tto -- time offset (seconds)\n\ +#\tts -- time servers\n\ +#\tvm -- vendor magic number\n\ +#\tyd -- YP (NIS) domain\n\ +#\tys -- YP (NIS) servers\n\ +#\tTn -- generic option tag n\n\ +\n"; + + /* + * Open bootpd.dump file. + */ + if ((fp = fopen(filename, "w")) == NULL) { + report(LOG_ERR, "error opening \"%s\": %s", + filename, get_errmsg()); + exit(1); + } + t = time(NULL); + fprintf(fp, "\n# %s %s.%d\n", progname, VERSION, PATCHLEVEL); + fprintf(fp, "# %s: dump of bootp server database.\n", filename); + fprintf(fp, "# Dump taken %s", ctime(&t)); + fwrite(legend, 1, sizeof(legend) - 1, fp); + + n = 0; + for (hp = (struct host *) hash_FirstEntry(nmhashtable); hp != NULL; + hp = (struct host *) hash_NextEntry(nmhashtable)) { + dump_host(fp, hp); + fprintf(fp, "\n"); + n++; + } + fclose(fp); + + report(LOG_INFO, "dumped %d entries to \"%s\".", n, filename); +} + + + +/* + * Dump all the available information on the host pointed to by "hp". + * The output is sent to the file pointed to by "fp". + */ + +static void +dump_host(fp, hp) + FILE *fp; + struct host *hp; +{ + /* Print symbols in alphabetical order for reader's convenience. */ + if (hp) { + fprintf(fp, "%s:", (hp->hostname ? + hp->hostname->string : "?")); + if (hp->flags.bootfile) { + fprintf(fp, "\\\n\t:bf=%s:", hp->bootfile->string); + } + if (hp->flags.bootsize) { + fprintf(fp, "\\\n\t:bs="); + if (hp->flags.bootsize_auto) { + fprintf(fp, "auto:"); + } else { + fprintf(fp, "%d:", hp->bootsize); + } + } + if (hp->flags.cookie_server) { + fprintf(fp, "\\\n\t:cs="); + list_ipaddresses(fp, hp->cookie_server); + fprintf(fp, ":"); + } + if (hp->flags.dump_file) { + fprintf(fp, "\\\n\t:df=%s:", hp->dump_file->string); + } + if (hp->flags.domain_name) { + fprintf(fp, "\\\n\t:dn=%s:", hp->domain_name->string); + } + if (hp->flags.domain_server) { + fprintf(fp, "\\\n\t:ds="); + list_ipaddresses(fp, hp->domain_server); + fprintf(fp, ":"); + } + if (hp->flags.exten_file) { + fprintf(fp, "\\\n\t:ef=%s:", hp->exten_file->string); + } + if (hp->flags.exec_file) { + fprintf(fp, "\\\n\t:ex=%s:", hp->exec_file->string); + } + if (hp->flags.gateway) { + fprintf(fp, "\\\n\t:gw="); + list_ipaddresses(fp, hp->gateway); + fprintf(fp, ":"); + } + /* FdC: swap_server (see below) */ + if (hp->flags.homedir) { + fprintf(fp, "\\\n\t:hd=%s:", hp->homedir->string); + } + /* FdC: dump_file (see above) */ + /* FdC: domain_name (see above) */ + /* FdC: root_path (see below) */ + if (hp->flags.name_switch && hp->flags.send_name) { + fprintf(fp, "\\\n\t:hn:"); + } + if (hp->flags.htype) { + int hlen = haddrlength(hp->htype); + fprintf(fp, "\\\n\t:ht=%u:", (unsigned) hp->htype); + if (hp->flags.haddr) { + fprintf(fp, "ha=\"%s\":", + haddrtoa(hp->haddr, hlen)); + } + } + if (hp->flags.impress_server) { + fprintf(fp, "\\\n\t:im="); + list_ipaddresses(fp, hp->impress_server); + fprintf(fp, ":"); + } + /* NetBSD: swap_server (see below) */ + if (hp->flags.iaddr) { + fprintf(fp, "\\\n\t:ip=%s:", inet_ntoa(hp->iaddr)); + } + if (hp->flags.log_server) { + fprintf(fp, "\\\n\t:lg="); + list_ipaddresses(fp, hp->log_server); + fprintf(fp, ":"); + } + if (hp->flags.lpr_server) { + fprintf(fp, "\\\n\t:lp="); + list_ipaddresses(fp, hp->lpr_server); + fprintf(fp, ":"); + } + if (hp->flags.msg_size) { + fprintf(fp, "\\\n\t:ms=%d:", hp->msg_size); + } + if (hp->flags.min_wait) { + fprintf(fp, "\\\n\t:mw=%d:", hp->min_wait); + } + if (hp->flags.name_server) { + fprintf(fp, "\\\n\t:ns="); + list_ipaddresses(fp, hp->name_server); + fprintf(fp, ":"); + } + if (hp->flags.ntp_server) { + fprintf(fp, "\\\n\t:nt="); + list_ipaddresses(fp, hp->ntp_server); + fprintf(fp, ":"); + } + if (hp->flags.reply_addr) { + fprintf(fp, "\\\n\t:ra=%s:", inet_ntoa(hp->reply_addr)); + } + if (hp->flags.rlp_server) { + fprintf(fp, "\\\n\t:rl="); + list_ipaddresses(fp, hp->rlp_server); + fprintf(fp, ":"); + } + if (hp->flags.root_path) { + fprintf(fp, "\\\n\t:rp=%s:", hp->root_path->string); + } + if (hp->flags.bootserver) { + fprintf(fp, "\\\n\t:sa=%s:", inet_ntoa(hp->bootserver)); + } + if (hp->flags.subnet_mask) { + fprintf(fp, "\\\n\t:sm=%s:", inet_ntoa(hp->subnet_mask)); + } + if (hp->flags.swap_server) { + fprintf(fp, "\\\n\t:sw=%s:", inet_ntoa(hp->subnet_mask)); + } + if (hp->flags.tftpdir) { + fprintf(fp, "\\\n\t:td=%s:", hp->tftpdir->string); + } + /* NetBSD: rootpath (see above) */ + /* NetBSD: domainname (see above) */ + /* NetBSD: dumpfile (see above) */ + if (hp->flags.time_offset) { + fprintf(fp, "\\\n\t:to=%ld:", hp->time_offset); + } + if (hp->flags.time_server) { + fprintf(fp, "\\\n\t:ts="); + list_ipaddresses(fp, hp->time_server); + fprintf(fp, ":"); + } + if (hp->flags.vm_cookie) { + fprintf(fp, "\\\n\t:vm="); + if (!bcmp(hp->vm_cookie, vm_rfc1048, 4)) { + fprintf(fp, "rfc1048:"); + } else if (!bcmp(hp->vm_cookie, vm_cmu, 4)) { + fprintf(fp, "cmu:"); + } else { + fprintf(fp, "%d.%d.%d.%d:", + (int) ((hp->vm_cookie)[0]), + (int) ((hp->vm_cookie)[1]), + (int) ((hp->vm_cookie)[2]), + (int) ((hp->vm_cookie)[3])); + } + } + if (hp->flags.nis_domain) { + fprintf(fp, "\\\n\t:yd=%s:", + hp->nis_domain->string); + } + if (hp->flags.nis_server) { + fprintf(fp, "\\\n\t:ys="); + list_ipaddresses(fp, hp->nis_server); + fprintf(fp, ":"); + } + /* + * XXX - Add new tags here (or above, + * so they print in alphabetical order). + */ + + if (hp->flags.generic) { + dump_generic(fp, hp->generic); + } + } +} + + +static void +dump_generic(fp, generic) + FILE *fp; + struct shared_bindata *generic; +{ + u_char *bp = generic->data; + u_char *ep = bp + generic->length; + u_char tag; + int len; + + while (bp < ep) { + tag = *bp++; + if (tag == TAG_PAD) + continue; + if (tag == TAG_END) + return; + len = *bp++; + if (bp + len > ep) { + fprintf(fp, " #junk in generic! :"); + return; + } + fprintf(fp, "\\\n\t:T%d=", tag); + while (len) { + fprintf(fp, "%02X", *bp); + bp++; + len--; + if (len) + fprintf(fp, "."); + } + fprintf(fp, ":"); + } +} + + + +/* + * Dump an entire struct in_addr_list of IP addresses to the indicated file. + * + * The addresses are printed in standard ASCII "dot" notation and separated + * from one another by a single space. A single leading space is also + * printed before the first adddress. + * + * Null lists produce no output (and no error). + */ + +static void +list_ipaddresses(fp, ipptr) + FILE *fp; + struct in_addr_list *ipptr; +{ + unsigned count; + struct in_addr *addrptr; + + if (ipptr) { + count = ipptr->addrcount; + addrptr = ipptr->addr; + while (count > 0) { + fprintf(fp, "%s", inet_ntoa(*addrptr++)); + count--; + if (count) + fprintf(fp, ", "); + } + } +} + +#endif /* DEBUG */ + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/getether.c b/libexec/bootpd/getether.c new file mode 100644 index 0000000..cf01b03 --- /dev/null +++ b/libexec/bootpd/getether.c @@ -0,0 +1,386 @@ +/* + * 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> + * + * $Id$ + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#ifndef NO_UNISTD +#include <unistd.h> +#endif + +#include <ctype.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)); + strncpy(&ifrnit.ifr_name[0], ifname, IFNAMSIZ); + + nit = open("/dev/nit", 0); + if (nit < 0) { + report(LOG_ERR, "getether: open /dev/nit: %s", + get_errmsg()); + return rc; + } + do { + if (ioctl(nit, NIOCBIND, &ifrnit) < 0) { + report(LOG_ERR, "getether: NIOCBIND on nit"); + break; + } + if (ioctl(nit, SIOCGIFADDR, &ifrnit) < 0) { + report(LOG_ERR, "getether: SIOCGIFADDR on nit"); + break; + } + bcopy(&ifrnit.ifr_addr.sa_data[0], eap, EALEN); + rc = 0; + } while (0); + close(nit); + return rc; +} + +#define GETETHER +#endif /* SUNOS */ + + +#if defined(__FreeBSD__) || defined(__NetBSD__) +/* Thanks to John Brezak <brezak@ch.hp.com> for this code. */ +#include <sys/ioctl.h> +#include <sys/time.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> + +getether(ifname, eap) + char *ifname; /* interface name from ifconfig structure */ + char *eap; /* Ether address (output) */ +{ + int fd, rc = -1; + register int n; + struct ifreq ibuf[16], ifr; + struct ifconf ifc; + register struct ifreq *ifrp, *ifend; + + /* Fetch the interface configuration */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + report(LOG_ERR, "getether: socket %s: %s", ifname, get_errmsg()); + return (fd); + } + ifc.ifc_len = sizeof(ibuf); + ifc.ifc_buf = (caddr_t) ibuf; + if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) < 0 || + ifc.ifc_len < sizeof(struct ifreq)) { + report(LOG_ERR, "getether: SIOCGIFCONF: %s", get_errmsg); + goto out; + } + /* Search interface configuration list for link layer address. */ + ifrp = ibuf; + ifend = (struct ifreq *) ((char *) ibuf + ifc.ifc_len); + while (ifrp < ifend) { + /* Look for interface */ + if (strcmp(ifname, ifrp->ifr_name) == 0 && + ifrp->ifr_addr.sa_family == AF_LINK && + ((struct sockaddr_dl *) &ifrp->ifr_addr)->sdl_type == IFT_ETHER) { + bcopy(LLADDR((struct sockaddr_dl *) &ifrp->ifr_addr), eap, EALEN); + rc = 0; + break; + } + /* Bump interface config pointer */ + n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); + if (n < sizeof(*ifrp)) + n = sizeof(*ifrp); + ifrp = (struct ifreq *) ((char *) ifrp + n); + } + + out: + close(fd); + return (rc); +} + +#define GETETHER +#endif /* __NetBSD__ */ + + +#ifdef SVR4 +/* + * This is for "Streams TCP/IP" by Lachman Associates. + * They sure made this cumbersome! -gwr + */ + +#include <sys/sockio.h> +#include <sys/dlpi.h> +#include <stropts.h> +#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 */ + + sprintf(devname, "/dev/%s", ifname); + fd = open(devname, 2); + if (fd < 0) { + /* Try without the trailing digit. */ + char *p = devname + 5; + while (isalpha(*p)) + p++; + if (isdigit(*p)) { + unit = *p - '0'; + *p = '\0'; + } + fd = open(devname, 2); + if (fd < 0) { + report(LOG_ERR, "getether: open %s: %s", + devname, get_errmsg()); + return rc; + } + } +#ifdef DL_ATTACH_REQ + /* + * If this is a "Style 2" DLPI, then we must "attach" first + * to tell the driver which unit (board, port) we want. + * For now, decide this based on the device name. + * (Should do "info_req" and check dl_provider_style ...) + */ + if (unit >= 0) { + memset(tmpbuf, 0, sizeof(tmpbuf)); + dlp = (union DL_primitives *) tmpbuf; + dlp->dl_primitive = DL_ATTACH_REQ; + dlp->attach_req.dl_ppa = unit; + cbuf.buf = tmpbuf; + cbuf.len = DL_ATTACH_REQ_SIZE; + if (putmsg(fd, &cbuf, NULL, 0) < 0) { + report(LOG_ERR, "getether: attach: putmsg: %s", get_errmsg()); + goto out; + } + /* Recv the ack. */ + cbuf.buf = tmpbuf; + cbuf.maxlen = sizeof(tmpbuf); + flags = 0; + if (getmsg(fd, &cbuf, NULL, &flags) < 0) { + report(LOG_ERR, "getether: attach: getmsg: %s", get_errmsg()); + goto out; + } + /* + * Check the type, etc. + */ + if (dlp->dl_primitive == DL_ERROR_ACK) { + report(LOG_ERR, "getether: attach: dlpi_errno=%d, unix_errno=%d", + dlp->error_ack.dl_errno, + dlp->error_ack.dl_unix_errno); + goto out; + } + if (dlp->dl_primitive != DL_OK_ACK) { + report(LOG_ERR, "getether: attach: not OK or ERROR"); + goto out; + } + } /* unit >= 0 */ +#endif /* DL_ATTACH_REQ */ + + /* + * Get the Ethernet address the same way the ARP module + * does when it is pushed onto a new stream (bind). + * One should instead be able just do an dl_info_req + * but many drivers do not supply the hardware address + * in the response to dl_info_req (they MUST supply it + * for dl_bind_ack because the ARP module requires it). + */ + memset(tmpbuf, 0, sizeof(tmpbuf)); + dlp = (union DL_primitives *) tmpbuf; + dlp->dl_primitive = DL_BIND_REQ; + dlp->bind_req.dl_sap = 0x8FF; /* XXX - Unused SAP */ + cbuf.buf = tmpbuf; + cbuf.len = DL_BIND_REQ_SIZE; + if (putmsg(fd, &cbuf, NULL, 0) < 0) { + report(LOG_ERR, "getether: bind: putmsg: %s", get_errmsg()); + goto out; + } + /* Recv the ack. */ + cbuf.buf = tmpbuf; + cbuf.maxlen = sizeof(tmpbuf); + flags = 0; + if (getmsg(fd, &cbuf, NULL, &flags) < 0) { + report(LOG_ERR, "getether: bind: getmsg: %s", get_errmsg()); + goto out; + } + /* + * Check the type, etc. + */ + if (dlp->dl_primitive == DL_ERROR_ACK) { + report(LOG_ERR, "getether: bind: dlpi_errno=%d, unix_errno=%d", + dlp->error_ack.dl_errno, + dlp->error_ack.dl_unix_errno); + goto out; + } + if (dlp->dl_primitive != DL_BIND_ACK) { + report(LOG_ERR, "getether: bind: not OK or ERROR"); + goto out; + } + if (dlp->bind_ack.dl_addr_offset == 0) { + report(LOG_ERR, "getether: bind: ack has no address"); + goto out; + } + if (dlp->bind_ack.dl_addr_length < EALEN) { + report(LOG_ERR, "getether: bind: ack address truncated"); + goto out; + } + /* + * Copy the Ethernet address out of the message. + */ + enaddr = tmpbuf + dlp->bind_ack.dl_addr_offset; + memcpy(eap, enaddr, EALEN); + rc = 0; + + out: + close(fd); + return rc; +} + +#define GETETHER +#endif /* SVR4 */ + + +#ifdef __linux__ +/* + * This is really easy on Linux! This version (for linux) + * written by Nigel Metheringham <nigelm@ohm.york.ac.uk> 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..2f98299 --- /dev/null +++ b/libexec/bootpd/getether.h @@ -0,0 +1,7 @@ +/* getether.h */ + +#ifdef __STDC__ +extern int getether(char *ifname, char *eaptr); +#else +extern int getether(); +#endif diff --git a/libexec/bootpd/getif.c b/libexec/bootpd/getif.c new file mode 100644 index 0000000..ecfa610 --- /dev/null +++ b/libexec/bootpd/getif.c @@ -0,0 +1,147 @@ +/* + * getif.c : get an interface structure + * + * $Id$ + */ + +#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(addrp, &(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..c51dafd --- /dev/null +++ b/libexec/bootpd/getif.h @@ -0,0 +1,7 @@ +/* getif.h */ + +#ifdef __STDC__ +extern struct ifreq *getif(int, struct in_addr *); +#else +extern struct ifreq *getif(); +#endif diff --git a/libexec/bootpd/hash.c b/libexec/bootpd/hash.c new file mode 100644 index 0000000..f959751 --- /dev/null +++ b/libexec/bootpd/hash.c @@ -0,0 +1,423 @@ +/************************************************************************ + 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. + + $Id$ + +************************************************************************/ + +/* + * Generalized hash table ADT + * + * Provides multiple, dynamically-allocated, variable-sized hash tables on + * various data and keys. + * + * This package attempts to follow some of the coding conventions suggested + * by Bob Sidebotham and the AFS Clean Code Committee of the + * Information Technology Center at Carnegie Mellon. + */ + + +#include <sys/types.h> +#include <stdlib.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#define bcopy(a,b,c) memcpy(b,a,c) +#define bzero(p,l) memset(p,0,l) +#define bcmp(a,b,c) memcmp(a,b,c) +#endif + +#include "hash.h" + +#define TRUE 1 +#define FALSE 0 +#ifndef NULL +#define NULL 0 +#endif + +/* + * This can be changed to make internal routines visible to debuggers, etc. + */ +#ifndef PRIVATE +#define PRIVATE static +#endif + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +PRIVATE void hashi_FreeMembers P((hash_member *, hash_freefp)); + +#undef P + + + +/* + * Hash table initialization routine. + * + * This routine creates and intializes a hash table of size "tablesize" + * entries. Successful calls return a pointer to the hash table (which must + * be passed to other hash routines to identify the hash table). Failed + * calls return NULL. + */ + +hash_tbl * +hash_Init(tablesize) + unsigned tablesize; +{ + register hash_tbl *hashtblptr; + register unsigned totalsize; + + if (tablesize > 0) { + totalsize = sizeof(hash_tbl) + + sizeof(hash_member *) * (tablesize - 1); + hashtblptr = (hash_tbl *) malloc(totalsize); + if (hashtblptr) { + bzero((char *) hashtblptr, totalsize); + hashtblptr->size = tablesize; /* Success! */ + hashtblptr->bucketnum = 0; + hashtblptr->member = (hashtblptr->table)[0]; + } + } else { + hashtblptr = NULL; /* Disallow zero-length tables */ + } + return hashtblptr; /* NULL if failure */ +} + + + +/* + * Frees an entire linked list of bucket members (used in the open + * hashing scheme). Does nothing if the passed pointer is NULL. + */ + +PRIVATE void +hashi_FreeMembers(bucketptr, free_data) + hash_member *bucketptr; + hash_freefp free_data; +{ + hash_member *nextbucket; + while (bucketptr) { + nextbucket = bucketptr->next; + (*free_data) (bucketptr->data); + free((char *) bucketptr); + bucketptr = nextbucket; + } +} + + + + +/* + * This routine re-initializes the hash table. It frees all the allocated + * memory and resets all bucket pointers to NULL. + */ + +void +hash_Reset(hashtable, free_data) + hash_tbl *hashtable; + hash_freefp free_data; +{ + hash_member **bucketptr; + unsigned i; + + bucketptr = hashtable->table; + for (i = 0; i < hashtable->size; i++) { + hashi_FreeMembers(*bucketptr, free_data); + *bucketptr++ = NULL; + } + hashtable->bucketnum = 0; + hashtable->member = (hashtable->table)[0]; +} + + + +/* + * Generic hash function to calculate a hash code from the given string. + * + * For each byte of the string, this function left-shifts the value in an + * accumulator and then adds the byte into the accumulator. The contents of + * the accumulator is returned after the entire string has been processed. + * It is assumed that this result will be used as the "hashcode" parameter in + * calls to other functions in this package. These functions automatically + * adjust the hashcode for the size of each hashtable. + * + * This algorithm probably works best when the hash table size is a prime + * number. + * + * Hopefully, this function is better than the previous one which returned + * the sum of the squares of all the bytes. I'm still open to other + * suggestions for a default hash function. The programmer is more than + * welcome to supply his/her own hash function as that is one of the design + * features of this package. + */ + +unsigned +hash_HashFunction(string, len) + unsigned char *string; + register unsigned len; +{ + register unsigned accum; + + accum = 0; + for (; len > 0; len--) { + accum <<= 1; + accum += (unsigned) (*string++ & 0xFF); + } + return accum; +} + + + +/* + * Returns TRUE if at least one entry for the given key exists; FALSE + * otherwise. + */ + +int +hash_Exists(hashtable, hashcode, compare, key) + hash_tbl *hashtable; + unsigned hashcode; + hash_cmpfp compare; + hash_datum *key; +{ + register hash_member *memberptr; + + memberptr = (hashtable->table)[hashcode % (hashtable->size)]; + while (memberptr) { + if ((*compare) (key, memberptr->data)) { + return TRUE; /* Entry does exist */ + } + memberptr = memberptr->next; + } + return FALSE; /* Entry does not exist */ +} + + + +/* + * Insert the data item "element" into the hash table using "hashcode" + * to determine the bucket number, and "compare" and "key" to determine + * its uniqueness. + * + * If the insertion is successful 0 is returned. If a matching entry + * already exists in the given bucket of the hash table, or some other error + * occurs, -1 is returned and the insertion is not done. + */ + +int +hash_Insert(hashtable, hashcode, compare, key, element) + hash_tbl *hashtable; + unsigned hashcode; + hash_cmpfp compare; + hash_datum *key, *element; +{ + hash_member *temp; + + hashcode %= hashtable->size; + if (hash_Exists(hashtable, hashcode, compare, key)) { + return -1; /* At least one entry already exists */ + } + temp = (hash_member *) malloc(sizeof(hash_member)); + if (!temp) + return -1; /* malloc failed! */ + + temp->data = element; + temp->next = (hashtable->table)[hashcode]; + (hashtable->table)[hashcode] = temp; + return 0; /* Success */ +} + + + +/* + * Delete all data elements which match the given key. If at least one + * element is found and the deletion is successful, 0 is returned. + * If no matching elements can be found in the hash table, -1 is returned. + */ + +int +hash_Delete(hashtable, hashcode, compare, key, free_data) + hash_tbl *hashtable; + unsigned hashcode; + hash_cmpfp compare; + hash_datum *key; + hash_freefp free_data; +{ + hash_member *memberptr, *tempptr; + hash_member *previous = NULL; + int retval; + + retval = -1; + hashcode %= hashtable->size; + + /* + * Delete the first member of the list if it matches. Since this moves + * the second member into the first position we have to keep doing this + * over and over until it no longer matches. + */ + memberptr = (hashtable->table)[hashcode]; + while (memberptr && (*compare) (key, memberptr->data)) { + (hashtable->table)[hashcode] = memberptr->next; + /* + * Stop hashi_FreeMembers() from deleting the whole list! + */ + memberptr->next = NULL; + hashi_FreeMembers(memberptr, free_data); + memberptr = (hashtable->table)[hashcode]; + retval = 0; + } + + /* + * Now traverse the rest of the list + */ + if (memberptr) { + previous = memberptr; + memberptr = memberptr->next; + } + while (memberptr) { + if ((*compare) (key, memberptr->data)) { + tempptr = memberptr; + previous->next = memberptr = memberptr->next; + /* + * Put the brakes on hashi_FreeMembers(). . . . + */ + tempptr->next = NULL; + hashi_FreeMembers(tempptr, free_data); + retval = 0; + } else { + previous = memberptr; + memberptr = memberptr->next; + } + } + return retval; +} + + + +/* + * Locate and return the data entry associated with the given key. + * + * If the data entry is found, a pointer to it is returned. Otherwise, + * NULL is returned. + */ + +hash_datum * +hash_Lookup(hashtable, hashcode, compare, key) + hash_tbl *hashtable; + unsigned hashcode; + hash_cmpfp compare; + hash_datum *key; +{ + hash_member *memberptr; + + memberptr = (hashtable->table)[hashcode % (hashtable->size)]; + while (memberptr) { + if ((*compare) (key, memberptr->data)) { + return (memberptr->data); + } + memberptr = memberptr->next; + } + return NULL; +} + + + +/* + * Return the next available entry in the hashtable for a linear search + */ + +hash_datum * +hash_NextEntry(hashtable) + hash_tbl *hashtable; +{ + register unsigned bucket; + register hash_member *memberptr; + + /* + * First try to pick up where we left off. + */ + memberptr = hashtable->member; + if (memberptr) { + hashtable->member = memberptr->next; /* Set up for next call */ + return memberptr->data; /* Return the data */ + } + /* + * We hit the end of a chain, so look through the array of buckets + * until we find a new chain (non-empty bucket) or run out of buckets. + */ + bucket = hashtable->bucketnum + 1; + while ((bucket < hashtable->size) && + !(memberptr = (hashtable->table)[bucket])) { + bucket++; + } + + /* + * Check to see if we ran out of buckets. + */ + if (bucket >= hashtable->size) { + /* + * Reset to top of table for next call. + */ + hashtable->bucketnum = 0; + hashtable->member = (hashtable->table)[0]; + /* + * But return end-of-table indication to the caller this time. + */ + return NULL; + } + /* + * Must have found a non-empty bucket. + */ + hashtable->bucketnum = bucket; + hashtable->member = memberptr->next; /* Set up for next call */ + return memberptr->data; /* Return the data */ +} + + + +/* + * Return the first entry in a hash table for a linear search + */ + +hash_datum * +hash_FirstEntry(hashtable) + hash_tbl *hashtable; +{ + hashtable->bucketnum = 0; + hashtable->member = (hashtable->table)[0]; + return hash_NextEntry(hashtable); +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/hash.h b/libexec/bootpd/hash.h new file mode 100644 index 0000000..51d0a5e --- /dev/null +++ b/libexec/bootpd/hash.h @@ -0,0 +1,158 @@ +#ifndef HASH_H +#define HASH_H +/* hash.h */ +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + +/* + * Generalized hash table ADT + * + * Provides multiple, dynamically-allocated, variable-sized hash tables on + * various data and keys. + * + * This package attempts to follow some of the coding conventions suggested + * by Bob Sidebotham and the AFS Clean Code Committee. + */ + + +/* + * The user must supply the following: + * + * 1. A comparison function which is declared as: + * + * int compare(data1, data2) + * hash_datum *data1, *data2; + * + * This function must compare the desired fields of data1 and + * data2 and return TRUE (1) if the data should be considered + * equivalent (i.e. have the same key value) or FALSE (0) + * otherwise. This function is called through a pointer passed to + * the various hashtable functions (thus pointers to different + * functions may be passed to effect different tests on different + * hash tables). + * + * Internally, all the functions of this package always call the + * compare function with the "key" parameter as the first parameter, + * and a full data element as the second parameter. Thus, the key + * and element arguments to functions such as hash_Lookup() may + * actually be of different types and the programmer may provide a + * compare function which compares the two different object types + * as desired. + * + * Example: + * + * int compare(key, element) + * char *key; + * struct some_complex_structure *element; + * { + * return !strcmp(key, element->name); + * } + * + * key = "John C. Doe" + * element = &some_complex_structure + * hash_Lookup(table, hashcode, compare, key); + * + * 2. A hash function yielding an unsigned integer value to be used + * as the hashcode (index into the hashtable). Thus, the user + * may hash on whatever data is desired and may use several + * different hash functions for various different hash tables. + * The actual hash table index will be the passed hashcode modulo + * the hash table size. + * + * A generalized hash function, hash_HashFunction(), is included + * with this package to make things a little easier. It is not + * guarenteed to use the best hash algorithm in existence. . . . + */ + + + +/* + * Various hash table definitions + */ + + +/* + * Define "hash_datum" as a universal data type + */ +#ifdef __STDC__ +typedef void hash_datum; +#else +typedef char hash_datum; +#endif + +typedef struct hash_memberstruct hash_member; +typedef struct hash_tblstruct hash_tbl; +typedef struct hash_tblstruct_hdr hash_tblhdr; + +struct hash_memberstruct { + hash_member *next; + hash_datum *data; +}; + +struct hash_tblstruct_hdr { + unsigned size, bucketnum; + hash_member *member; +}; + +struct hash_tblstruct { + unsigned size, bucketnum; + hash_member *member; /* Used for linear dump */ + hash_member *table[1]; /* Dynamically extended */ +}; + +/* ANSI function prototypes or empty arg list? */ +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +typedef int (*hash_cmpfp) P((hash_datum *, hash_datum *)); +typedef void (*hash_freefp) P((hash_datum *)); + +extern hash_tbl *hash_Init P((u_int tablesize)); + +extern void hash_Reset P((hash_tbl *tbl, hash_freefp)); + +extern unsigned hash_HashFunction P((u_char *str, u_int len)); + +extern int hash_Exists P((hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key)); + +extern int hash_Insert P((hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key, + hash_datum *element)); + +extern int hash_Delete P((hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key, + hash_freefp)); + +extern hash_datum *hash_Lookup P((hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key)); + +extern hash_datum *hash_FirstEntry P((hash_tbl *)); + +extern hash_datum *hash_NextEntry P((hash_tbl *)); + +#undef P + +#endif /* HASH_H */ diff --git a/libexec/bootpd/hwaddr.c b/libexec/bootpd/hwaddr.c new file mode 100644 index 0000000..bc41cff --- /dev/null +++ b/libexec/bootpd/hwaddr.c @@ -0,0 +1,352 @@ +/* + * hwaddr.c - routines that deal with hardware addresses. + * (i.e. Ethernet) + * + * $Id$ + */ + +#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 __P((struct in_addr *, char *, int)); +#endif +#endif + +#include "bptypes.h" +#include "hwaddr.h" +#include "report.h" + +extern int debug; + +/* + * Hardware address lengths (in bytes) and network name based on hardware + * type code. List in order specified by Assigned Numbers RFC; Array index + * is hardware type code. Entries marked as zero are unknown to the author + * at this time. . . . + */ + +struct hwinfo hwinfolist[] = +{ + {0, "Reserved"}, /* Type 0: Reserved (don't use this) */ + {6, "Ethernet"}, /* Type 1: 10Mb Ethernet (48 bits) */ + {1, "3Mb Ethernet"}, /* Type 2: 3Mb Ethernet (8 bits) */ + {0, "AX.25"}, /* Type 3: Amateur Radio AX.25 */ + {1, "ProNET"}, /* Type 4: Proteon ProNET Token Ring */ + {0, "Chaos"}, /* Type 5: Chaos */ + {6, "IEEE 802"}, /* Type 6: IEEE 802 Networks */ + {0, "ARCNET"} /* Type 7: ARCNET */ +}; +int hwinfocnt = sizeof(hwinfolist) / sizeof(hwinfolist[0]); + + +/* + * Setup the arp cache so that IP address 'ia' will be temporarily + * bound to hardware address 'ha' of length 'len'. + */ +void +setarp(s, ia, 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); + sprintf(buf, "arp -d %s; arp -s %s %s temp", + a, a, haddrtoa(haddr, halen)); + if (debug > 2) + report(LOG_INFO, buf); + status = system(buf); + if (status) + report(LOG_ERR, "arp failed, exit code=0x%x", status); + return; +#endif /* ! 4.4 BSD */ +#endif /* SIOCSARP */ +} + + +/* + * Convert a hardware address to an ASCII string. + */ +char * +haddrtoa(haddr, hlen) + u_char *haddr; + int hlen; +{ + static char haddrbuf[3 * MAXHADDRLEN + 1]; + char *bufptr; + + if (hlen > MAXHADDRLEN) + hlen = MAXHADDRLEN; + + bufptr = haddrbuf; + while (hlen > 0) { + sprintf(bufptr, "%02X:", (unsigned) (*haddr++ & 0xFF)); + bufptr += 3; + hlen--; + } + bufptr[-1] = 0; + return (haddrbuf); +} + + +/* + * haddr_conv802() + * -------------- + * + * Converts a backwards address to a canonical address and a canonical address + * to a backwards address. + * + * INPUTS: + * adr_in - pointer to six byte string to convert (unsigned char *) + * addr_len - how many bytes to convert + * + * OUTPUTS: + * addr_out - The string is updated to contain the converted address. + * + * CALLER: + * many + * + * DATA: + * Uses conv802table to bit-reverse the address bytes. + */ + +static u_char conv802table[256] = +{ + /* 0x00 */ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + /* 0x08 */ 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + /* 0x10 */ 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + /* 0x18 */ 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + /* 0x20 */ 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + /* 0x28 */ 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + /* 0x30 */ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + /* 0x38 */ 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + /* 0x40 */ 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + /* 0x48 */ 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + /* 0x50 */ 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + /* 0x58 */ 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + /* 0x60 */ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + /* 0x68 */ 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + /* 0x70 */ 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + /* 0x78 */ 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + /* 0x80 */ 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + /* 0x88 */ 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + /* 0x90 */ 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + /* 0x98 */ 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + /* 0xA0 */ 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + /* 0xA8 */ 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + /* 0xB0 */ 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + /* 0xB8 */ 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + /* 0xC0 */ 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + /* 0xC8 */ 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + /* 0xD0 */ 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + /* 0xD8 */ 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + /* 0xE0 */ 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + /* 0xE8 */ 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + /* 0xF0 */ 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + /* 0xF8 */ 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF, +}; + +void +haddr_conv802(addr_in, addr_out, len) + register u_char *addr_in, *addr_out; + int len; +{ + u_char *lim; + + lim = addr_out + len; + while (addr_out < lim) + *addr_out++ = conv802table[*addr_in++]; +} + +#if 0 +/* + * For the record, here is a program to generate the + * bit-reverse table above. + */ +static int +bitrev(n) + int n; +{ + int i, r; + + r = 0; + for (i = 0; i < 8; i++) { + r <<= 1; + r |= (n & 1); + n >>= 1; + } + return r; +} + +main() +{ + int i; + for (i = 0; i <= 0xFF; i++) { + if ((i & 7) == 0) + printf("/* 0x%02X */", i); + printf(" 0x%02X,", bitrev(i)); + if ((i & 7) == 7) + printf("\n"); + } +} + +#endif + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/hwaddr.h b/libexec/bootpd/hwaddr.h new file mode 100644 index 0000000..b0a8c29 --- /dev/null +++ b/libexec/bootpd/hwaddr.h @@ -0,0 +1,44 @@ +/* + * hwaddr.h + * + * $Id$ + */ + +#ifndef HWADDR_H +#define HWADDR_H + +#define MAXHADDRLEN 8 /* Max hw address length in bytes */ + +/* + * This structure holds information about a specific network type. The + * length of the network hardware address is stored in "hlen". + * The string pointed to by "name" is the cononical name of the network. + */ +struct hwinfo { + unsigned int hlen; + char *name; +}; + +extern struct hwinfo hwinfolist[]; +extern int hwinfocnt; + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern void setarp P((int, struct in_addr *, int, u_char *, int)); +extern char *haddrtoa P((u_char *, int)); +extern void haddr_conv802 P((u_char *, u_char *, int)); + +#undef P + +/* + * Return the length in bytes of a hardware address of the given type. + * Return the canonical name of the network of the given type. + */ +#define haddrlength(type) ((hwinfolist[(int) (type)]).hlen) +#define netname(type) ((hwinfolist[(int) (type)]).name) + +#endif /* HWADDR_H */ diff --git a/libexec/bootpd/lookup.c b/libexec/bootpd/lookup.c new file mode 100644 index 0000000..af3b37a --- /dev/null +++ b/libexec/bootpd/lookup.c @@ -0,0 +1,129 @@ +/* + * lookup.c - Lookup IP address, HW address, netmask + * + * $Id$ + */ + +#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..04805d8 --- /dev/null +++ b/libexec/bootpd/lookup.h @@ -0,0 +1,15 @@ +/* lookup.h */ + +#include "bptypes.h" /* for int32, u_int32 */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern u_char *lookup_hwa P((char *hostname, int htype)); +extern int lookup_ipa P((char *hostname, u_int32 *addr)); +extern int lookup_netmask P((u_int32 addr, u_int32 *mask)); + +#undef P diff --git a/libexec/bootpd/patchlevel.h b/libexec/bootpd/patchlevel.h new file mode 100644 index 0000000..85566b5 --- /dev/null +++ b/libexec/bootpd/patchlevel.h @@ -0,0 +1,8 @@ +/* + * patchlevel.h + * + * $Id$ + */ + +#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..8dfd430 --- /dev/null +++ b/libexec/bootpd/readfile.c @@ -0,0 +1,2088 @@ +/************************************************************************ + 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. + + $Id$ + +************************************************************************/ + +/* + * bootpd configuration file reading code. + * + * The routines in this file deal with reading, interpreting, and storing + * the information found in the bootpd configuration file (usually + * /etc/bootptab). + */ + + +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/time.h> +#include <netinet/in.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include <syslog.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#define bcopy(a,b,c) memcpy(b,a,c) +#define bzero(p,l) memset(p,0,l) +#define bcmp(a,b,c) memcmp(a,b,c) +#endif + +#include "bootp.h" +#include "hash.h" +#include "hwaddr.h" +#include "lookup.h" +#include "readfile.h" +#include "report.h" +#include "tzone.h" +#include "bootpd.h" + +#define HASHTABLESIZE 257 /* Hash table size (prime) */ + +/* Non-standard hardware address type (see bootp.h) */ +#define HTYPE_DIRECT 0 + +/* Error codes returned by eval_symbol: */ +#define SUCCESS 0 +#define E_END_OF_ENTRY (-1) +#define E_SYNTAX_ERROR (-2) +#define E_UNKNOWN_SYMBOL (-3) +#define E_BAD_IPADDR (-4) +#define E_BAD_HWADDR (-5) +#define E_BAD_LONGWORD (-6) +#define E_BAD_HWATYPE (-7) +#define E_BAD_PATHNAME (-8) +#define E_BAD_VALUE (-9) + +/* Tag idendities. */ +#define SYM_NULL 0 +#define SYM_BOOTFILE 1 +#define SYM_COOKIE_SERVER 2 +#define SYM_DOMAIN_SERVER 3 +#define SYM_GATEWAY 4 +#define SYM_HWADDR 5 +#define SYM_HOMEDIR 6 +#define SYM_HTYPE 7 +#define SYM_IMPRESS_SERVER 8 +#define SYM_IPADDR 9 +#define SYM_LOG_SERVER 10 +#define SYM_LPR_SERVER 11 +#define SYM_NAME_SERVER 12 +#define SYM_RLP_SERVER 13 +#define SYM_SUBNET_MASK 14 +#define SYM_TIME_OFFSET 15 +#define SYM_TIME_SERVER 16 +#define SYM_VENDOR_MAGIC 17 +#define SYM_SIMILAR_ENTRY 18 +#define SYM_NAME_SWITCH 19 +#define SYM_BOOTSIZE 20 +#define SYM_BOOT_SERVER 22 +#define SYM_TFTPDIR 23 +#define SYM_DUMP_FILE 24 +#define SYM_DOMAIN_NAME 25 +#define SYM_SWAP_SERVER 26 +#define SYM_ROOT_PATH 27 +#define SYM_EXTEN_FILE 28 +#define SYM_REPLY_ADDR 29 +#define SYM_NIS_DOMAIN 30 /* RFC 1533 */ +#define SYM_NIS_SERVER 31 /* RFC 1533 */ +#define SYM_NTP_SERVER 32 /* RFC 1533 */ +#define SYM_EXEC_FILE 33 /* YORK_EX_OPTION */ +#define SYM_MSG_SIZE 34 +#define SYM_MIN_WAIT 35 +/* XXX - Add new tags here */ + +#define OP_ADDITION 1 /* Operations on tags */ +#define OP_DELETION 2 +#define OP_BOOLEAN 3 + +#define MAXINADDRS 16 /* Max size of an IP address list */ +#define MAXBUFLEN 256 /* Max temp buffer space */ +#define MAXENTRYLEN 2048 /* Max size of an entire entry */ + + + +/* + * Structure used to map a configuration-file symbol (such as "ds") to a + * unique integer. + */ + +struct symbolmap { + char *symbol; + int symbolcode; +}; + + +struct htypename { + char *name; + byte htype; +}; + + +PRIVATE int nhosts; /* Number of hosts (/w hw or IP address) */ +PRIVATE int nentries; /* Total number of entries */ +PRIVATE int32 modtime = 0; /* Last modification time of bootptab */ +PRIVATE char *current_hostname; /* Name of the current entry. */ +PRIVATE char current_tagname[8]; + +/* + * List of symbolic names used in the bootptab file. The order and actual + * values of the symbol codes (SYM_. . .) are unimportant, but they must + * all be unique. + */ + +PRIVATE struct symbolmap symbol_list[] = { + {"bf", SYM_BOOTFILE}, + {"bs", SYM_BOOTSIZE}, + {"cs", SYM_COOKIE_SERVER}, + {"df", SYM_DUMP_FILE}, + {"dn", SYM_DOMAIN_NAME}, + {"ds", SYM_DOMAIN_SERVER}, + {"ef", SYM_EXTEN_FILE}, + {"ex", SYM_EXEC_FILE}, /* YORK_EX_OPTION */ + {"gw", SYM_GATEWAY}, + {"ha", SYM_HWADDR}, + {"hd", SYM_HOMEDIR}, + {"hn", SYM_NAME_SWITCH}, + {"ht", SYM_HTYPE}, + {"im", SYM_IMPRESS_SERVER}, + {"ip", SYM_IPADDR}, + {"lg", SYM_LOG_SERVER}, + {"lp", SYM_LPR_SERVER}, + {"ms", SYM_MSG_SIZE}, + {"mw", SYM_MIN_WAIT}, + {"ns", SYM_NAME_SERVER}, + {"nt", SYM_NTP_SERVER}, + {"ra", SYM_REPLY_ADDR}, + {"rl", SYM_RLP_SERVER}, + {"rp", SYM_ROOT_PATH}, + {"sa", SYM_BOOT_SERVER}, + {"sm", SYM_SUBNET_MASK}, + {"sw", SYM_SWAP_SERVER}, + {"tc", SYM_SIMILAR_ENTRY}, + {"td", SYM_TFTPDIR}, + {"to", SYM_TIME_OFFSET}, + {"ts", SYM_TIME_SERVER}, + {"vm", SYM_VENDOR_MAGIC}, + {"yd", SYM_NIS_DOMAIN}, + {"ys", SYM_NIS_SERVER}, + /* XXX - Add new tags here */ +}; + + +/* + * List of symbolic names for hardware types. Name translates into + * hardware type code listed with it. Names must begin with a letter + * and must be all lowercase. This is searched linearly, so put + * commonly-used entries near the beginning. + */ + +PRIVATE struct htypename htnamemap[] = { + {"ethernet", HTYPE_ETHERNET}, + {"ethernet3", HTYPE_EXP_ETHERNET}, + {"ether", HTYPE_ETHERNET}, + {"ether3", HTYPE_EXP_ETHERNET}, + {"ieee802", HTYPE_IEEE802}, + {"tr", HTYPE_IEEE802}, + {"token-ring", HTYPE_IEEE802}, + {"pronet", HTYPE_PRONET}, + {"chaos", HTYPE_CHAOS}, + {"arcnet", HTYPE_ARCNET}, + {"ax.25", HTYPE_AX25}, + {"direct", HTYPE_DIRECT}, + {"serial", HTYPE_DIRECT}, + {"slip", HTYPE_DIRECT}, + {"ppp", HTYPE_DIRECT} +}; + + + +/* + * Externals and forward declarations. + */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern boolean iplookcmp(); +boolean nmcmp P((hash_datum *, hash_datum *)); + +PRIVATE void + adjust P((char **)); +PRIVATE void + del_string P((struct shared_string *)); +PRIVATE void + del_bindata P((struct shared_bindata *)); +PRIVATE void + del_iplist P((struct in_addr_list *)); +PRIVATE void + eat_whitespace P((char **)); +PRIVATE int + eval_symbol P((char **, struct host *)); +PRIVATE void + fill_defaults P((struct host *, char **)); +PRIVATE void + free_host P((hash_datum *)); +PRIVATE struct in_addr_list * + get_addresses P((char **)); +PRIVATE struct shared_string * + get_shared_string P((char **)); +PRIVATE char * + get_string P((char **, char *, u_int *)); +PRIVATE u_int32 + get_u_long P((char **)); +PRIVATE boolean + goodname P((char *)); +PRIVATE boolean + hwinscmp P((hash_datum *, hash_datum *)); +PRIVATE int + interp_byte P((char **, byte *)); +PRIVATE void + makelower P((char *)); +PRIVATE boolean + nullcmp P((hash_datum *, hash_datum *)); +PRIVATE int + process_entry P((struct host *, char *)); +PRIVATE int + process_generic P((char **, struct shared_bindata **, u_int)); +PRIVATE byte * + prs_haddr P((char **, u_int)); +PRIVATE int + prs_inetaddr P((char **, u_int32 *)); +PRIVATE void + read_entry P((FILE *, char *, u_int *)); +PRIVATE char * + smalloc P((u_int)); + +#undef P + + +/* + * Vendor magic cookies for CMU and RFC1048 + */ +u_char vm_cmu[4] = VM_CMU; +u_char vm_rfc1048[4] = VM_RFC1048; + +/* + * Main hash tables + */ +hash_tbl *hwhashtable; +hash_tbl *iphashtable; +hash_tbl *nmhashtable; + +/* + * Allocate hash tables for hardware address, ip address, and hostname + * (shared by bootpd and bootpef) + */ +void +rdtab_init() +{ + hwhashtable = hash_Init(HASHTABLESIZE); + iphashtable = hash_Init(HASHTABLESIZE); + nmhashtable = hash_Init(HASHTABLESIZE); + if (!(hwhashtable && iphashtable && nmhashtable)) { + report(LOG_ERR, "Unable to allocate hash tables."); + exit(1); + } +} + + +/* + * Read bootptab database file. Avoid rereading the file if the + * write date hasn't changed since the last time we read it. + */ + +void +readtab(force) + int force; +{ + struct host *hp; + FILE *fp; + struct stat st; + unsigned hashcode, buflen; + static char buffer[MAXENTRYLEN]; + + /* + * Check the last modification time. + */ + if (stat(bootptab, &st) < 0) { + report(LOG_ERR, "stat on \"%s\": %s", + bootptab, get_errmsg()); + return; + } +#ifdef DEBUG + if (debug > 3) { + char timestr[28]; + strcpy(timestr, ctime(&(st.st_mtime))); + /* zap the newline */ + timestr[24] = '\0'; + report(LOG_INFO, "bootptab mtime: %s", + timestr); + } +#endif + if ((force == 0) && + (st.st_mtime == modtime) && + st.st_nlink) { + /* + * hasn't been modified or deleted yet. + */ + return; + } + if (debug) + report(LOG_INFO, "reading %s\"%s\"", + (modtime != 0L) ? "new " : "", + bootptab); + + /* + * Open bootptab file. + */ + if ((fp = fopen(bootptab, "r")) == NULL) { + report(LOG_ERR, "error opening \"%s\": %s", bootptab, get_errmsg()); + return; + } + /* + * Record file modification time. + */ + if (fstat(fileno(fp), &st) < 0) { + report(LOG_ERR, "fstat: %s", get_errmsg()); + fclose(fp); + return; + } + modtime = st.st_mtime; + + /* + * Entirely erase all hash tables. + */ + hash_Reset(hwhashtable, free_host); + hash_Reset(iphashtable, free_host); + hash_Reset(nmhashtable, free_host); + + nhosts = 0; + nentries = 0; + while (TRUE) { + buflen = sizeof(buffer); + read_entry(fp, buffer, &buflen); + if (buflen == 0) { /* More entries? */ + break; + } + hp = (struct host *) smalloc(sizeof(struct host)); + bzero((char *) hp, sizeof(*hp)); + /* the link count it zero */ + + /* + * Get individual info + */ + if (process_entry(hp, buffer) < 0) { + hp->linkcount = 1; + free_host((hash_datum *) hp); + continue; + } + /* + * If this is not a dummy entry, and the IP or HW + * address is not yet set, try to get them here. + * Dummy entries have . as first char of name. + */ + if (goodname(hp->hostname->string)) { + char *hn = hp->hostname->string; + u_int32 value; + if (hp->flags.iaddr == 0) { + if (lookup_ipa(hn, &value)) { + report(LOG_ERR, "can not get IP addr for %s", hn); + report(LOG_ERR, "(dummy names should start with '.')"); + } else { + hp->iaddr.s_addr = value; + hp->flags.iaddr = TRUE; + } + } + /* Set default subnet mask. */ + if (hp->flags.subnet_mask == 0) { + if (lookup_netmask(hp->iaddr.s_addr, &value)) { + report(LOG_ERR, "can not get netmask for %s", hn); + } else { + hp->subnet_mask.s_addr = value; + hp->flags.subnet_mask = TRUE; + } + } + } + if (hp->flags.iaddr) { + nhosts++; + } + /* Register by HW addr if known. */ + if (hp->flags.htype && hp->flags.haddr) { + /* We will either insert it or free it. */ + hp->linkcount++; + hashcode = hash_HashFunction(hp->haddr, haddrlength(hp->htype)); + if (hash_Insert(hwhashtable, hashcode, hwinscmp, hp, hp) < 0) { + report(LOG_NOTICE, "duplicate %s address: %s", + netname(hp->htype), + haddrtoa(hp->haddr, 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 preceeded by a backslash cause + * line-continuation onto the next line. The entry is terminated by a + * newline character which is not preceeded by a backslash. Sequences + * surrounded by double quotes are taken literally (including newlines, but + * not backslashes). + * + * The "bufsiz" parameter points to an unsigned int which specifies the + * maximum permitted buffer size. Upon return, this value will be replaced + * with the actual length of the entry (not including the null terminator). + * + * This code is a little scary. . . . I don't like using gotos in C + * either, but I first wrote this as an FSM diagram and gotos seemed like + * the easiest way to implement it. Maybe later I'll clean it up. + */ + +PRIVATE void +read_entry(fp, buffer, bufsiz) + FILE *fp; + char *buffer; + unsigned *bufsiz; +{ + int c, length; + + length = 0; + + /* + * Eat whitespace, blank lines, and comment lines. + */ + top: + c = fgetc(fp); + if (c < 0) { + goto done; /* Exit if end-of-file */ + } + if (isspace(c)) { + goto top; /* Skip over whitespace */ + } + if (c == '#') { + while (TRUE) { /* Eat comments after # */ + c = fgetc(fp); + if (c < 0) { + goto done; /* Exit if end-of-file */ + } + if (c == '\n') { + goto top; /* Try to read the next line */ + } + } + } + ungetc(c, fp); /* Other character, push it back to reprocess it */ + + + /* + * Now we're actually reading a data entry. Get each character and + * assemble it into the data buffer, processing special characters like + * double quotes (") and backslashes (\). + */ + + mainloop: + c = fgetc(fp); + switch (c) { + case EOF: + case '\n': + goto done; /* Exit on EOF or newline */ + case '\\': + c = fgetc(fp); /* Backslash, read a new character */ + if (c < 0) { + goto done; /* Exit on EOF */ + } + *buffer++ = c; /* Store the literal character */ + length++; + if (length < *bufsiz - 1) { + goto mainloop; + } else { + goto done; + } + case '"': + *buffer++ = '"'; /* Store double-quote */ + length++; + if (length >= *bufsiz - 1) { + goto done; + } + while (TRUE) { /* Special quote processing loop */ + c = fgetc(fp); + switch (c) { + case EOF: + goto done; /* Exit on EOF . . . */ + case '"': + *buffer++ = '"';/* Store matching quote */ + length++; + if (length < *bufsiz - 1) { + goto mainloop; /* And continue main loop */ + } else { + goto done; + } + case '\\': + if ((c = fgetc(fp)) < 0) { /* Backslash */ + goto done; /* EOF. . . .*/ + } /* else fall through */ + default: + *buffer++ = c; /* Other character, store it */ + length++; + if (length >= *bufsiz - 1) { + goto done; + } + } + } + case ':': + *buffer++ = c; /* Store colons */ + length++; + if (length >= *bufsiz - 1) { + goto done; + } + do { /* But remove whitespace after them */ + c = fgetc(fp); + if ((c < 0) || (c == '\n')) { + goto done; + } + } while (isspace(c)); /* Skip whitespace */ + + if (c == '\\') { /* Backslash quotes next character */ + c = fgetc(fp); + if (c < 0) { + goto done; + } + if (c == '\n') { + goto top; /* Backslash-newline continuation */ + } + } + /* fall through if "other" character */ + default: + *buffer++ = c; /* Store other characters */ + length++; + if (length >= *bufsiz - 1) { + goto done; + } + } + goto mainloop; /* Keep going */ + + done: + *buffer = '\0'; /* Terminate string */ + *bufsiz = length; /* Tell the caller its length */ +} + + + +/* + * Parse out all the various tags and parameters in the host entry pointed + * to by "src". Stuff all the data into the appropriate fields of the + * host structure pointed to by "host". If there is any problem with the + * entry, an error message is reported via report(), no further processing + * is done, and -1 is returned. Successful calls return 0. + * + * (Some errors probably shouldn't be so completely fatal. . . .) + */ + +PRIVATE int +process_entry(host, src) + struct host *host; + char *src; +{ + int retval; + char *msg; + + if (!host || *src == '\0') { + return -1; + } + host->hostname = get_shared_string(&src); +#if 0 + /* Be more liberal for the benefit of dummy tag names. */ + if (!goodname(host->hostname->string)) { + report(LOG_ERR, "bad hostname: \"%s\"", host->hostname->string); + del_string(host->hostname); + return -1; + } +#endif + current_hostname = host->hostname->string; + adjust(&src); + while (TRUE) { + retval = eval_symbol(&src, host); + if (retval == SUCCESS) { + adjust(&src); + continue; + } + if (retval == E_END_OF_ENTRY) { + /* The default subnet mask is set in readtab() */ + return 0; + } + /* Some kind of error. */ + switch (retval) { + case E_SYNTAX_ERROR: + msg = "bad syntax"; + break; + case E_UNKNOWN_SYMBOL: + msg = "unknown symbol"; + break; + case E_BAD_IPADDR: + msg = "bad INET address"; + break; + case E_BAD_HWADDR: + msg = "bad hardware address"; + break; + case E_BAD_LONGWORD: + msg = "bad longword value"; + break; + case E_BAD_HWATYPE: + msg = "bad HW address type"; + break; + case E_BAD_PATHNAME: + msg = "bad pathname (need leading '/')"; + case E_BAD_VALUE: + msg = "bad value"; + default: + msg = "unkown error"; + break; + } /* switch */ + report(LOG_ERR, "in entry named \"%s\", symbol \"%s\": %s", + current_hostname, current_tagname, msg); + return -1; + } +} + + +/* + * Macros for use in the function below: + */ + +/* Parse one INET address stored directly in MEMBER. */ +#define PARSE_IA1(MEMBER) do \ +{ \ + if (optype == OP_BOOLEAN) \ + return E_SYNTAX_ERROR; \ + hp->flags.MEMBER = FALSE; \ + if (optype == OP_ADDITION) { \ + if (prs_inetaddr(symbol, &value) < 0) \ + return E_BAD_IPADDR; \ + hp->MEMBER.s_addr = value; \ + hp->flags.MEMBER = TRUE; \ + } \ +} while (0) + +/* Parse a list of INET addresses pointed to by MEMBER */ +#define PARSE_IAL(MEMBER) do \ +{ \ + if (optype == OP_BOOLEAN) \ + return E_SYNTAX_ERROR; \ + if (hp->flags.MEMBER) { \ + hp->flags.MEMBER = FALSE; \ + assert(hp->MEMBER); \ + del_iplist(hp->MEMBER); \ + hp->MEMBER = NULL; \ + } \ + if (optype == OP_ADDITION) { \ + hp->MEMBER = get_addresses(symbol); \ + if (hp->MEMBER == NULL) \ + return E_SYNTAX_ERROR; \ + hp->flags.MEMBER = TRUE; \ + } \ +} while (0) + +/* Parse a shared string pointed to by MEMBER */ +#define PARSE_STR(MEMBER) do \ +{ \ + if (optype == OP_BOOLEAN) \ + return E_SYNTAX_ERROR; \ + if (hp->flags.MEMBER) { \ + hp->flags.MEMBER = FALSE; \ + assert(hp->MEMBER); \ + del_string(hp->MEMBER); \ + hp->MEMBER = NULL; \ + } \ + if (optype == OP_ADDITION) { \ + hp->MEMBER = get_shared_string(symbol); \ + if (hp->MEMBER == NULL) \ + return E_SYNTAX_ERROR; \ + hp->flags.MEMBER = TRUE; \ + } \ +} while (0) + +/* Parse an 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); + sprintf(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..3913455 --- /dev/null +++ b/libexec/bootpd/readfile.h @@ -0,0 +1,19 @@ +/* readfile.h */ + +#include "bptypes.h" +#include "hash.h" + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern boolean hwlookcmp P((hash_datum *, hash_datum *)); +extern boolean iplookcmp P((hash_datum *, hash_datum *)); +extern boolean nmcmp P((hash_datum *, hash_datum *)); +extern void readtab P((int)); +extern void rdtab_init P((void)); + +#undef P + diff --git a/libexec/bootpd/report.c b/libexec/bootpd/report.c new file mode 100644 index 0000000..4f7f036 --- /dev/null +++ b/libexec/bootpd/report.c @@ -0,0 +1,154 @@ +/* + * report() - calls syslog + */ + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include <stdio.h> +#include <syslog.h> + +#include "report.h" + +#ifndef LOG_NDELAY +#define LOG_NDELAY 0 +#endif +#ifndef LOG_DAEMON +#define LOG_DAEMON 0 +#endif +#ifndef LOG_BOOTP +#define LOG_BOOTP LOG_DAEMON +#endif + +extern int debug; +extern char *progname; + +/* + * This is initialized so you get stderr until you call + * report_init() + */ +static int stderr_only = 1; + +void +report_init(nolog) + int nolog; +{ + stderr_only = nolog; +#ifdef SYSLOG + if (!stderr_only) { + openlog(progname, LOG_PID | LOG_NDELAY, LOG_BOOTP); + } +#endif +} + +/* + * This routine reports errors and such via stderr and syslog() if + * appopriate. It just helps avoid a lot of "#ifdef SYSLOG" constructs + * from being scattered throughout the code. + * + * The syntax is identical to syslog(3), but %m is not considered special + * for output to stderr (i.e. you'll see "%m" in the output. . .). Also, + * control strings should normally end with \n since newlines aren't + * automatically generated for stderr output (whereas syslog strips out all + * newlines and adds its own at the end). + */ + +static char *levelnames[] = { +#ifdef LOG_SALERT + "level(0): ", + "alert(1): ", + "alert(2): ", + "emerg(3): ", + "error(4): ", + "crit(5): ", + "warn(6): ", + "note(7): ", + "info(8): ", + "debug(9): ", + "level(?): " +#else + "emerg(0): ", + "alert(1): ", + "crit(2): ", + "error(3): ", + "warn(4): ", + "note(5): ", + "info(6): ", + "debug(7): ", + "level(?): " +#endif +}; +static int numlevels = sizeof(levelnames) / sizeof(levelnames[0]); + + +/* + * Print a log message using syslog(3) and/or stderr. + * The message passed in should not include a newline. + */ +#ifdef __STDC__ +void +report(int priority, char *fmt,...) +#else +/*VARARGS2*/ +void +report(priority, fmt, va_alist) + int priority; + char *fmt; + va_dcl +#endif +{ + va_list ap; + static char buf[128]; + + if ((priority < 0) || (priority >= numlevels)) { + priority = numlevels - 1; + } +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vsprintf(buf, fmt, ap); + va_end(ap); + + /* + * Print the message + */ + if (stderr_only || (debug > 2)) { + fprintf(stderr, "%s: %s %s\n", + progname, levelnames[priority], buf); + } +#ifdef SYSLOG + if (!stderr_only) + syslog((priority | LOG_BOOTP), "%s", buf); +#endif +} + + + +/* + * Return pointer to static string which gives full filesystem error message. + */ +char * +get_errmsg() +{ + extern int errno; + extern char *strerror(); + + return strerror(errno); +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/report.h b/libexec/bootpd/report.h new file mode 100644 index 0000000..0bf63d6 --- /dev/null +++ b/libexec/bootpd/report.h @@ -0,0 +1,13 @@ +/* report.h */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern void report_init P((int nolog)); +extern void report P((int, char *, ...)); +extern char *get_errmsg P((void)); + +#undef P diff --git a/libexec/bootpd/rtmsg.c b/libexec/bootpd/rtmsg.c new file mode 100644 index 0000000..b6bd363 --- /dev/null +++ b/libexec/bootpd/rtmsg.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 1984, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1994 + * Geoffrey M. Rehmet, All rights reserved. + * + * This code is derived from software which forms part of the 4.4-Lite + * Berkeley software distribution, which was in derived from software + * contributed to Berkeley by Sun Microsystems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * from arp.c 8.2 (Berkeley) 1/2/94 + * $Id$ + */ + +#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_var.h> /* needed for if_ether.h XXX */ +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/route.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <arpa/inet.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "report.h" + + +static int rtmsg __P((int)); + +static int s = -1; /* routing socket */ + + +/* + * Open the routing socket + */ +static void getsocket () { + if (s < 0) { + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) { + report(LOG_ERR, "socket %s", strerror(errno)); + exit(1); + } + } else { + /* + * Drain the socket of any unwanted routing messages. + */ + int n; + char buf[512]; + + ioctl(s, FIONREAD, &n); + while (n > 0) { + read(s, buf, sizeof buf); + ioctl(s, FIONREAD, &n); + } + } +} + +static struct sockaddr_in so_mask = {8, 0, 0, { 0xffffffff}}; +static struct sockaddr_inarp blank_sin = {sizeof(blank_sin), AF_INET }, sin_m; +static struct sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m; +static int expire_time, flags, export_only, doing_proxy; +static struct { + struct rt_msghdr m_rtm; + char m_space[512]; +} m_rtmsg; + +/* + * Set an individual arp entry + */ +int bsd_arp_set(ia, eaddr, len) + struct in_addr *ia; + char *eaddr; + int len; +{ + register struct sockaddr_inarp *sin = &sin_m; + register struct sockaddr_dl *sdl; + register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); + u_char *ea; + struct timeval time; + int op = RTM_ADD; + + getsocket(); + sdl_m = blank_sdl; + sin_m = blank_sin; + sin->sin_addr = *ia; + + ea = (u_char *)LLADDR(&sdl_m); + bcopy(eaddr, ea, len); + sdl_m.sdl_alen = len; + doing_proxy = flags = export_only = expire_time = 0; + + /* make arp entry temporary */ + gettimeofday(&time, 0); + expire_time = time.tv_sec + 20 * 60; + +tryagain: + if (rtmsg(RTM_GET) < 0) { + report(LOG_WARNING, "rtmget: %s", strerror(errno)); + return (1); + } + sin = (struct sockaddr_inarp *)(rtm + 1); + sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin); + if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) { + if (sdl->sdl_family == AF_LINK && + (rtm->rtm_flags & RTF_LLINFO) && + !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) { + case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023: + case IFT_ISO88024: case IFT_ISO88025: + op = RTM_CHANGE; + goto overwrite; + } + if (doing_proxy == 0) { + report(LOG_WARNING, "set: can only proxy for %s\n", + inet_ntoa(sin->sin_addr)); + return (1); + } + if (sin_m.sin_other & SIN_PROXY) { + report(LOG_WARNING, + "set: proxy entry exists for non 802 device\n"); + return(1); + } + sin_m.sin_other = SIN_PROXY; + export_only = 1; + goto tryagain; + } +overwrite: + if (sdl->sdl_family != AF_LINK) { + report(LOG_WARNING, + "cannot intuit interface index and type for %s\n", + inet_ntoa(sin->sin_addr)); + return (1); + } + sdl_m.sdl_type = sdl->sdl_type; + sdl_m.sdl_index = sdl->sdl_index; + return (rtmsg(op)); +} + + +static int rtmsg(cmd) + int cmd; +{ + static int seq; + int rlen; + register struct rt_msghdr *rtm = &m_rtmsg.m_rtm; + register char *cp = m_rtmsg.m_space; + register int l; + + errno = 0; + bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); + rtm->rtm_flags = flags; + rtm->rtm_version = RTM_VERSION; + + switch (cmd) { + default: + report(LOG_ERR, "set_arp: internal wrong cmd - exiting"); + exit(1); + case RTM_ADD: + case RTM_CHANGE: + rtm->rtm_addrs |= RTA_GATEWAY; + rtm->rtm_rmx.rmx_expire = expire_time; + rtm->rtm_inits = RTV_EXPIRE; + rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); + sin_m.sin_other = 0; + if (doing_proxy) { + if (export_only) + sin_m.sin_other = SIN_PROXY; + else { + rtm->rtm_addrs |= RTA_NETMASK; + rtm->rtm_flags &= ~RTF_HOST; + } + } + /* FALLTHROUGH */ + case RTM_GET: + rtm->rtm_addrs |= RTA_DST; + } +#define NEXTADDR(w, s) \ + if (rtm->rtm_addrs & (w)) { \ + bcopy((char *)&s, cp, sizeof(s)); cp += sizeof(s);} + + NEXTADDR(RTA_DST, sin_m); + NEXTADDR(RTA_GATEWAY, sdl_m); + NEXTADDR(RTA_NETMASK, so_mask); + + rtm->rtm_msglen = cp - (char *)&m_rtmsg; + + l = rtm->rtm_msglen; + rtm->rtm_seq = ++seq; + rtm->rtm_type = cmd; + if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { + if ((errno != ESRCH) && !(errno == EEXIST && cmd == RTM_ADD)){ + report(LOG_WARNING, "writing to routing socket: %s", + strerror(errno)); + return (-1); + } + } + do { + l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); + } while (l > 0 && (rtm->rtm_type != cmd || rtm->rtm_seq != seq || rtm->rtm_pid != getpid())); + if (l < 0) + report(LOG_WARNING, "arp: read from routing socket: %s\n", + strerror(errno)); + return (0); +} + +#endif /* BSD */ +#endif /* BSD >= 199306 */ diff --git a/libexec/bootpd/syslog.conf b/libexec/bootpd/syslog.conf new file mode 100644 index 0000000..2c135af --- /dev/null +++ b/libexec/bootpd/syslog.conf @@ -0,0 +1,63 @@ +# +# syslog configuration file for SunOS 4.X +# (modified to do local2 separately) +# +# This file is processed by m4 so be careful to quote (`') names +# that match m4 reserved words. Also, within ifdef's, arguments +# containing commas must be quoted. +# +# Note: Have to exclude user from most lines so that user.alert +# and user.emerg are not included, because old sendmails +# will generate them for debugging information. If you +# have no 4.2BSD based systems doing network logging, you +# can remove all the special cases for "user" logging. + +#*.err;kern.debug;auth.notice;user.none /dev/console +kern.debug;user,mail.crit;auth.notice /dev/console +daemon,syslog,lpr,news,uucp,cron.err /dev/console + +#*.err;kern.debug;daemon,auth.notice;mail.crit;user.none /var/adm/messages +kern.debug;user,mail.crit;auth.notice /var/adm/messages +daemon.notice;syslog,news,uucp,cron.err /var/adm/messages + +lpr.debug /var/adm/lpd-errs + +*.alert;kern.err;daemon.err;user.none operator +*.alert;user.none root + +*.emerg;user.none * + +# for loghost machines, to have authentication messages (su, login, etc.) +# logged to a file, un-comment out the following line and adjust the file name +# as appropriate. +# +# if a non-loghost machine chooses to have such messages +# sent to the loghost machine, un-comment out the following line. +# +#auth.notice ifdef(`LOGHOST', /var/log/authlog, @loghost) + +mail.debug ifdef(`LOGHOST', /var/log/syslog, @loghost) + +# following line for compatibility with old sendmails. they will send +# messages with no facility code, which will be turned into "user" messages +# by the local syslog daemon. only the "loghost" machine needs the following +# line, to cause these old sendmail log messages to be logged in the +# mail syslog file. +# +ifdef(`LOGHOST', +user.alert /var/log/syslog +) +# +# non-loghost machines will use the following lines to cause "user" +# log messages to be logged locally. +# +ifdef(`LOGHOST', , +user.err /dev/console +user.err /var/adm/messages +user.alert `root, operator' +user.emerg * +) + +# Local2: (bootpd, pppd) +local2.debug /dev/console +#local2.debug /var/log/local2 diff --git a/libexec/bootpd/tools/Makefile b/libexec/bootpd/tools/Makefile new file mode 100644 index 0000000..b1f38e7 --- /dev/null +++ b/libexec/bootpd/tools/Makefile @@ -0,0 +1,6 @@ +# Makefile +# $Id$ + +SUBDIR= bootpef bootptest + +.include <bsd.subdir.mk> diff --git a/libexec/bootpd/tools/Makefile.inc b/libexec/bootpd/tools/Makefile.inc new file mode 100644 index 0000000..0a2ab66 --- /dev/null +++ b/libexec/bootpd/tools/Makefile.inc @@ -0,0 +1,4 @@ +# Makefile.inc +# $Id$ + +BINDIR=/usr/sbin diff --git a/libexec/bootpd/tools/bootpef/Makefile b/libexec/bootpd/tools/bootpef/Makefile new file mode 100644 index 0000000..7d17606 --- /dev/null +++ b/libexec/bootpd/tools/bootpef/Makefile @@ -0,0 +1,13 @@ +# Makefile +# $Id$ + +PROG= bootpef +MAN8= bootpef.8 +SRCS= bootpef.c dovend.c readfile.c hash.c dumptab.c lookup.c \ + hwaddr.c report.c tzone.c rtmsg.c + +SRCDIR= ${.CURDIR}/../.. +CFLAGS+=-I${SRCDIR} +.PATH: ${SRCDIR} + +.include <bsd.prog.mk> diff --git a/libexec/bootpd/tools/bootpef/bootpef.8 b/libexec/bootpd/tools/bootpef/bootpef.8 new file mode 100644 index 0000000..0f0b1fc --- /dev/null +++ b/libexec/bootpd/tools/bootpef/bootpef.8 @@ -0,0 +1,52 @@ +.\" bootpef.8 +.TH BOOTPEF 8 "4 Dec 1993" "MAINTENANCE COMMANDS" +.SH NAME +bootpef \- BOOTP Extension File compiler +.SH SYNOPSIS +.LP +.B bootpef +.RI [ "-c chdir" ] +.RI [ "-d debug-level" ] +.RI [ "-f config-file" ] +.RI [ client-name " [...]]" +.SH DESCRIPTION +.B bootpef +builds the +.I Extension Path +files described by RFC 1497 (tag 18). +If any +.I client-name +arguments are specified, then +.I bootpef +compiles the extension files for only those clients. +.SH OPTIONS +.TP +.BI \-c \ chdir\-path +Sets the current directory used by +.I bootpef +while creating extension files. This is useful when the +extension file names are specified as relative pathnames, and +.I bootpef +needs to use the same current directory as the TFTP server +(typically /tftpboot). +.TP +.BI \-d \ debug\-level +Sets the +.I debug\-level +variable that controls the amount of debugging messages generated. +For example, -d4 or -d 4 will set the debugging level to 4. +.TP +.BI \-f \ config\-file +Set the name of the config file that specifies the option +data to be sent to each client. +.SH "SEE ALSO" +bootpd(8), tftpd(8) +.SH REFERENCES +.TP +RFC951 +BOOTSTRAP PROTOCOL (BOOTP) +.TP +RFC1497 +BOOTP Vendor Information Extensions + + diff --git a/libexec/bootpd/tools/bootpef/bootpef.c b/libexec/bootpd/tools/bootpef/bootpef.c new file mode 100644 index 0000000..c7e9276 --- /dev/null +++ b/libexec/bootpd/tools/bootpef/bootpef.c @@ -0,0 +1,343 @@ +/************************************************************************ + 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. + + $Id$ + +************************************************************************/ + +/* + * bootpef - BOOTP Extension File generator + * Makes an "Extension File" for each host entry that + * defines an and Extension File. (See RFC1497, tag 18.) + * + * HISTORY + * See ./Changes + * + * BUGS + * See ./ToDo + */ + + + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include <sys/types.h> +#include <sys/time.h> + +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#ifndef NO_UNISTD +#include <unistd.h> +#endif +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <syslog.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#define bcopy(a,b,c) memcpy(b,a,c) +#define bzero(p,l) memset(p,0,l) +#define bcmp(a,b,c) memcmp(a,b,c) +#endif + +#include "bootp.h" +#include "hash.h" +#include "hwaddr.h" +#include "bootpd.h" +#include "dovend.h" +#include "readfile.h" +#include "report.h" +#include "tzone.h" +#include "patchlevel.h" + +#define BUFFERSIZE 0x4000 + +#ifndef CONFIG_FILE +#define CONFIG_FILE "/etc/bootptab" +#endif + + + +/* + * Externals, forward declarations, and global variables + */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +static void mktagfile P((struct host *)); +static void usage P((void)); + +#undef P + + +/* + * General + */ + +char *progname; +char *chdir_path; +int debug = 0; /* Debugging flag (level) */ +byte *buffer; + +/* + * Globals below are associated with the bootp database file (bootptab). + */ + +char *bootptab = CONFIG_FILE; + + +/* + * Print "usage" message and exit + */ +static void +usage() +{ + fprintf(stderr, + "usage: $s [ -c chdir ] [-d level] [-f configfile] [host...]\n"); + fprintf(stderr, "\t -c n\tset current directory\n"); + fprintf(stderr, "\t -d n\tset debug level\n"); + fprintf(stderr, "\t -f n\tconfig file name\n"); + exit(1); +} + + +/* + * Initialization such as command-line processing is done and then the + * main server loop is started. + */ +void +main(argc, argv) + int argc; + char **argv; +{ + struct host *hp; + char *stmp; + int n; + + progname = strrchr(argv[0], '/'); + if (progname) progname++; + else progname = argv[0]; + + /* Get work space for making tag 18 files. */ + buffer = (byte *) malloc(BUFFERSIZE); + if (!buffer) { + report(LOG_ERR, "malloc failed"); + exit(1); + } + /* + * Set defaults that might be changed by option switches. + */ + stmp = NULL; + + /* + * Read switches. + */ + for (argc--, argv++; argc > 0; argc--, argv++) { + if (argv[0][0] != '-') + break; + switch (argv[0][1]) { + + case 'c': /* chdir_path */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (stmp[0] != '/')) { + fprintf(stderr, + "bootpd: invalid chdir specification\n"); + break; + } + chdir_path = stmp; + break; + + case 'd': /* debug */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else if (argv[1] && argv[1][0] == '-') { + /* + * Backwards-compatible behavior: + * no parameter, so just increment the debug flag. + */ + debug++; + break; + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { + fprintf(stderr, + "bootpd: invalid debug level\n"); + break; + } + debug = n; + break; + + case 'f': /* config file */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + bootptab = stmp; + break; + + default: + fprintf(stderr, "bootpd: unknown switch: -%c\n", + argv[0][1]); + usage(); + break; + } + } + + /* Get the timezone. */ + tzone_init(); + + /* Allocate hash tables. */ + rdtab_init(); + + /* + * Read the bootptab file. + */ + readtab(1); /* force read */ + + /* Set the cwd (i.e. to /tftpboot) */ + if (chdir_path) { + if (chdir(chdir_path) < 0) + report(LOG_ERR, "%s: chdir failed", chdir_path); + } + /* If there are host names on the command line, do only those. */ + if (argc > 0) { + unsigned int tlen, hashcode; + + while (argc) { + tlen = strlen(argv[0]); + hashcode = hash_HashFunction((u_char *)argv[0], tlen); + hp = (struct host *) hash_Lookup(nmhashtable, + hashcode, + nmcmp, argv[0]); + if (!hp) { + printf("%s: no matching entry\n", argv[0]); + exit(1); + } + if (!hp->flags.exten_file) { + printf("%s: no extension file\n", argv[0]); + exit(1); + } + mktagfile(hp); + argv++; + argc--; + } + exit(0); + } + /* No host names specified. Do them all. */ + hp = (struct host *) hash_FirstEntry(nmhashtable); + while (hp != NULL) { + mktagfile(hp); + hp = (struct host *) hash_NextEntry(nmhashtable); + } +} + + + +/* + * Make a "TAG 18" file for this host. + * (Insert the RFC1497 options.) + */ + +static void +mktagfile(hp) + struct host *hp; +{ + FILE *fp; + int bytesleft, len; + byte *vp; + + 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..148a556 --- /dev/null +++ b/libexec/bootpd/tools/bootptest/Makefile @@ -0,0 +1,12 @@ +# Makefile +# $Id$ + +PROG= bootptest +MAN8= bootptest.8 +SRCS= bootptest.c getether.c getif.c print-bootp.c report.c + +SRCDIR= ${.CURDIR}/../.. +CFLAGS+=-I${SRCDIR} +.PATH: ${SRCDIR} + +.include <bsd.prog.mk> diff --git a/libexec/bootpd/tools/bootptest/bootptest.8 b/libexec/bootpd/tools/bootptest/bootptest.8 new file mode 100644 index 0000000..d076c8b --- /dev/null +++ b/libexec/bootpd/tools/bootptest/bootptest.8 @@ -0,0 +1,74 @@ +.\" bootptest.8 +.TH BOOTPTEST 8 "10 June 1993" "MAINTENANCE COMMANDS" +.SH NAME +bootptest \- send BOOTP queries and print responses +.SH SYNOPSIS +.LP +.B bootptest +[ +.B \-f +.I bootfile +] +[ +.B \-h +] +[ +.B \-m +.I magic_number +] +.I server\-name +.RI [ template-file ] +.SH DESCRIPTION +.B bootptest +sends BOOTP requests to the host specified as +.I server\-name +at one\-second intervals until either a response is received, +or until ten requests have gone unanswered. +After a response is received, +.B bootptest +will wait one more second listening for additional responses. +.SH OPTIONS +.TP +.B \-f +.I bootfile +Fill in the boot file field of the request with +.IR bootfile . +.TP +.B \-h +Use the hardware (Ethernet) address to identify the client. +By default, the IP address is copied into the request +indicating that this client already knows its IP address. +.TP +.B \-m +.I magic_number +Initialize the first word of the vendor options field with +.IR magic_number . +.LP +A +.I template-file +may be specified, in which case +.B bootptest +uses the (binary) contents of this file to initialize the +.I options +area of the request packet. +.SH CREDITS +.LP +The bootptest program is a combination of original and derived works. +The main program module (bootptest.c) is original work by +Gordon W. Ross <gwr@mc.com>. +The packet printing module (print-bootp.c) is a slightly modified +version of a file from the BSD tcpdump program. +.LP +This program includes software developed by the University of +California, Lawrence Berkeley Laboratory and its contributors. +(See the copyright notice in print-bootp.c) +.SH "SEE ALSO" +.LP +bootpd(8) +.SH REFERENCES +.TP +RFC951 +BOOTSTRAP PROTOCOL (BOOTP) +.TP +RFC1048 +BOOTP Vendor Information Extensions diff --git a/libexec/bootpd/tools/bootptest/bootptest.c b/libexec/bootpd/tools/bootptest/bootptest.c new file mode 100644 index 0000000..2e23112 --- /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> + * + * $Id$ + */ + +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 <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. + */ + +void +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) { + fprintf(stderr, "%s: can't get hostname\n", argv[0]); + exit(1); + } + 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 reqest. */ + if (argc < 2) + goto error; + argc--; argv++; + bp_file = *argv; + break; + + case 'h': /* Use hardware address. */ + use_hwa = 1; + break; + + case 'm': /* Magic number value. */ + if (argc < 2) + goto error; + argc--; argv++; + vend_magic = inet_addr(*argv); + break; + + error: + default: + puts(usage); + exit(1); + + } + argc--; + argv++; + } + + /* Get server name (or address) for query. */ + if (argc > 0) { + servername = *argv; + argc--; + argv++; + } + /* Get optional vendor-data-template-file. */ + if (argc > 0) { + vendor_file = *argv; + argc--; + argv++; + } + if (!servername) { + printf("missing server name.\n"); + puts(usage); + exit(1); + } + /* + * Create a socket. + */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket"); + exit(1); + } + /* + * Get server's listening port number + */ + sep = getservbyname("bootps", "udp"); + if (sep) { + bootps_port = ntohs((u_short) sep->s_port); + } else { + fprintf(stderr, "udp/bootps: unknown service -- using port %d\n", + IPPORT_BOOTPS); + bootps_port = (u_short) IPPORT_BOOTPS; + } + + /* + * Set up server socket address (for send) + */ + if (servername) { + if (isdigit(servername[0])) + server_addr = inet_addr(servername); + else { + hep = gethostbyname(servername); + if (!hep) { + fprintf(stderr, "%s: unknown host\n", servername); + exit(1); + } + bcopy(hep->h_addr, &server_addr, sizeof(server_addr)); + } + } else { + /* Get broadcast address */ + /* XXX - not yet */ + server_addr = INADDR_ANY; + } + sin_server.sin_family = AF_INET; + sin_server.sin_port = htons(bootps_port); + sin_server.sin_addr.s_addr = server_addr; + + /* + * Get client's listening port number + */ + sep = getservbyname("bootpc", "udp"); + if (sep) { + bootpc_port = ntohs(sep->s_port); + } else { + fprintf(stderr, "udp/bootpc: unknown service -- using port %d\n", + IPPORT_BOOTPC); + bootpc_port = (u_short) IPPORT_BOOTPC; + } + + /* + * Set up client socket address (for listen) + */ + sin_client.sin_family = AF_INET; + sin_client.sin_port = htons(bootpc_port); + sin_client.sin_addr.s_addr = INADDR_ANY; + + /* + * Bind client socket to BOOTPC port. + */ + if (bind(s, (struct sockaddr *) &sin_client, sizeof(sin_client)) < 0) { + perror("bind BOOTPC port"); + if (errno == EACCES) + fprintf(stderr, "You need to run this as root\n"); + exit(1); + } + /* + * Build a request. + */ + bp = (struct bootp *) sndbuf; + bzero(bp, sizeof(*bp)); + bp->bp_op = BOOTREQUEST; + xid = (int32) getpid(); + bp->bp_xid = (u_int32) htonl(xid); + if (bp_file) + strncpy(bp->bp_file, bp_file, BP_FILE_LEN); + + /* + * Fill in the hardware address (or client IP address) + */ + if (use_hwa) { + struct ifreq *ifr; + + ifr = getif(s, &sin_server.sin_addr); + if (!ifr) { + printf("No interface for %s\n", servername); + exit(1); + } + if (getether(ifr->ifr_name, (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. + */ + } + fprintf(stderr, "no response from %s\n", servername); + exit(1); +} + +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; + sprintf(b, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + return (b); +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/tools/bootptest/bootptest.h b/libexec/bootpd/tools/bootptest/bootptest.h new file mode 100644 index 0000000..27f78ba --- /dev/null +++ b/libexec/bootpd/tools/bootptest/bootptest.h @@ -0,0 +1,30 @@ +/* bootptest.h */ +/* + * Hacks for sharing print-bootp.c between tcpdump and bootptest. + */ +#define ESRC(p) (p) +#define EDST(p) (p) + +#ifndef USE_BFUNCS +/* Use mem/str functions */ +/* There are no overlapped copies, so memcpy is OK. */ +#define bcopy(a,b,c) memcpy(b,a,c) +#define bzero(p,l) memset(p,0,l) +#define bcmp(a,b,c) memcmp(a,b,c) +#endif + +extern int vflag; /* verbose flag */ + +/* global pointers to beginning and end of current packet (during printing) */ +extern unsigned char *packetp; +extern unsigned char *snapend; + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern char *ipaddr_string P((struct in_addr *)); + +#undef P diff --git a/libexec/bootpd/tools/bootptest/print-bootp.c b/libexec/bootpd/tools/bootptest/print-bootp.c new file mode 100644 index 0000000..c8e1c2e --- /dev/null +++ b/libexec/bootpd/tools/bootptest/print-bootp.c @@ -0,0 +1,492 @@ +/* + * Copyright (c) 1988-1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Format and print bootp packets. + * + * This file was copied from tcpdump-2.1.1 and modified. + * There is an e-mail list for tcpdump: <tcpdump@ee.lbl.gov> + * + * $Id$ + */ + +#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:%d", ntohl(bp->bp_xid)); + + if (bp->bp_secs) + printf(" secs:%d", ntohs(bp->bp_secs)); + + /* Client's ip address */ + TCHECK(bp->bp_ciaddr, sizeof(bp->bp_ciaddr)); + if (bp->bp_ciaddr.s_addr) + printf(" C:%s", ipaddr_string(&bp->bp_ciaddr)); + + /* 'your' ip address (bootp client) */ + TCHECK(bp->bp_yiaddr, sizeof(bp->bp_yiaddr)); + if (bp->bp_yiaddr.s_addr) + printf(" Y:%s", ipaddr_string(&bp->bp_yiaddr)); + + /* Server's ip address */ + TCHECK(bp->bp_siaddr, sizeof(bp->bp_siaddr)); + if (bp->bp_siaddr.s_addr) + printf(" S:%s", ipaddr_string(&bp->bp_siaddr)); + + /* Gateway's ip address */ + TCHECK(bp->bp_giaddr, sizeof(bp->bp_giaddr)); + if (bp->bp_giaddr.s_addr) + printf(" G:%s", ipaddr_string(&bp->bp_giaddr)); + + TCHECK(bp->bp_sname[0], sizeof(bp->bp_sname)); + if (*bp->bp_sname) { + printf(" sname:"); + if (printfn(bp->bp_sname, ep)) { + fputs(tstr + 1, stdout); + return; + } + } + TCHECK(bp->bp_file[0], sizeof(bp->bp_file)); + if (*bp->bp_file) { + printf(" file:"); + if (printfn(bp->bp_file, ep)) { + fputs(tstr + 1, stdout); + return; + } + } + /* Don't try to decode the vendor buffer unless we're verbose */ + if (vflag <= 0) + return; + + vdlen = sizeof(bp->bp_vend); + /* Vendor data can extend to the end of the packet. */ + if (vdlen < (ep - bp->bp_vend)) + vdlen = (ep - bp->bp_vend); + + TCHECK(bp->bp_vend[0], vdlen); + printf(" vend"); + if (!bcmp(bp->bp_vend, vm_rfc1048, sizeof(u_int32))) + rfc1048_print(bp->bp_vend, vdlen); + else if (!bcmp(bp->bp_vend, vm_cmu, sizeof(u_int32))) + cmu_print(bp->bp_vend, vdlen); + else + other_print(bp->bp_vend, vdlen); + + return; + trunc: + fputs(tstr, stdout); +#undef TCHECK +} + +/* + * Option description data follows. + * These are decribed in: RFC-1048, RFC-1395, RFC-1497, RFC-1533 + * + * The first char of each option string encodes the data format: + * ?: unknown + * a: ASCII + * b: byte (8-bit) + * i: inet address + * l: int32 + * s: short (16-bit) + */ +char * +rfc1048_opts[] = { + /* Originally from RFC-1048: */ + "?PAD", /* 0: Padding - special, no data. */ + "iSM", /* 1: subnet mask (RFC950)*/ + "lTZ", /* 2: time offset, seconds from UTC */ + "iGW", /* 3: gateways (or routers) */ + "iTS", /* 4: time servers (RFC868) */ + "iINS", /* 5: IEN name servers (IEN116) */ + "iDNS", /* 6: domain name servers (RFC1035)(1034?) */ + "iLOG", /* 7: MIT log servers */ + "iCS", /* 8: cookie servers (RFC865) */ + "iLPR", /* 9: lpr server (RFC1179) */ + "iIPS", /* 10: impress servers (Imagen) */ + "iRLP", /* 11: resource location servers (RFC887) */ + "aHN", /* 12: host name (ASCII) */ + "sBFS", /* 13: boot file size (in 512 byte blocks) */ + + /* Added by RFC-1395: */ + "aDUMP", /* 14: Merit Dump File */ + "aDNAM", /* 15: Domain Name (for DNS) */ + "iSWAP", /* 16: Swap Server */ + "aROOT", /* 17: Root Path */ + + /* Added by RFC-1497: */ + "aEXTF", /* 18: Extensions Path (more options) */ + + /* Added by RFC-1533: (many, many options...) */ +#if 1 /* These might not be worth recognizing by name. */ + + /* IP Layer Parameters, per-host (RFC-1533, sect. 4) */ + "bIP-forward", /* 19: IP Forwarding flag */ + "bIP-srcroute", /* 20: IP Source Routing Enable flag */ + "iIP-filters", /* 21: IP Policy Filter (addr pairs) */ + "sIP-maxudp", /* 22: IP Max-UDP reassembly size */ + "bIP-ttlive", /* 23: IP Time to Live */ + "lIP-pmtuage", /* 24: IP Path MTU aging timeout */ + "sIP-pmtutab", /* 25: IP Path MTU plateau table */ + + /* IP parameters, per-interface (RFC-1533, sect. 5) */ + "sIP-mtu-sz", /* 26: IP MTU size */ + "bIP-mtu-sl", /* 27: IP MTU all subnets local */ + "bIP-bcast1", /* 28: IP Broadcast Addr ones flag */ + "bIP-mask-d", /* 29: IP do mask discovery */ + "bIP-mask-s", /* 30: IP do mask supplier */ + "bIP-rt-dsc", /* 31: IP do router discovery */ + "iIP-rt-sa", /* 32: IP router solicitation addr */ + "iIP-routes", /* 33: IP static routes (dst,router) */ + + /* Link Layer parameters, per-interface (RFC-1533, sect. 6) */ + "bLL-trailer", /* 34: do tralier encapsulation */ + "lLL-arp-tmo", /* 35: ARP cache timeout */ + "bLL-ether2", /* 36: Ethernet version 2 (IEEE 802.3) */ + + /* TCP parameters (RFC-1533, sect. 7) */ + "bTCP-def-ttl", /* 37: default time to live */ + "lTCP-KA-tmo", /* 38: keepalive time interval */ + "bTCP-KA-junk", /* 39: keepalive sends extra junk */ + + /* Application and Service Parameters (RFC-1533, sect. 8) */ + "aNISDOM", /* 40: NIS Domain (Sun YP) */ + "iNISSRV", /* 41: NIS Servers */ + "iNTPSRV", /* 42: NTP (time) Servers (RFC 1129) */ + "?VSINFO", /* 43: Vendor Specific Info (encapsulated) */ + "iNBiosNS", /* 44: NetBIOS Name Server (RFC-1001,1..2) */ + "iNBiosDD", /* 45: NetBIOS Datagram Dist. Server. */ + "bNBiosNT", /* 46: NetBIOS Note Type */ + "?NBiosS", /* 47: NetBIOS Scope */ + "iXW-FS", /* 48: X Window System Font Servers */ + "iXW-DM", /* 49: X Window System Display Managers */ + + /* DHCP extensions (RFC-1533, sect. 9) */ +#endif +}; +#define KNOWN_OPTIONS (sizeof(rfc1048_opts) / sizeof(rfc1048_opts[0])) + +static void +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>%d)", len, ep - bp); + return; + } + /* Print the option value(s). */ + switch (optstr[0]) { + + case 'a': /* ASCII string */ + printfn(bp, bp + len); + bp += len; + len = 0; + break; + + case 's': /* Word formats */ + while (len >= 2) { + bcopy((char *) bp, (char *) &us, 2); + printf("%d", ntohs(us)); + bp += 2; + len -= 2; + if (len) printf(","); + } + if (len) printf("(junk=%d)", len); + break; + + case 'l': /* Long words */ + while (len >= 4) { + bcopy((char *) bp, (char *) &ul, 4); + printf("%d", ntohl(ul)); + bp += 4; + len -= 4; + if (len) printf(","); + } + if (len) printf("(junk=%d)", len); + break; + + case 'i': /* INET addresses */ + while (len >= 4) { + bcopy((char *) bp, (char *) &ia, 4); + printf("%s", ipaddr_string(&ia)); + bp += 4; + len -= 4; + if (len) printf(","); + } + if (len) printf("(junk=%d)", len); + break; + + case 'b': + default: + break; + + } /* switch */ + + /* Print as characters, if appropriate. */ + if (len) { + dump_hex(bp, len); + if (isascii(*bp) && isprint(*bp)) { + printf("("); + printfn(bp, bp + len); + printf(")"); + } + bp += len; + len = 0; + } + } /* while bp < ep */ +} + +static void +cmu_print(bp, length) + register u_char *bp; + int length; +{ + struct cmu_vend *v; + u_char *ep; + + printf("-cmu"); + + v = (struct cmu_vend *) bp; + if (length < sizeof(*v)) { + printf(" |L=%d", length); + return; + } + /* Setup end pointer */ + ep = bp + length; + + /* Subnet mask */ + if (v->v_flags & VF_SMASK) { + printf(" SM:%s", ipaddr_string(&v->v_smask)); + } + /* Default gateway */ + if (v->v_dgate.s_addr) + printf(" GW:%s", ipaddr_string(&v->v_dgate)); + + /* Domain name servers */ + if (v->v_dns1.s_addr) + printf(" DNS1:%s", ipaddr_string(&v->v_dns1)); + if (v->v_dns2.s_addr) + printf(" DNS2:%s", ipaddr_string(&v->v_dns2)); + + /* IEN-116 name servers */ + if (v->v_ins1.s_addr) + printf(" INS1:%s", ipaddr_string(&v->v_ins1)); + if (v->v_ins2.s_addr) + printf(" INS2:%s", ipaddr_string(&v->v_ins2)); + + /* Time servers */ + if (v->v_ts1.s_addr) + printf(" TS1:%s", ipaddr_string(&v->v_ts1)); + if (v->v_ts2.s_addr) + printf(" TS2:%s", ipaddr_string(&v->v_ts2)); + +} + + +/* + * Print out arbitrary, unknown vendor data. + */ + +static void +other_print(bp, length) + register u_char *bp; + int length; +{ + u_char *ep; /* end pointer */ + u_char *zp; /* points one past last non-zero byte */ + + /* 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..41800d7 --- /dev/null +++ b/libexec/bootpd/trygetea.c @@ -0,0 +1,55 @@ +/* + * trygetea.c - test program for getether.c + * + * $Id$ + */ + +#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..e6226dc --- /dev/null +++ b/libexec/bootpd/trygetif.c @@ -0,0 +1,74 @@ +/* + * trygetif.c - test program for getif.c + * + * $Id$ + */ + +#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..0681dce --- /dev/null +++ b/libexec/bootpd/trylook.c @@ -0,0 +1,58 @@ +/* + * trylook.c - test program for lookup.c + * + * $Id$ + */ + +#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..4adc4ae --- /dev/null +++ b/libexec/bootpd/tzone.c @@ -0,0 +1,44 @@ +/* + * tzone.c - get the timezone + * + * This is shared by bootpd and bootpef + */ + +#ifdef SVR4 +/* XXX - Is this really SunOS specific? -gwr */ +/* This is in <time.h> but only visible if (__STDC__ == 1). */ +extern long timezone; +#else /* SVR4 */ +/* BSD or SunOS */ +# include <sys/time.h> +# include <syslog.h> +#endif /* SVR4 */ + +#include "bptypes.h" +#include "report.h" +#include "tzone.h" + +/* This is what other modules use. */ +int32 secondswest; + +/* + * Get our timezone offset so we can give it to clients if the + * configuration file doesn't specify one. + */ +void +tzone_init() +{ +#ifdef SVR4 + /* XXX - Is this really SunOS specific? -gwr */ + secondswest = timezone; +#else /* SVR4 */ + struct timezone tzp; /* Time zone offset for clients */ + struct timeval tp; /* Time (extra baggage) */ + if (gettimeofday(&tp, &tzp) < 0) { + secondswest = 0; /* Assume GMT for lack of anything better */ + report(LOG_ERR, "gettimeofday: %s", get_errmsg()); + } else { + secondswest = 60L * tzp.tz_minuteswest; /* Convert to seconds */ + } +#endif /* SVR4 */ +} diff --git a/libexec/bootpd/tzone.h b/libexec/bootpd/tzone.h new file mode 100644 index 0000000..ddd67c4 --- /dev/null +++ b/libexec/bootpd/tzone.h @@ -0,0 +1,3 @@ +/* tzone.h */ +extern int32 secondswest; +extern void tzone_init(); diff --git a/libexec/bugfiler/Makefile b/libexec/bugfiler/Makefile deleted file mode 100644 index 98489ad..0000000 --- a/libexec/bugfiler/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# @(#)Makefile 8.1 (Berkeley) 6/4/93 - -PROG= bugfiler -CFLAGS+=-I${.CURDIR} -SRCS= bugfiler.c error.c gethead.c process.c redist.c reply.c -BINOWN= root -BINMODE=4555 -MAN1= sendbug.0 -MAN8= bugfiler.0 - -beforeinstall: - install -c -o bin -g ${BINGRP} -m 555 \ - ${.CURDIR}/sendbug.sh ${DESTDIR}/usr/bin/sendbug - install -c -o bin -g ${BINGRP} -m 444 ${.CURDIR}/bugformat \ - ${DESTDIR}/usr/share/misc - -.include <bsd.prog.mk> diff --git a/libexec/bugfiler/bug.h b/libexec/bugfiler/bug.h deleted file mode 100644 index 69ea986..0000000 --- a/libexec/bugfiler/bug.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 1986, 1987, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)bug.h 8.1 (Berkeley) 6/4/93 - */ - -#define BUGS_HOME "owner-bugs@ucbvax.Berkeley.EDU" -#define BUGS_ID "bugs" - -/* - * the METOO definition has the bugfiler exit with an error (-1) status - * if there's a problem. This causes sendmail to send off a copy of the - * report (as failed mail) to the "owner" of the mail alias that executed - * the bugfiler. This is great if you would have otherwise lost the bug - * report. It's not so great if you get a whole bunch of mail that you - * really don't want. - */ -#define METOO - -/* files */ -#define ACK_FILE "bug:ack" /* acknowledge file */ -#define DIST_FILE "bug:redist" /* redistribution file */ -#define ERROR_FILE "log" /* error file */ -#define LOCK_FILE "bug:lock" /* lock file name */ -#define SUMMARY_FILE "summary" /* summary file */ -#define TMP_BUG "errors/BUG_XXXXXX" /* tmp bug report */ -#define TMP_DIR "errors" /* tmp directory */ - -#define CHN (char *)NULL /* null arg string */ -#define COMMENT '#' /* comment in redist file */ -#define EOS (char)NULL /* end of string */ -#define ERR -1 /* error return */ -#define MAXLINELEN 200 /* max line length in message */ -#define NO 0 /* no/false */ -#define OK 0 /* okay return */ -#define YES 1 /* yes/true */ - -typedef struct { - short found, /* line number if found */ - redist; /* if part of redist headers */ - int (*valid)(); /* validation routine */ - short len; /* length of tag */ - char *tag, /* leading tag */ - *line; /* actual line */ -} HEADER; -extern HEADER mailhead[]; - -#define DATE_TAG 0 /* "Date:" offset */ -#define FROM_TAG 1 /* "From " offset */ -#define CFROM_TAG 2 /* "From:" offset */ -#define INDX_TAG 3 /* "Index:" offset */ -#define MSG_TAG 4 /* "Message-Id:" offset */ -#define RPLY_TAG 5 /* "Reply-To:" offset */ -#define RET_TAG 6 /* "Return-Path:" offset */ -#define SUBJ_TAG 7 /* "Subject:" offset */ -#define TO_TAG 8 /* "To:" offset */ -#define APPAR_TO_TAG 9 /* "Apparently-To:" offset */ - -/* so sizeof doesn't return 0 */ -extern char bfr[MAXBSIZE], /* general I/O buffer */ - dir[MAXNAMLEN], /* subject and folder */ - folder[MAXNAMLEN], - tmpname[sizeof(TMP_BUG) + 5]; /* temp bug file */ diff --git a/libexec/bugfiler/bugfiler.8 b/libexec/bugfiler/bugfiler.8 deleted file mode 100644 index 1fc86d2..0000000 --- a/libexec/bugfiler/bugfiler.8 +++ /dev/null @@ -1,290 +0,0 @@ -.\" Copyright (c) 1983, 1991, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)bugfiler.8 8.2 (Berkeley) 12/11/93 -.\" -.Dd December 11, 1993 -.Dt BUGFILER 8 -.Os BSD 4.2 -.Sh NAME -.Nm bugfiler -.Nd file bug reports in folders automatically -.Sh SYNOPSIS -.Nm bugfiler -.Op Fl ar -.Op Fl v Ar version -.Sh DESCRIPTION -.Nm Bugfiler -is a program to automatically intercept, acknowledge, -redistribute and store bug reports. -.Nm Bugfiler -is normally invoked -by the mail delivery program with a line similar to the following in -.Pa /etc/aliases . -.Bd -literal -offset indent -bugs: "|bugfiler" -.Ed -.Pp -It should be noted that the login -.Dq bugs -must exist for the bugfiler -to run. Unless otherwise noted all paths used by -.Nm bugfiler -are -relative to the home directory of this login. -.Nm Bugfiler -also -expects all of its files and directories to be owned by -.Dq bugs . -.Pp -Available options. -.Bl -tag -width Ds -.It Fl a -Do not send automatic mail acknowledgement to the bug report filer. -(The default is to send the acknowledgement with the file -.Pa ~bugs/version/bug:ack -appended). -.It Fl r -Do not redistribute. -.It Fl v Ar version -Override the -.Ar version -provided within the bug report itself. -.El -.Pp -For the bug report to be correctly filed, it must contain a line -in the following format: -.Pp -.Bd -filled -offset indent -compact -.Bl -column Index folder -.It Index: Ta Em folder Ta Ar version -.El -.Ed -.Pp -The directories -.Pa ~bugs/ Ns Ar version -and -.Pa ~bugs/ Ns Ar version/ Ns Em folder -must exist before -.Nm bugfiler -attempts to store the bug report. Bug -reports will be stored in files named by the concatenation of -.Ar version , -.Em folder , -and sequential numbers, i.e. if -.Ar version -is -.Dq 4.3 Tn BSD -and -.Em folder -is -.Dq ucb -the first bug report will be placed in -.Pa ~bugs/4.3BSD/ucb/1 . -If -.Em folder -contains more than one component only -the first one will be used, e.g. if -.Em folder -is -.Dq bin/from.c -or -.Dq bin/adb/con.c -it will be treated as if it were simply -.Dq bin . -.Pp -.Pp -If the -.Fl r -flag is not supplied, redistribution of the bug reports -is done as specified in the file -.Pa ~bugs/version/bug:redist . -This file -is in the format of the -.Xr aliases 5 -file, including comments and -entries requiring multiple lines, with the single exception that the -.Em folder -component of the -.Dq Index: -line replaces the name to alias. -The special folder -.Dq all: -receives a redistribution of all bug reports -sent to this -.Ar version . -For example, the -.Pa bug:redist -file -.Pp -.Bd -literal -offset indent -compact -# bigbug gets a copy of everything -all: bigbug -# ucb folder redistribution list -ucb: karels, kjd@coke.berkeley.edu - ra@beno.css.gov -.Ed -.Pp -will send copies of all bug reports with -.Dq ucb -as the -.Em folder -to bigbug, karels, kjd, and ra. -.Pp -Reports that cannot be filed, due to an invalid -.Dq Index: -line or -some other error, are placed in the directory -.Pa ~bugs/errors . -The -.Nm bugfiler -maintainer should correct these bug reports and then -run -.Nm bugfiler , -with the corrected report as its standard input, -as bug reports with errors are neither acknowledged or redistributed. -All reports that -.Nm bugfiler -handles are logged in -.Pa ~bugs/log. -.Pp -Valid bugs are also logged in the file -.Pa ~bugs/version/summary. -This file has an entry for each bug report for -.Ar version -in the -format: -.Pp -.Bd -literal -offset indent -compact -Filename Date - Subject: - Index: - Owner: Bugs Bunny - Status: Received -.Ed -.Pp -.Li Filename -is the concatenation of -.Ar version , -.Em folder , -and a number -as described above. -.Xr Date -is the date as reported by the system -clock, using -.Xr ctime 3 . -The -.Li Subject: -and -.Li Index: -lines are -copies of the -.Dq Subject: -and -.Dq index: -lines contained in the bug -report. The -.Li Owner -and -.Li Status -fields are intended to provide a -rudimentary method of tracking the status of bug reports. -.Pp -The file -.Pa ~bugs/bug:lock -is the focus of all locking for -.Nm bugfiler . -If you wish to manipulate any of the log or error files, rename or remove -it and -.Nm bugfiler -will treat all bug reports that it receives as if -they were incorrectly formatted, i.e. it will place them in the directory -.Pa ~bugs/errors , -for later recovery by the -.Nm bugfiler -maintainer. -Obviously, this file must be created when you first install -.Nm bugfiler . -.Pp -All errors that occur before -.Pa ~bugs/log -is found are logged into the system -log file, using -.Xr syslog 8 . -.Sh FILES -.Bl -tag -width /usr/share/misc/bugformatxx -compact -.It Pa ~bugs/bug:ack -the acknowledgement message -.It Pa ~bugs/bug:redist -the redistribution list -.It Pa ~bugs/bug:lock -the locking file -.It Pa ~bugs/errors/BUG_?????? -bug reports with format errors -.It Pa ~bugs/log -the log file -.It Pa ~bugs/folder/summary -the summary files -.It Pa /usr/sbin/sendmail -the mail delivery program -.It Pa /usr/share/misc/bugformat -a sample bug report format -.El -.Sh SEE ALSO -.Xr sendbug 1 , -.Xr aliases 5 , -.Xr syslog 8 -.Sh BUGS -Since mail can be forwarded in a number of different ways, -.Nm bugfiler -does not recognize forwarded mail and will acknowledge to the forwarder -instead of the original sender unless there is a -.Dq Reply-To -field in the -header. -.Pp -This version of -.Nm bugfiler -is not compatible with the version -released with -.Bx 4.3 -in that it doesn't complain to the sender about -incorrectly formatted bug reports. -Frankly, we got tired of the profanity, not to mention the extended -conversations -.Nm bugfiler -was holding with -.Xr vacation 1 . -.Sh HISTORY -The -.Nm -command appeared in -.Bx 4.2 . diff --git a/libexec/bugfiler/bugfiler.c b/libexec/bugfiler/bugfiler.c deleted file mode 100644 index 75dbef3..0000000 --- a/libexec/bugfiler/bugfiler.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 1983, 1986, 1987, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char copyright[] = -"@(#) Copyright (c) 1983, 1986, 1987, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)bugfiler.c 8.1 (Berkeley) 6/4/93"; -#endif /* not lint */ - -/* - * Bug report processing program, designed to be invoked - * through aliases(5). - */ -#include <sys/param.h> -#include <sys/time.h> -#include <sys/stat.h> - -#include <dirent.h> -#include <pwd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "bug.h" -#include "extern.h" - -char bfr[MAXBSIZE], /* general I/O buffer */ - tmpname[sizeof(TMP_BUG) + 5]; /* temp bug file */ - -static void logit __P((void)); -static void make_copy __P((void)); - -int -main(argc, argv) - int argc; - char *argv[]; -{ - extern char *optarg; /* getopt arguments */ - register struct passwd *pwd; /* bugs password entry */ - register int ch; /* getopts char */ - int do_ack, /* acknowledge bug report */ - do_redist; /* redistribut BR */ - char *argversion; /* folder name provided */ - - do_ack = do_redist = YES; - argversion = NULL; - while ((ch = getopt(argc, argv, "av:r")) != EOF) - switch(ch) { - case 'a': - do_ack = NO; - break; - case 'v': - argversion = optarg; - break; - case 'r': - do_redist = NO; - break; - case '?': - default: - fputs("usage: bugfiler [-ar] [-v version]\n", stderr); - error("usage: bugfiler [-ar] [-v version]", CHN); - } - - if (!(pwd = getpwnam(BUGS_ID))) - error("can't find bugs login.", BUGS_ID); - - if (chdir(pwd->pw_dir)) /* change to bugs home directory */ - error("can't chdir to %s.", pwd->pw_dir); - - if (seteuid(pwd->pw_uid)) - error("can't set id to %s.", BUGS_ID); - - (void)umask(02); /* everything is 664 */ - seterr(); /* redirect to log file */ - logit(); /* log report arrival */ - make_copy(); /* save copy in case */ - gethead(do_redist); - - if (argversion) /* specific folder requested */ - (void)strcpy(dir, argversion); - - process(); - - if (seteuid(0)) - error("can't set id to root.", CHN); - if (do_ack) - reply(); - if (do_redist) - redist(); - (void)unlink(tmpname); - exit(OK); -} - -/* - * make_copy -- - * make a copy of bug report in error folder - */ -static void -make_copy() -{ - register int cnt, /* read return value */ - tfd; /* temp file descriptor */ - - if (access(TMP_DIR, F_OK)) - (void)mkdir(TMP_DIR, S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH); - (void)strcpy(tmpname, TMP_BUG); - if (tfd = mkstemp(tmpname)) { - while ((cnt = read(fileno(stdin), - bfr, sizeof(bfr))) != ERR && cnt) - write(tfd, bfr, cnt); - (void)close(tfd); - return; - } - error("can't make copy using %s.", tmpname); -} - -/* - * logit -- - * log this run of the bugfiler - */ -static void -logit() -{ - struct timeval tp; - char *C1, *C2; - - if (gettimeofday(&tp, (struct timezone *)NULL)) - error("can't get time of day.", CHN); - for (C1 = C2 = ctime(&tp.tv_sec); *C1 && *C1 != '\n'; ++C1); - *C1 = EOS; - fputs(C2, stderr); -} diff --git a/libexec/bugfiler/bugformat b/libexec/bugfiler/bugformat deleted file mode 100644 index 97a767d..0000000 --- a/libexec/bugfiler/bugformat +++ /dev/null @@ -1,32 +0,0 @@ -Subject: Short summary of the problem (please make this meaningful!) -Index: folder 4.4BSD-alpha - -Description: - Detailed description of the problem, suggestion, or complaint. -Repeat-By: - Describe the sequence of events that causes the problem - to occur. -Fix: - Description of how to fix the problem. If you don't know a - fix for the problem, don't include this section. - --------- Remove this line and what's below it, for reference only. -------- - -To ensure that your bug report is handled correctly by bugfiler(8), -you must replace "folder" (on the line above starting with "Index:") -with one of the following values: - - folder ::= bin | doc | etc | games | ideas | include | lib - | local | man | misc | new | sys | ucb - | usr.bin | usr.lib - -If you're not running 4.3BSD, you should also replace "4.3BSD" on -the same line with one of the following values. - - version ::= 4.3BSD | 4.3BSD-tahoe | 4.3BSD-reno | net2 - | 4.4BSD-alpha - -For example, if your bug concerns the program "/usr/bin/file" and -you're currently running 4.3BSD-Reno, you should replace "folder" -with "usr.bin/file", and "4.4BSD-alpha" with "4.3BSD-Reno". The folder -"ideas" is for suggestions. diff --git a/libexec/bugfiler/gethead.c b/libexec/bugfiler/gethead.c deleted file mode 100644 index ba7e361..0000000 --- a/libexec/bugfiler/gethead.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 1986, 1987, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)gethead.c 8.1 (Berkeley) 6/4/93"; -#endif /* not lint */ - -#include <sys/param.h> -#include <sys/stat.h> - -#include <dirent.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "pathnames.h" -#include "bug.h" -#include "extern.h" - -static int chk1 __P((char *)); -static int pbuf __P((char *)); - -#define ENT(X) sizeof(X) - 1, X -HEADER mailhead[] = { /* mail headers */ - { NO, YES, NULL, ENT("Date:"), }, - { NO, NO, NULL, ENT("From "), }, - { NO, YES, NULL, ENT("From:"), }, - { NO, NO, chk1, ENT("Index:"), }, - { NO, YES, NULL, ENT("Message-Id:"), }, - { NO, YES, NULL, ENT("Reply-To:"), }, - { NO, YES, NULL, ENT("Return-Path:"), }, - { NO, NO, pbuf, ENT("Subject:"), }, - { NO, YES, NULL, ENT("To:"), }, - { NO, NO, NULL, ENT("Apparently-To:"), }, - { ERR, } -}; - -FILE *dfp; /* distf file pointer */ -char dir[MAXNAMLEN], /* subject and folder */ - folder[MAXNAMLEN]; - -/* - * gethead -- - * read mail and bug headers from bug report, construct redist headers - */ -void -gethead(redist) - int redist; -{ - register HEADER *hp; /* mail header pointer */ - - if (redist) { - int fd; - char *distf; - - distf = strdup(_PATH_TMP); - if (!(fd = mkstemp(distf)) || !(dfp = fdopen(fd, "w+"))) - error("can't create redistribution file %s.", distf); - /* disappear after last reference is closed */ - (void)unlink(distf); - free(distf); - } - if (!freopen(tmpname, "r", stdin)) - error("can't read temporary bug file %s.", tmpname); - - while (fgets(bfr, sizeof(bfr), stdin)) { - for (hp = mailhead; hp->found != ERR; ++hp) - if (!hp->found) - if (!strncmp(hp->tag, bfr, hp->len)) { - if (hp->valid && !((*(hp->valid))(bfr))) - break; - if (!(hp->line = - malloc((u_int)(strlen(bfr) + 1)))) - error("malloc failed.", CHN); - (void)strcpy(hp->line, bfr); - hp->found = YES; - break; - } - if ((hp->found == ERR || hp->redist) && redist) - fputs(bfr, dfp); - } - - if (!mailhead[INDX_TAG].found) - error("no readable \"Index:\" header in bug report.", CHN); -} - -/* - * chk1 -- - * parse the "Index:" line into folder and directory - */ -static int -chk1(line) - char *line; -{ - register char *C; /* tmp pointer */ - struct stat sbuf; /* existence check */ - - if (sscanf(line, " Index: %s %s ", folder, dir) != 2) - return(NO); - if (C = strchr(folder, '/')) { /* deal with "bin/from.c" */ - if (C == folder) - return(NO); - *C = EOS; - } - if (stat(dir, &sbuf) || (sbuf.st_mode & S_IFMT) != S_IFDIR) - return(NO); - (void)pbuf(line); - return(YES); -} - -/* - * pbuf -- - * kludge so that summary file looks pretty - */ -static int -pbuf(line) - char *line; -{ - register char *rp, /* tmp pointers */ - *wp; - - for (rp = line; *rp == ' ' || *rp == '\t'; ++rp); - for (wp = line; *rp; ++wp) { - if ((*wp = *rp++) != ' ' && *wp != '\t') - continue; - *wp = ' '; - while (*rp == ' ' || *rp == '\t') - ++rp; - } - if (wp[-1] == ' ') /* wp can't == line */ - --wp; - *wp = EOS; - return(YES); -} diff --git a/libexec/bugfiler/process.c b/libexec/bugfiler/process.c deleted file mode 100644 index 6d376fd..0000000 --- a/libexec/bugfiler/process.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 1986, 1987, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)process.c 8.1 (Berkeley) 6/4/93"; -#endif /* not lint */ - -#include <sys/param.h> -#include <sys/time.h> - -#include <ctype.h> -#include <dirent.h> -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> - -#include "bug.h" -#include "extern.h" - -char pfile[MAXPATHLEN]; /* permanent file name */ - -static int getnext __P((void)); - -/* - * process -- - * copy report to permanent file, - * update summary file. - */ -int -process() -{ - register int rval; /* read return value */ - struct timeval tp; /* time of day */ - int lfd; /* lock file descriptor */ - - if (access(LOCK_FILE, R_OK) || (lfd = open(LOCK_FILE, O_RDONLY, 0)) < 0) - error("can't find lock file %s.", LOCK_FILE); - if (flock(lfd, LOCK_EX)) - error("can't get lock.", CHN); - sprintf(pfile, "%s/%s/%d", dir, folder, getnext()); - fprintf(stderr, "\t%s\n", pfile); - if (!(freopen(pfile, "w", stdout))) - error("can't create %s.", pfile); - rewind(stdin); - while ((rval = read(fileno(stdin), bfr, sizeof(bfr))) != ERR && rval) - if (write(fileno(stdout), bfr, rval) != rval) - error("write to %s failed.", pfile); - - /* append information to the summary file */ - sprintf(bfr, "%s/%s", dir, SUMMARY_FILE); - if (!(freopen(bfr, "a", stdout))) - error("can't append to summary file %s.", bfr); - if (gettimeofday(&tp, (struct timezone *)NULL)) - error("can't get time of day.", CHN); - printf("\n%s\t\t%s\t%s\t%s\tOwner: Bugs Bunny\n\tStatus: Received\n", - pfile, ctime(&tp.tv_sec), mailhead[INDX_TAG].line, - mailhead[SUBJ_TAG].found ? mailhead[SUBJ_TAG].line : "Subject:\n"); - (void)flock(lfd, LOCK_UN); - (void)fclose(stdout); -} - -/* - * getnext -- - * get next file name (number) - */ -static int -getnext() -{ - register struct dirent *d; /* directory structure */ - register DIR *dirp; /* directory pointer */ - register int highval, newval; - register char *p; - - (void)sprintf(bfr, "%s/%s", dir, folder); - if (!(dirp = opendir(bfr))) - error("can't read folder directory %s.", bfr); - for (highval = -1; d = readdir(dirp);) { - for (p = d->d_name; *p && isdigit(*p); ++p); - if (!*p && (newval = atoi(d->d_name)) > highval) - highval = newval; - } - closedir(dirp); - return(++highval); -} diff --git a/libexec/bugfiler/redist.c b/libexec/bugfiler/redist.c deleted file mode 100644 index 87a18b6..0000000 --- a/libexec/bugfiler/redist.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 1986, 1987, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)redist.c 8.1 (Berkeley) 6/4/93"; -#endif /* not lint */ - -#include <sys/param.h> - -#include <ctype.h> -#include <dirent.h> -#include <stdio.h> -#include <string.h> - -#include "bug.h" -#include "pathnames.h" -#include "extern.h" - -/* - * redist -- - * Redistribute a bug report to those people indicated in the - * redistribution list file. - */ -void -redist() -{ - extern FILE *dfp; /* dist file fp */ - extern char pfile[]; /* permanent bug file */ - register char *C1, *C2; - FILE *pf; - int group; - - (void)sprintf(bfr, "%s/%s", dir, DIST_FILE); - if (!freopen(bfr, "r", stdin)) - return; - for (pf = NULL, group = 0; fgets(bfr, sizeof(bfr), stdin);) { - if (C1 = strchr(bfr, '\n')) - *C1 = '\0'; -nextline: if (*bfr == COMMENT || - isspace(*bfr) || !(C1 = index(bfr, ':'))) - continue; - *C1 = EOS; - if (!strcmp(bfr, folder) || !strcmp(bfr, "all")) { - for (++C1; *C1 && (*C1 == ' ' || *C1 == '\t'); ++C1); - if (!*C1) /* if empty list */ - continue; - if (!pf) { - if (!(pf = popen(MAIL_CMD, "w"))) - error("sendmail pipe failed.", CHN); - if (mailhead[SUBJ_TAG].found) - fprintf(pf, - "%s", mailhead[SUBJ_TAG].line); - else - fprintf(pf, - "Subject: Untitled Bug Report\n"); - if (!mailhead[TO_TAG].line) { - if (mailhead[APPAR_TO_TAG].line) - fprintf(pf, "To%s", - strchr(mailhead[APPAR_TO_TAG].line, - ':')); - else - fprintf(pf, "To: %s\n", BUGS_ID); - } - fputs("Resent-To: ", pf); - } - /* - * write out first entry, then succeeding entries - * backward compatible, handles back slashes at end - * of line - */ - if (group++) - fputs(", ", pf); - for (;;) { - if (C2 = strchr(C1, '\\')) - *C2 = EOS; - fputs(C1, pf); - if (!fgets(bfr, sizeof(bfr), stdin)) - break; - if (C1 = strchr(bfr, '\n')) - *C1 = '\0'; - if (*bfr != ' ' && *bfr != '\t') - goto nextline; - for (C1 = bfr; - *C1 && (*C1 == ' ' || *C1 == '\t'); ++C1); - } - } - } - if (!pf) - return; - - putc('\n', pf); - - rewind(dfp); - /* add Reference header and copy bug report out */ - while (fgets(bfr, sizeof(bfr), dfp) && *bfr != '\n') - fputs(bfr, pf); - fprintf(pf, "\n%sReference: %s\n\n", mailhead[INDX_TAG].line, pfile); - while (fgets(bfr, sizeof(bfr), dfp)) - fputs(bfr, pf); - (void)pclose(pf); -} diff --git a/libexec/bugfiler/reply.c b/libexec/bugfiler/reply.c deleted file mode 100644 index 0f99401..0000000 --- a/libexec/bugfiler/reply.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 1986, 1987, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char sccsid[] = "@(#)reply.c 8.1 (Berkeley) 6/4/93"; -#endif /* not lint */ - -#include <sys/param.h> - -#include <dirent.h> -#include <fcntl.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> - -#include "bug.h" -#include "extern.h" -#include "pathnames.h" - -/* - * reply -- - * tell the user we got their silly little bug report - */ -void -reply() -{ - register char *C, /* traveling pointer */ - *to; /* who we're replying to */ - register int afd, /* ack file descriptor */ - rval; /* return value */ - FILE *pf; /* pipe pointer */ - - if (mailhead[RPLY_TAG].found) { - for (C = mailhead[RPLY_TAG].line + mailhead[RPLY_TAG].len; - *C != '\n' && (*C == ' ' || *C == '\t');++C); - if (*C) - goto gotone; - } - if (mailhead[FROM_TAG].found) { - for (C = mailhead[FROM_TAG].line + mailhead[FROM_TAG].len; - *C != '\n' && (*C == ' ' || *C == '\t');++C); - if (*C) - goto gotone; - } - if (mailhead[CFROM_TAG].found) { - for (C = mailhead[CFROM_TAG].line + mailhead[CFROM_TAG].len; - *C != '\n' && (*C == ' ' || *C == '\t');++C); - if (*C) - goto gotone; - } - return; - - /* if it's a foo <XXX>, get the XXX, else get foo (first string) */ -gotone: if (to = strchr(C, '<')) - for (C = ++to; - *C != '\n' && *C != ' ' && *C != '\t' && *C != '>';++C); - else { - to = C; - for (to = C++;*C != '\n' && *C != ' ' && *C != '\t';++C); - } - *C = EOS; - - if (!(pf = popen(MAIL_CMD, "w"))) - error("sendmail pipe failed.", CHN); - - fprintf(pf, "Reply-To: %s\nFrom: %s (Bugs Bunny)\nTo: %s\n", - BUGS_HOME, BUGS_HOME, to); - if (mailhead[SUBJ_TAG].found) - fprintf(pf, "Subject: Re:%s", - mailhead[SUBJ_TAG].line + mailhead[SUBJ_TAG].len); - else - fputs("Subject: Bug report acknowledgement.\n", pf); - if (mailhead[DATE_TAG].found) - fprintf(pf, "In-Acknowledgement-Of: Your message of %s", - mailhead[DATE_TAG].line + mailhead[DATE_TAG].len); - if (mailhead[MSG_TAG].found) - fprintf(pf, "\t\t%s", mailhead[MSG_TAG].line); - fputs("Precedence: bulk\n\n", pf); /* vacation(1) uses this... */ - fflush(pf); - - (void)sprintf(bfr, "%s/%s", dir, ACK_FILE); - if ((afd = open(bfr, O_RDONLY, 0)) >= 0) { - while ((rval = read(afd, bfr, sizeof(bfr))) != ERR && rval) - (void)write(fileno(pf), bfr, rval); - (void)close(afd); - } - pclose(pf); -} diff --git a/libexec/bugfiler/sendbug.sh b/libexec/bugfiler/sendbug.sh deleted file mode 100644 index 911ef9d..0000000 --- a/libexec/bugfiler/sendbug.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/bin/sh - -# -# Copyright (c) 1983, 1993 -# The Regents of the University of California. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. All advertising materials mentioning features or use of this software -# must display the following acknowledgement: -# This product includes software developed by the University of -# California, Berkeley and its contributors. -# 4. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)sendbug.sh 8.1 (Berkeley) 6/4/93 -# - -# create a bug report and mail it to '4bsd-bugs'. - -PATH=/bin:/sbin:/usr/sbin:/usr/bin -export PATH - -TEMP=/tmp/bug$$ -FORMAT=/usr/share/misc/bugformat - -# uucp sites should use ": ${BUGADDR=ucbvax!4bsd-bugs}" with a suitable path. -: ${BUGADDR=4bsd-bugs@CS.Berkeley.EDU} -: ${EDITOR=vi} - -trap 'rm -f $TEMP ; exit 1' 1 2 3 13 15 - -cp $FORMAT $TEMP -chmod u+w $TEMP -if $EDITOR $TEMP -then - if cmp -s $FORMAT $TEMP - then - echo "File not changed, no bug report submitted." - exit - fi - case "$#" in - 0) sendmail -t -oi $BUGADDR < $TEMP ;; - *) sendmail -t -oi "$@" < $TEMP ;; - esac -fi - -rm -f $TEMP diff --git a/libexec/comsat/Makefile b/libexec/comsat/Makefile index 0e11b5f..7352ab5 100644 --- a/libexec/comsat/Makefile +++ b/libexec/comsat/Makefile @@ -1,6 +1,7 @@ # @(#)Makefile 8.1 (Berkeley) 6/4/93 +# $Id$ PROG= comsat -MAN8= comsat.0 +MAN8= comsat.8 .include <bsd.prog.mk> diff --git a/libexec/comsat/comsat.8 b/libexec/comsat/comsat.8 index 395522c..916d1c9 100644 --- a/libexec/comsat/comsat.8 +++ b/libexec/comsat/comsat.8 @@ -30,6 +30,7 @@ .\" SUCH DAMAGE. .\" .\" @(#)comsat.8 8.1 (Berkeley) 6/4/93 +.\" $Id$ .\" .Dd June 4, 1993 .Dt COMSAT 8 @@ -53,7 +54,7 @@ and .Xr inetd 8 ) . The one line messages are of the form: .Pp -.Dl user@mailbox-offset +.Dl user@mailbox-offset[:mailbox-name] .Pp If the .Em user @@ -72,10 +73,14 @@ the message header other than the or .Dq Subject lines are not included in the displayed message. +.Pp +If mailbox-name omitted, standard mailbox assumed. .Sh FILES -.Bl -tag -width /var/run/utmp -compact +.Bl -tag -width /var/mail/user -compact .It Pa /var/run/utmp to find out who's logged on and on what terminals +.It Pa /var/mail/user +standard mailbox .El .Sh SEE ALSO .Xr biff 1 , diff --git a/libexec/comsat/comsat.c b/libexec/comsat/comsat.c index e2e4477..c3b876a 100644 --- a/libexec/comsat/comsat.c +++ b/libexec/comsat/comsat.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id$ */ #ifndef lint @@ -54,7 +56,7 @@ static char sccsid[] = "@(#)comsat.c 8.1 (Berkeley) 6/4/93"; #include <netdb.h> #include <paths.h> #include <pwd.h> -#include <sgtty.h> +#include <termios.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> @@ -73,9 +75,9 @@ struct utmp *utmp = NULL; time_t lastmsgtime; int nutmp, uf; -void jkfprintf __P((FILE *, char[], off_t)); +void jkfprintf __P((FILE *, char[], char[], off_t)); void mailfor __P((char *)); -void notify __P((struct utmp *, off_t)); +void notify __P((struct utmp *, char[], off_t, int)); void onalrm __P((int)); void reapchildren __P((int)); @@ -87,7 +89,7 @@ main(argc, argv) struct sockaddr_in from; register int cc; int fromlen; - char msgbuf[100]; + char msgbuf[256]; /* verify proper invocation */ fromlen = sizeof(from); @@ -170,27 +172,43 @@ mailfor(name) { register struct utmp *utp = &utmp[nutmp]; register char *cp; + char *file; off_t offset; + int folder; + char buf[sizeof(_PATH_MAILDIR) + sizeof(utmp[0].ut_name) + 1]; + char buf2[sizeof(_PATH_MAILDIR) + sizeof(utmp[0].ut_name) + 1]; if (!(cp = strchr(name, '@'))) return; *cp = '\0'; offset = atoi(cp + 1); + if (!(cp = strchr(cp + 1, ':'))) + file = name; + else + file = cp + 1; + sprintf(buf, "%s/%.*s", _PATH_MAILDIR, sizeof(utmp[0].ut_name), name); + if (*file != '/') { + sprintf(buf2, "%s/%.*s", _PATH_MAILDIR, sizeof(utmp[0].ut_name), file); + file = buf2; + } + folder = strcmp(buf, file); while (--utp >= utmp) if (!strncmp(utp->ut_name, name, sizeof(utmp[0].ut_name))) - notify(utp, offset); + notify(utp, file, offset, folder); } static char *cr; void -notify(utp, offset) +notify(utp, file, offset, folder) register struct utmp *utp; + char file[]; off_t offset; + int folder; { FILE *tp; struct stat stb; - struct sgttyb gttybuf; + struct termios tio; char tty[20], name[sizeof(utmp[0].ut_name) + 1]; (void)snprintf(tty, sizeof(tty), "%s%.*s", @@ -213,22 +231,24 @@ notify(utp, offset) dsyslog(LOG_ERR, "%s: %s", tty, strerror(errno)); _exit(-1); } - (void)ioctl(fileno(tp), TIOCGETP, >tybuf); - cr = (gttybuf.sg_flags&CRMOD) && !(gttybuf.sg_flags&RAW) ? - "\n" : "\n\r"; + (void)tcgetattr(fileno(tp), &tio); + cr = ((tio.c_oflag & (OPOST|ONLCR)) == (OPOST|ONLCR)) ? "\n" : "\n\r"; (void)strncpy(name, utp->ut_name, sizeof(utp->ut_name)); name[sizeof(name) - 1] = '\0'; - (void)fprintf(tp, "%s\007New mail for %s@%.*s\007 has arrived:%s----%s", - cr, name, (int)sizeof(hostname), hostname, cr, cr); - jkfprintf(tp, name, offset); + (void)fprintf(tp, "%s\007New mail for %s@%.*s\007 has arrived%s%s%s:%s----%s", + cr, name, (int)sizeof(hostname), hostname, + folder ? cr : "", folder ? "to " : "", folder ? file : "", + cr, cr); + jkfprintf(tp, name, file, offset); (void)fclose(tp); _exit(0); } void -jkfprintf(tp, name, offset) +jkfprintf(tp, user, file, offset) register FILE *tp; - char name[]; + char user[]; + char file[]; off_t offset; { register char *cp, ch; @@ -238,10 +258,10 @@ jkfprintf(tp, name, offset) char line[BUFSIZ]; /* Set effective uid to user in case mail drop is on nfs */ - if ((p = getpwnam(name)) != NULL) + if ((p = getpwnam(user)) != NULL) (void) setuid(p->pw_uid); - if ((fi = fopen(name, "r")) == NULL) + if ((fi = fopen(file, "r")) == NULL) return; (void)fseek(fi, offset, L_SET); @@ -271,9 +291,18 @@ jkfprintf(tp, name, offset) } /* strip weird stuff so can't trojan horse stupid terminals */ for (cp = line; (ch = *cp) && ch != '\n'; ++cp, --charcnt) { - ch = toascii(ch); - if (!isprint(ch) && !isspace(ch)) + if (!isprint(ch)) { + if (ch & 0x80) + (void)fputs("M-", tp); + ch &= 0177; + if (!isprint(ch)) { + if (ch == 0177) + ch = '?'; + else ch |= 0x40; + (void)fputc('^', tp); + } + } (void)fputc(ch, tp); } (void)fputs(cr, tp); diff --git a/libexec/fingerd/Makefile b/libexec/fingerd/Makefile index e2ed65c..4deac64 100644 --- a/libexec/fingerd/Makefile +++ b/libexec/fingerd/Makefile @@ -1,6 +1,7 @@ # @(#)Makefile 8.1 (Berkeley) 6/4/93 +# $Id$ PROG= fingerd -MAN8= fingerd.0 +MAN8= fingerd.8 .include <bsd.prog.mk> diff --git a/libexec/fingerd/fingerd.8 b/libexec/fingerd/fingerd.8 index c5c0762..f2a5810 100644 --- a/libexec/fingerd/fingerd.8 +++ b/libexec/fingerd/fingerd.8 @@ -56,7 +56,9 @@ protocol consists mostly of specifying a single .Dq command line . .Pp .Nm Fingerd -listens for +is started by +.Xr inetd 8 , +which listens for .Tn TCP requests at port 79. Once connected it reads a single command line @@ -116,7 +118,8 @@ to have more control over what information is provided to remote sites. .El .Sh SEE ALSO -.Xr finger 1 +.Xr finger 1 , +.Xr inetd 8 .Sh BUGS Connecting directly to the server from a .Tn TIP diff --git a/libexec/fingerd/fingerd.c b/libexec/fingerd/fingerd.c index 74173a9..93d6f25 100644 --- a/libexec/fingerd/fingerd.c +++ b/libexec/fingerd/fingerd.c @@ -38,12 +38,17 @@ static char copyright[] = #endif /* not lint */ #ifndef lint +/* static char sccsid[] = "@(#)fingerd.c 8.1 (Berkeley) 6/4/93"; +*/ +static const char rcsid[] = + "$Id: fingerd.c,v 1.7 1997/02/22 14:21:25 peter Exp $"; #endif /* not lint */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> +#include <netinet/tcp.h> #include <arpa/inet.h> #include <errno.h> @@ -55,7 +60,7 @@ static char sccsid[] = "@(#)fingerd.c 8.1 (Berkeley) 6/4/93"; #include <strings.h> #include "pathnames.h" -void err __P((const char *, ...)); +void logerr __P((const char *, ...)); int main(argc, argv) @@ -75,7 +80,7 @@ main(argc, argv) logging = secure = 0; openlog("fingerd", LOG_PID | LOG_CONS, LOG_DAEMON); opterr = 0; - while ((ch = getopt(argc, argv, "slp:")) != EOF) + while ((ch = getopt(argc, argv, "slp:")) != -1) switch (ch) { case 'l': logging = 1; @@ -88,13 +93,13 @@ main(argc, argv) break; case '?': default: - err("illegal option -- %c", ch); + logerr("illegal option -- %c", ch); } if (logging) { sval = sizeof(sin); if (getpeername(0, (struct sockaddr *)&sin, &sval) < 0) - err("getpeername: %s", strerror(errno)); + logerr("getpeername: %s", strerror(errno)); if (hp = gethostbyaddr((char *)&sin.sin_addr.s_addr, sizeof(sin.sin_addr.s_addr), AF_INET)) lp = hp->h_name; @@ -103,21 +108,33 @@ main(argc, argv) syslog(LOG_NOTICE, "query from %s", lp); } + /* + * Enable server-side Transaction TCP. + */ + { + int one = 1; + if (setsockopt(STDOUT_FILENO, IPPROTO_TCP, TCP_NOPUSH, &one, + sizeof one) < 0) { + logerr("setsockopt(TCP_NOPUSH) failed: %m"); + } + } + if (!fgets(line, sizeof(line), stdin)) exit(1); - + comp = &av[1]; - for (lp = line, ap = &av[2];;) { + av[2] = "--"; + for (lp = line, ap = &av[3];;) { *ap = strtok(lp, " \t\r\n"); if (!*ap) { - if (secure && ap == &av[2]) { + if (secure && ap == &av[3]) { puts("must provide username\r\n"); exit(1); } break; } if (secure && strchr(*ap, '@')) { - puts("fowarding service denied\r\n"); + puts("forwarding service denied\r\n"); exit(1); } @@ -136,7 +153,7 @@ main(argc, argv) else *comp = prog; if (pipe(p) < 0) - err("pipe: %s", strerror(errno)); + logerr("pipe: %s", strerror(errno)); switch(vfork()) { case 0: @@ -146,14 +163,14 @@ main(argc, argv) (void)close(p[1]); } execv(prog, comp); - err("execv: %s: %s", prog, strerror(errno)); + logerr("execv: %s: %s", prog, strerror(errno)); _exit(1); case -1: - err("fork: %s", strerror(errno)); + logerr("fork: %s", strerror(errno)); } (void)close(p[1]); if (!(fp = fdopen(p[0], "r"))) - err("fdopen: %s", strerror(errno)); + logerr("fdopen: %s", strerror(errno)); while ((ch = getc(fp)) != EOF) { if (ch == '\n') putchar('\r'); @@ -170,9 +187,9 @@ main(argc, argv) void #if __STDC__ -err(const char *fmt, ...) +logerr(const char *fmt, ...) #else -err(fmt, va_alist) +logerr(fmt, va_alist) char *fmt; va_dcl #endif diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile index 946aab7..49e98d5 100644 --- a/libexec/ftpd/Makefile +++ b/libexec/ftpd/Makefile @@ -1,9 +1,31 @@ # @(#)Makefile 8.2 (Berkeley) 4/4/94 +# $Id: Makefile,v 1.21 1997/04/26 12:12:10 davidn Exp $ PROG= ftpd -CFLAGS+=-DSETPROCTITLE -SRCS= ftpd.c ftpcmd.c logwtmp.c popen.c -MAN8= ftpd.0 +MAN8= ftpd.8 +SRCS= ftpd.c ftpcmd.c logwtmp.c popen.c skey-stuff.c + +CFLAGS+=-DSETPROCTITLE -DSKEY -DLOGIN_CAP -DVIRTUAL_HOSTING -Wall + +LDADD= -lskey -lmd -lcrypt -lutil +DPADD= ${LIBSKEY} ${LIBMD} ${LIBCRYPT} ${LIBUTIL} + CLEANFILES+=ftpcmd.c y.tab.h +.ifdef FTPD_INTERNAL_LS +LSDIR= ../../bin/ls +.PATH: ${.CURDIR}/${LSDIR} +SRCS+= ls.c cmp.c print.c stat_flags.c util.c +CFLAGS+=-DINTERNAL_LS -Dmain=ls_main -I${.CURDIR}/${LSDIR} +.endif + +.if exists(${DESTDIR}/usr/lib/libkrb.a) && defined(MAKE_EBONES) +.PATH: ${.CURDIR}/../../usr.bin/login +SRCS+= klogin.c +LDADD+= -lkrb -ldes +DPADD+= ${LIBKRB} ${LIBDES} +CFLAGS+=-DKERBEROS +DISTRIBUTION= krb +.endif + .include <bsd.prog.mk> diff --git a/libexec/ftpd/extern.h b/libexec/ftpd/extern.h index e3336b5..616da79 100644 --- a/libexec/ftpd/extern.h +++ b/libexec/ftpd/extern.h @@ -31,6 +31,7 @@ * SUCH DAMAGE. * * @(#)extern.h 8.2 (Berkeley) 4/4/94 + * $Id: extern.h,v 1.8 1997/02/22 14:21:26 peter Exp $ */ void blkfree __P((char **)); @@ -56,10 +57,19 @@ char *renamefrom __P((char *)); void reply __P((int, const char *, ...)); void retrieve __P((char *, char *)); void send_file_list __P((char *)); +#ifdef OLD_SETPROCTITLE void setproctitle __P((const char *, ...)); +#endif void statcmd __P((void)); void statfilecmd __P((char *)); void store __P((char *, char *, int)); void upper __P((char *)); void user __P((char *)); void yyerror __P((char *)); +int yyparse __P((void)); +#if defined(SKEY) && defined(_PWD_H_) /* XXX evil */ +char *skey_challenge __P((char *, struct passwd *, int)); +#endif +#if defined(INTERNAL_LS) +int ls_main __P((int, char **)); +#endif diff --git a/libexec/ftpd/ftpcmd.y b/libexec/ftpd/ftpcmd.y index 6ec3d25..659081b 100644 --- a/libexec/ftpd/ftpcmd.y +++ b/libexec/ftpd/ftpcmd.y @@ -31,6 +31,7 @@ * SUCH DAMAGE. * * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 + * $Id: ftpcmd.y,v 1.10 1997/02/22 14:21:27 peter Exp $ */ /* @@ -63,13 +64,15 @@ static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; #include <syslog.h> #include <time.h> #include <unistd.h> +#include <libutil.h> #include "extern.h" -extern struct sockaddr_in data_dest; +extern struct sockaddr_in data_dest, his_addr; extern int logged_in; extern struct passwd *pw; extern int guest; +extern int paranoid; extern int logging; extern int type; extern int form; @@ -77,7 +80,8 @@ extern int debug; extern int timeout; extern int maxtimeout; extern int pdata; -extern char hostname[], remotehost[]; +extern char *hostname; +extern char remotehost[]; extern char proctitle[]; extern int usedefault; extern int transflag; @@ -148,18 +152,32 @@ cmd pass($3); free($3); } - | PORT SP host_port CRLF - { - usedefault = 0; - if (pdata >= 0) { - (void) close(pdata); - pdata = -1; + | PORT check_login SP host_port CRLF + { + if ($2) { + if (paranoid && + ((ntohs(data_dest.sin_port) < + IPPORT_RESERVED) || + memcmp(&data_dest.sin_addr, + &his_addr.sin_addr, + sizeof(data_dest.sin_addr)))) { + usedefault = 1; + reply(500, + "Illegal PORT range rejected."); + } else { + usedefault = 0; + if (pdata >= 0) { + (void) close(pdata); + pdata = -1; + } + reply(200, "PORT command successful."); + } } - reply(200, "PORT command successful."); } - | PASV CRLF + | PASV check_login CRLF { - passive(); + if ($2) + passive(); } | TYPE SP type_code CRLF { @@ -291,16 +309,18 @@ cmd if ($4 != NULL) free($4); } - | RNTO SP pathname CRLF + | RNTO check_login SP pathname CRLF { - if (fromname) { - renamecmd(fromname, $3); - free(fromname); - fromname = (char *) 0; - } else { - reply(503, "Bad sequence of commands."); + if ($2) { + if (fromname) { + renamecmd(fromname, $4); + free(fromname); + fromname = (char *) 0; + } else { + reply(503, "Bad sequence of commands."); + } } - free($3); + free($4); } | ABOR CRLF { @@ -490,8 +510,9 @@ cmd struct tm *t; t = gmtime(&stbuf.st_mtime); reply(213, - "19%02d%02d%02d%02d%02d%02d", - t->tm_year, t->tm_mon+1, t->tm_mday, + "%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); } } @@ -552,11 +573,12 @@ host_port { char *a, *p; - a = (char *)&data_dest.sin_addr; - a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; + data_dest.sin_len = sizeof(struct sockaddr_in); + data_dest.sin_family = AF_INET; p = (char *)&data_dest.sin_port; p[0] = $9; p[1] = $11; - data_dest.sin_family = AF_INET; + a = (char *)&data_dest.sin_addr; + a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; } ; @@ -976,7 +998,7 @@ yylex() upper(cp); p = lookup(sitetab, cp); cbuf[cpos] = c; - if (p != 0) { + if (guest == 0 && p != 0) { if (p->implemented == 0) { state = CMD; nack(p->name); diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8 index eb93c38..b3fa6bd 100644 --- a/libexec/ftpd/ftpd.8 +++ b/libexec/ftpd/ftpd.8 @@ -29,9 +29,10 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)ftpd.8 8.3 (Berkeley) 6/1/94 +.\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94 +.\" $Id: ftpd.8,v 1.17 1997/04/27 08:29:21 davidn Exp $ .\" -.Dd June 1, 1994 +.Dd April 19, 1994 .Dt FTPD 8 .Os BSD 4.2 .Sh NAME @@ -41,8 +42,14 @@ Internet File Transfer Protocol server .Sh SYNOPSIS .Nm ftpd .Op Fl dl +.Op Fl D +.Op Fl R +.Op Fl S +.Op Fl U .Op Fl T Ar maxtimeout .Op Fl t Ar timeout +.Op Fl a Ar address +.Op Fl p Ar file .Sh DESCRIPTION .Nm Ftpd is the @@ -65,7 +72,47 @@ Each successful and failed session is logged using syslog with a facility of LOG_FTP. If this option is specified twice, the retrieve (get), store (put), append, delete, make directory, remove directory and rename operations and -their filename arguments are also logged. +their filename arguments are also logged. Note: LOG_FTP messages +are not displayed by +.Xr syslogd 8 +by default, and may have to be enabled in +.Xr syslogd 8 Ns 's +configuration file. +.It Fl D +With this option set, +.Nm ftpd +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 ftpd +from +.Xr inetd 8 +and is thus useful on busy servers to reduce load. +.It Fl R +With this option set, +.Nm ftpd +will revert to historical behavior with regard to security checks on +user operations and restrictions on PORT requests. +Currently, +.Nm ftpd +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 S +With this option set, +.Nm ftpd +logs all anonymous transfers to the file +.Pa /var/log/ftpd +when this file exists. +. +.It Fl U +In previous versions of +.Nm ftpd , +when a passive mode client requested a data connection to the server, +the server would use data ports in the range 1024..4999. Now, by default, +the server will use data ports in the range 40000..44999. Specifying this +option will revert to the old behavior. .It Fl T A client may also request a different timeout period; the maximum period allowed may be set to @@ -78,6 +125,18 @@ The default limit is 2 hours. The inactivity timeout period is set to .Ar timeout seconds (the default is 15 minutes). +.It Fl a +When +.Fl D +is specified, accept connections only on the specified +.Ar address . +.It Fl p +When +.Fl D +is specified, write the daemon's process ID to +.Ar file . +.It Fl A +Allow only anonymous ftp access .El .Pp The file @@ -94,7 +153,7 @@ prints it before issuing the .Dq ready message. If the file -.Pa /etc/motd +.Pa /etc/ftpmotd exists, .Nm prints it after a successful login. @@ -182,22 +241,55 @@ This allows users to utilize the metacharacters .Dq Li \&*?[]{}~ . .Pp .Nm Ftpd -authenticates users according to three rules. +authenticates users according to five rules. .Pp .Bl -enum -offset indent .It -The login name must be in the password data base, -.Pa /etc/passwd , +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 S/Key key, the response from a successful USER +command will include an S/Key challenge. The client may choose to respond +with a PASS command giving either a standard password or an S/Key +one-time password. The server will automatically determine which type of +password it has been given and attempt to authenticate accordingly. See +.Xr key 1 +for more information on S/Key authentication. S/Key is a Trademark of +Bellcore. .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 user's login directory by +.Xr chroot 2 +as for an +.Dq anonymous +or +.Dq ftp +account (see next item). +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 @@ -209,6 +301,9 @@ file (user 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, @@ -228,7 +323,6 @@ subtree be constructed with care, following these rules: Make the home directory owned by .Dq root and unwritable by anyone. -.ne 1i .It Pa ~ftp/bin Make this directory owned by .Dq root @@ -241,8 +335,8 @@ This program should be mode 111. Make this directory owned by .Dq root and unwritable by anyone (mode 555). -The files -.Xr passwd 5 +The files pwd.db (see +.Xr passwd 5 ) and .Xr group 5 must be present for the @@ -252,7 +346,7 @@ The password field in .Xr passwd is not used, and should not contain real passwords. The file -.Pa motd , +.Pa ftpmotd , if present, will be printed after a successful login. These files should be mode 444. .It Pa ~ftp/pub @@ -262,20 +356,91 @@ Guests can then place files which are to be accessible via the anonymous account in this directory. .El +.Pp +If the system has multiple IP addresses, +.Nm ftpd +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 +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 hypen '-' 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 +If compiled with the +.Em INTERNAL_LS +option, +.Nm ftpd +will have internal support for handling remote requests to list +files, and will not execute +.Pa /bin/ls +in either a chrooted or non-chrooted environment. +In this case, the +.Pa ~/bin/ls +executable need not be placed into the chrooted tree, nor need the +.Pa ~/bin +directory exist. +This support may be added by making ftpd with the +.Em FTP_INTERNAL_LS +variable set either in +.Pa /etc/make.conf +or in the shell's environment. .Sh FILES .Bl -tag -width /etc/ftpwelcome -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/ftpwelcome Welcome notice. -.It Pa /etc/motd +.It Pa /etc/ftpmotd Welcome notice after login. .It Pa /etc/nologin Displayed and access refused. +.It Pa /var/log/ftpd +Log file for anonymous transfers. .El .Sh SEE ALSO .Xr ftp 1 , +.Xr key 1 , .Xr getusershell 3 , +.Xr login.conf 5 , +.Xr inetd 8 , .Xr syslogd 8 .Sh BUGS The server must run as the super-user diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c index 875ec62..37407a3 100644 --- a/libexec/ftpd/ftpd.c +++ b/libexec/ftpd/ftpd.c @@ -29,17 +29,23 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id: ftpd.c,v 1.40 1997/05/21 23:24:41 danny Exp $ */ +#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 +#if 0 #ifndef lint -static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95"; +static char sccsid[] = "@(#)ftpd.c 8.4 (Berkeley) 4/16/94"; #endif /* not lint */ +#endif /* * FTP server. @@ -49,10 +55,12 @@ static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95"; #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/wait.h> +#include <sys/mman.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> +#include <netinet/tcp.h> #define FTP_NAMES #include <arpa/ftp.h> @@ -68,6 +76,7 @@ static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95"; #include <limits.h> #include <netdb.h> #include <pwd.h> +#include <grp.h> #include <setjmp.h> #include <signal.h> #include <stdio.h> @@ -76,6 +85,14 @@ static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95"; #include <syslog.h> #include <time.h> #include <unistd.h> +#include <libutil.h> +#ifdef LOGIN_CAP +#include <login_cap.h> +#endif + +#ifdef SKEY +#include <skey.h> +#endif #include "pathnames.h" #include "extern.h" @@ -86,17 +103,24 @@ static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95"; #include <varargs.h> #endif +#ifdef INTERNAL_LS +static char version[] = "Version 6.00LS"; +#undef main +#else static char version[] = "Version 6.00"; +#endif extern off_t restart_point; extern char cbuf[]; +struct sockaddr_in server_addr; struct sockaddr_in ctrl_addr; struct sockaddr_in data_source; struct sockaddr_in data_dest; struct sockaddr_in his_addr; struct sockaddr_in pasv_addr; +int daemon_mode; int data; jmp_buf errcatch, urgcatch; int logged_in; @@ -105,7 +129,13 @@ int debug; int timeout = 900; /* timeout after 15 minutes of inactivity */ int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ int logging; +int restricted_data_ports = 1; +int paranoid = 1; /* be extra careful about security */ +int anon_only = 0; /* Only anonymous ftp allowed */ int guest; +int dochroot; +int stats; +int statfd = -1; int type; int form; int stru; /* avoid C keyword */ @@ -121,8 +151,39 @@ off_t byte_count; #endif int defumask = CMASK; /* default umask value */ char tmpline[7]; -char hostname[MAXHOSTNAMELEN]; +char *hostname; +#ifdef VIRTUAL_HOSTING +char *ftpuser; + +static struct ftphost { + struct ftphost *next; + struct in_addr hostaddr; + char *hostname; + char *anonuser; + char *statfile; + char *welcome; + char *loginmsg; +} *thishost, *firsthost; + +#endif char remotehost[MAXHOSTNAMELEN]; +char *ident = NULL; + +static char ttyline[20]; +char *tty = ttyline; /* for klogin */ + +#ifdef KERBEROS +int klogin __P((struct passwd *, char *, char *, char *)); +#endif + +struct in_addr bind_address; +char *pid_file = NULL; + +#if defined(KERBEROS) +int notickets = 1; +int noticketsdontcomplain = 1; +char *krbtkfile_env = NULL; +#endif /* * Timeout intervals for retrying connections @@ -136,11 +197,18 @@ int swaitmax = SWAITMAX; int swaitint = SWAITINT; #ifdef SETPROCTITLE +#ifdef OLD_SETPROCTITLE char **Argv = NULL; /* pointer to argument vector */ char *LastArgv = NULL; /* end of argv */ +#endif /* OLD_SETPROCTITLE */ char proctitle[LINE_MAX]; /* initial part of title */ #endif /* SETPROCTITLE */ +#ifdef SKEY +int pwok = 0; +char addr_string[20]; /* XXX */ +#endif + #define LOGCMD(cmd, file) \ if (logging > 1) \ syslog(LOG_INFO,"%s %s%s", cmd, \ @@ -160,9 +228,13 @@ char proctitle[LINE_MAX]; /* initial part of title */ cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \ } +#ifdef VIRTUAL_HOSTING +static void inithosts __P((void)); +static void selecthost __P((struct in_addr *)); +#endif static void ack __P((char *)); static void myoob __P((int)); -static int checkuser __P((char *)); +static int checkuser __P((char *, char *)); static FILE *dataconn __P((char *, off_t, char *)); static void dolog __P((struct sockaddr_in *)); static char *curdir __P((void)); @@ -171,10 +243,12 @@ static FILE *getdatasock __P((char *)); static char *gunique __P((char *)); static void lostconn __P((int)); static int receive_data __P((FILE *, FILE *)); -static void send_data __P((FILE *, FILE *, off_t)); +static void send_data __P((FILE *, FILE *, off_t, off_t, int)); static struct passwd * sgetpwnam __P((char *)); static char *sgetsave __P((char *)); +static void reapchild __P((int)); +static void logxfer __P((char *, long, long)); static char * curdir() @@ -199,29 +273,9 @@ main(argc, argv, envp) char *cp, line[LINE_MAX]; FILE *fd; - /* - * LOG_NDELAY sets up the logging connection immediately, - * necessary for anonymous ftp's that chroot and can't do it later. - */ - openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); - addrlen = sizeof(his_addr); - if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { - syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); - exit(1); - } - addrlen = sizeof(ctrl_addr); - if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { - syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); - exit(1); - } -#ifdef IP_TOS - tos = IPTOS_LOWDELAY; - if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) - syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); -#endif - data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); - debug = 0; -#ifdef SETPROCTITLE + tzset(); /* in case no timezone database in ~ftp */ + +#ifdef OLD_SETPROCTITLE /* * Save start and extent of argv for setproctitle. */ @@ -229,22 +283,30 @@ main(argc, argv, envp) while (*envp) envp++; LastArgv = envp[-1] + strlen(envp[-1]); -#endif /* SETPROCTITLE */ +#endif /* OLD_SETPROCTITLE */ + - while ((ch = getopt(argc, argv, "dlt:T:u:v")) != EOF) { + bind_address.s_addr = htonl(INADDR_ANY); + while ((ch = getopt(argc, argv, "AdlDSURt:T:u:va:p:")) != -1) { switch (ch) { + case 'D': + daemon_mode++; + break; + case 'd': - debug = 1; + debug++; break; case 'l': logging++; /* > 1 == extra logging */ break; - case 't': - timeout = atoi(optarg); - if (maxtimeout < timeout) - maxtimeout = timeout; + case 'R': + paranoid = 0; + break; + + case 'S': + stats++; break; case 'T': @@ -253,6 +315,25 @@ main(argc, argv, envp) timeout = maxtimeout; break; + case 't': + timeout = atoi(optarg); + if (maxtimeout < timeout) + maxtimeout = timeout; + break; + + case 'U': + restricted_data_ports = 0; + break; + + case 'a': + if (!inet_aton(optarg, &bind_address)) + errx(1, "invalid address for -a"); + break; + + case 'p': + pid_file = optarg; + break; + case 'u': { long val = 0; @@ -264,6 +345,9 @@ main(argc, argv, envp) defumask = val; break; } + case 'A': + anon_only = 1; + break; case 'v': debug = 1; @@ -274,12 +358,133 @@ main(argc, argv, envp) break; } } + +#ifdef VIRTUAL_HOSTING + inithosts(); +#endif (void) freopen(_PATH_DEVNULL, "w", stderr); - (void) signal(SIGPIPE, lostconn); + + /* + * 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); + + if (daemon_mode) { + int ctl_sock, fd; + struct servent *sv; + + /* + * Detach from parent. + */ + if (daemon(1, 1) < 0) { + syslog(LOG_ERR, "failed to become a daemon"); + exit(1); + } + (void) signal(SIGCHLD, reapchild); + /* + * Get port number for ftp/tcp. + */ + sv = getservbyname("ftp", "tcp"); + if (sv == NULL) { + syslog(LOG_ERR, "getservbyname for ftp failed"); + exit(1); + } + /* + * Open a socket, bind it to the FTP port, and start + * listening. + */ + ctl_sock = socket(AF_INET, SOCK_STREAM, 0); + if (ctl_sock < 0) { + syslog(LOG_ERR, "control socket: %m"); + exit(1); + } + if (setsockopt(ctl_sock, SOL_SOCKET, SO_REUSEADDR, + (char *)&on, sizeof(on)) < 0) + syslog(LOG_ERR, "control setsockopt: %m");; + server_addr.sin_family = AF_INET; + server_addr.sin_addr = bind_address; + server_addr.sin_port = sv->s_port; + if (bind(ctl_sock, (struct sockaddr *)&server_addr, sizeof(server_addr))) { + syslog(LOG_ERR, "control bind: %m"); + exit(1); + } + if (listen(ctl_sock, 32) < 0) { + syslog(LOG_ERR, "control listen: %m"); + exit(1); + } + /* + * Atomically write process ID + */ + if (pid_file) + { + int fd; + char buf[20]; + + fd = open(pid_file, O_CREAT | O_WRONLY | O_TRUNC + | O_NONBLOCK | O_EXLOCK, 0644); + if (fd < 0) + if (errno == EAGAIN) + errx(1, "%s: file locked", pid_file); + else + err(1, "%s", pid_file); + snprintf(buf, sizeof(buf), + "%lu\n", (unsigned long) getpid()); + if (write(fd, buf, strlen(buf)) < 0) + err(1, "%s: write", pid_file); + /* Leave the pid file open and locked */ + } + /* + * Loop forever accepting connection requests and forking off + * children to handle them. + */ + while (1) { + addrlen = sizeof(his_addr); + fd = accept(ctl_sock, (struct sockaddr *)&his_addr, &addrlen); + if (fork() == 0) { + /* child */ + (void) dup2(fd, 0); + (void) dup2(fd, 1); + close(ctl_sock); + break; + } + 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); + } + } + (void) signal(SIGCHLD, SIG_IGN); + (void) signal(SIGPIPE, lostconn); if ((int)signal(SIGURG, myoob) < 0) syslog(LOG_ERR, "signal: %m"); +#ifdef SKEY + strncpy(addr_string, inet_ntoa(his_addr.sin_addr), sizeof(addr_string)); +#endif + addrlen = sizeof(ctrl_addr); + if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { + syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); + exit(1); + } +#ifdef VIRTUAL_HOSTING + /* select our identity from virtual host table */ + selecthost(&ctrl_addr.sin_addr); +#endif +#ifdef IP_TOS + tos = IPTOS_LOWDELAY; + if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) + syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); +#endif + data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); + + /* set this here so klogin can use it... */ + (void)sprintf(ttyline, "ftp%d", getpid()); + /* Try to handle urgent data inline */ #ifdef SO_OOBINLINE if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) @@ -313,7 +518,11 @@ main(argc, argv, envp) reply(530, "System not available."); exit(0); } +#ifdef VIRTUAL_HOSTING + if ((fd = fopen(thishost->welcome, "r")) != NULL) { +#else if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { +#endif while (fgets(line, sizeof(line), fd) != NULL) { if ((cp = strchr(line, '\n')) != NULL) *cp = '\0'; @@ -323,7 +532,11 @@ main(argc, argv, envp) (void) fclose(fd); /* reply(220,) must follow */ } - (void) gethostname(hostname, sizeof(hostname)); +#ifndef VIRTUAL_HOSTING + if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) + fatal("Ran out of memory."); + (void) gethostname(hostname, MAXHOSTNAMELEN); +#endif reply(220, "%s FTP server (%s) ready.", hostname, version); (void) setjmp(errcatch); for (;;) @@ -341,7 +554,146 @@ lostconn(signo) dologout(-1); } -static char ttyline[20]; +#ifdef VIRTUAL_HOSTING +/* + * read in virtual host tables (if they exist) + */ + +static void +inithosts() +{ + FILE *fp; + char *cp; + struct hostent *hp; + struct ftphost *hrp, *lhrp; + char line[1024]; + + /* + * Fill in the default host information + */ + if (gethostname(line, sizeof(line)) < 0) + line[0] = '\0'; + if ((hrp = malloc(sizeof(struct ftphost))) == NULL || + (hrp->hostname = strdup(line)) == NULL) + fatal("Ran out of memory."); + memset(&hrp->hostaddr, 0, sizeof hrp->hostaddr); + if ((hp = gethostbyname(hrp->hostname)) != NULL) + (void) memcpy(&hrp->hostaddr, + hp->h_addr_list[0], + sizeof(hrp->hostaddr)); + 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) { + while (fgets(line, sizeof(line), fp) != NULL) { + int i; + + if ((cp = strchr(line, '\n')) == NULL) { + /* ignore long lines */ + while (fgets(line, sizeof(line), fp) != NULL && + strchr(line, '\n') == NULL) + ; + continue; + } + *cp = '\0'; + cp = strtok(line, " \t"); + /* skip comments and empty lines */ + if (cp == NULL || line[0] == '#') + continue; + /* first, try a standard gethostbyname() */ + if ((hp = gethostbyname(cp)) == NULL) + continue; + for (hrp = firsthost; hrp != NULL; hrp = hrp->next) { + if (memcmp(&hrp->hostaddr, + hp->h_addr_list[0], + sizeof(hrp->hostaddr)) == 0) + break; + } + if (hrp == NULL) { + if ((hrp = malloc(sizeof(struct ftphost))) == NULL) + continue; + /* defaults */ + hrp->statfile = _PATH_FTPDSTATFILE; + hrp->welcome = _PATH_FTPWELCOME; + hrp->loginmsg = _PATH_FTPLOGINMESG; + hrp->anonuser = "ftp"; + hrp->next = NULL; + lhrp->next = hrp; + lhrp = hrp; + } + (void) memcpy(&hrp->hostaddr, + hp->h_addr_list[0], + sizeof(hrp->hostaddr)); + /* + * determine hostname to use. + * force defined name if it is a valid alias + * otherwise fallback to primary hostname + */ + if ((hp = gethostbyaddr((char*)&hrp->hostaddr, + sizeof(hrp->hostaddr), + AF_INET)) != NULL) { + if (strcmp(cp, hp->h_name) != 0) { + if (hp->h_aliases == NULL) + cp = hp->h_name; + else { + i = 0; + while (hp->h_aliases[i] && + strcmp(cp, hp->h_aliases[i]) != 0) + ++i; + if (hp->h_aliases[i] == NULL) + cp = hp->h_name; + } + } + } + hrp->hostname = strdup(cp); + /* ok, now we now peel off the rest */ + i = 0; + while (i < 4 && (cp = strtok(NULL, " \t")) != NULL) { + if (*cp != '-' && (cp = strdup(cp)) != NULL) { + switch (i) { + case 0: /* anon user permissions */ + hrp->anonuser = cp; + break; + case 1: /* statistics file */ + hrp->statfile = cp; + break; + case 2: /* welcome message */ + hrp->welcome = cp; + break; + case 3: /* login message */ + hrp->loginmsg = cp; + break; + } + } + ++i; + } + } + (void) fclose(fp); + } +} + +static void +selecthost(a) + struct in_addr *a; +{ + struct ftphost *hrp; + + hrp = thishost = firsthost; /* default */ + while (hrp != NULL) { + if (memcmp(a, &hrp->hostaddr, sizeof(hrp->hostaddr)) == 0) { + thishost = hrp; + break; + } + hrp = hrp->next; + } + /* setup static variables as appropriate */ + hostname = thishost->hostname; + ftpuser = thishost->anonuser; +} +#endif /* * Helper function for sgetpwnam(). @@ -416,19 +768,27 @@ user(name) 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; if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { - if (checkuser("ftp") || checkuser("anonymous")) + if (checkuser(_PATH_FTPUSERS, "ftp") || + checkuser(_PATH_FTPUSERS, "anonymous")) reply(530, "User %s access denied.", name); +#ifdef VIRTUAL_HOSTING + else if ((pw = sgetpwnam(thishost->anonuser)) != NULL) { +#else else if ((pw = sgetpwnam("ftp")) != NULL) { +#endif guest = 1; askpasswd = 1; reply(331, - "Guest login ok, type your name as password."); + "Guest login ok, send your email address as password."); } else reply(530, "User %s unknown.", name); if (!askpasswd && logging) @@ -436,7 +796,12 @@ user(name) "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); return; } - if (pw = sgetpwnam(name)) { + 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; while ((cp = getusershell()) != NULL) @@ -444,7 +809,7 @@ user(name) break; endusershell(); - if (cp == NULL || checkuser(name)) { + if (cp == NULL || checkuser(_PATH_FTPUSERS, name)) { reply(530, "User %s access denied.", name); if (logging) syslog(LOG_NOTICE, @@ -456,7 +821,12 @@ user(name) } if (logging) strncpy(curname, name, sizeof(curname)-1); +#ifdef SKEY + pwok = skeyaccess(name, NULL, remotehost, addr_string); + reply(331, "%s", skey_challenge(name, pw, pwok)); +#else reply(331, "Password required for %s.", name); +#endif askpasswd = 1; /* * Delay before reading passwd after first failed @@ -467,26 +837,42 @@ user(name) } /* - * Check if a user is in the file _PATH_FTPUSERS + * Check if a user is in the file "fname" */ static int -checkuser(name) +checkuser(fname, name) + char *fname; char *name; { FILE *fd; int found = 0; char *p, line[BUFSIZ]; - if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) { - while (fgets(line, sizeof(line), fd) != NULL) + if ((fd = fopen(fname, "r")) != NULL) { + while (!found && fgets(line, sizeof(line), fd) != NULL) if ((p = strchr(line, '\n')) != NULL) { *p = '\0'; if (line[0] == '#') continue; - if (strcmp(line, name) == 0) { - found = 1; - break; + /* + * if first chr is '@', check group membership + */ + if (line[0] == '@') { + int i = 0; + struct group *grp; + + if ((grp = getgrnam(line+1)) == NULL) + continue; + while (!found && grp->gr_mem[i]) + found = strcmp(name, + grp->gr_mem[i++]) + == 0; } + /* + * Otherwise, just check for username match + */ + else + found = strcmp(line, name) == 0; } (void) fclose(fd); } @@ -505,16 +891,25 @@ end_login() if (logged_in) logwtmp(ttyline, "", ""); pw = NULL; +#ifdef LOGIN_CAP + setusercontext(NULL, getpwuid(0), (uid_t)0, + LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK); +#endif logged_in = 0; guest = 0; + dochroot = 0; } void pass(passwd) char *passwd; { - char *salt, *xpasswd; + int rval; FILE *fd; +#ifdef LOGIN_CAP + login_cap_t *lc = NULL; +#endif + static char homedir[MAXPATHLEN]; if (logged_in || askpasswd == 0) { reply(503, "Login with USER first."); @@ -522,14 +917,33 @@ pass(passwd) } askpasswd = 0; if (!guest) { /* "ftp" is only account allowed no password */ - if (pw == NULL) - salt = "xx"; - else - salt = pw->pw_passwd; - xpasswd = crypt(passwd, salt); + if (pw == NULL) { + rval = 1; /* failure below */ + goto skip; + } +#if defined(KERBEROS) + rval = klogin(pw, "", hostname, passwd); + if (rval == 0) + goto skip; +#endif +#ifdef SKEY + rval = strcmp(skey_crypt(passwd, pw->pw_passwd, pw, pwok), + pw->pw_passwd); + pwok = 0; +#else + rval = strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd); +#endif /* The strcmp does not catch null passwords! */ - if (pw == NULL || *pw->pw_passwd == '\0' || - strcmp(xpasswd, pw->pw_passwd)) { + if (*pw->pw_passwd == '\0' || + (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 Kerberos or local authentication + * succeeded. + */ + if (rval) { reply(530, "Login incorrect."); if (logging) syslog(LOG_NOTICE, @@ -550,13 +964,52 @@ pass(passwd) 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[MAXHOSTNAMELEN]; + + strncpy(remote_ip, inet_ntoa(his_addr.sin_addr), + sizeof(remote_ip) - 1); + 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.\n"); + pw = NULL; + return; + } + if (!auth_timeok(lc, time(NULL))) { + reply(530, "Login not available right now.\n"); + pw = NULL; + return; + } + } + setusercontext(lc, pw, (uid_t)0, + LOGIN_SETGROUP|LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK); +#else (void) initgroups(pw->pw_name, pw->pw_gid); +#endif /* open wtmp before chroot */ - (void)sprintf(ttyline, "ftp%d", getpid()); logwtmp(ttyline, pw->pw_name, remotehost); logged_in = 1; + if (guest && stats && statfd < 0) +#ifdef VIRTUAL_HOSTING + if ((statfd = open(thishost->statfile, O_WRONLY|O_APPEND)) < 0) +#else + if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0) +#endif + stats = 0; + + dochroot = +#ifdef LOGIN_CAP /* Allow login.conf configuration as well */ + login_getcapbool(lc, "ftp-chroot", 0) || +#endif + checkuser(_PATH_FTPCHROOT, pw->pw_name); if (guest) { /* * We MUST do a chdir() after the chroot. Otherwise @@ -567,6 +1020,11 @@ pass(passwd) reply(550, "Can't set guest privileges."); goto bad; } + } else if (dochroot) { + if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { + reply(550, "Can't change root."); + goto bad; + } } else if (chdir(pw->pw_dir) < 0) { if (chdir("/") < 0) { reply(530, "User %s: can't change directory to %s.", @@ -579,11 +1037,22 @@ pass(passwd) reply(550, "Can't set uid."); goto bad; } + + /* + * Set home directory so that use of ~ (tilde) works correctly. + */ + if (getcwd(homedir, MAXPATHLEN) != NULL) + setenv("HOME", homedir, 1); + /* * Display a login message, if it exists. * N.B. reply(230,) must follow the message. */ +#ifdef VIRTUAL_HOSTING + if ((fd = fopen(thishost->loginmsg, "r")) != NULL) { +#else if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { +#endif char *cp, line[LINE_MAX]; while (fgets(line, sizeof(line), fd) != NULL) { @@ -595,32 +1064,56 @@ pass(passwd) (void) fclose(fd); } if (guest) { + if (ident != NULL) + free(ident); + ident = strdup(passwd); + if (ident == NULL) + fatal("Ran out of memory."); + reply(230, "Guest login ok, access restrictions apply."); #ifdef SETPROCTITLE - snprintf(proctitle, sizeof(proctitle), - "%s: anonymous/%.*s", remotehost, - sizeof(proctitle) - sizeof(remotehost) - - sizeof(": anonymous/"), passwd); - setproctitle(proctitle); +#ifdef VIRTUAL_HOSTING + if (thishost != firsthost) + snprintf(proctitle, sizeof(proctitle), + "%s: anonymous(%s)/%.*s", remotehost, hostname, + sizeof(proctitle) - sizeof(remotehost) - + sizeof(": anonymous/"), passwd); + else +#endif + snprintf(proctitle, sizeof(proctitle), + "%s: anonymous/%.*s", remotehost, + sizeof(proctitle) - sizeof(remotehost) - + sizeof(": anonymous/"), passwd); + setproctitle("%s", proctitle); #endif /* SETPROCTITLE */ if (logging) syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", remotehost, passwd); } else { + 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: %s", remotehost, pw->pw_name); - setproctitle(proctitle); + "%s: %s", remotehost, pw->pw_name); + setproctitle("%s", proctitle); #endif /* SETPROCTITLE */ if (logging) syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", remotehost, pw->pw_name); } - (void) umask(defumask); +#ifdef LOGIN_CAP + login_close(lc); +#endif return; bad: /* Forget all about it... */ +#ifdef LOGIN_CAP + login_close(lc); +#endif end_login(); } @@ -631,6 +1124,7 @@ retrieve(cmd, name) FILE *fin, *dout; struct stat st; int (*closefunc) __P((FILE *)); + long start; if (cmd == 0) { fin = fopen(name, "r"), closefunc = fclose; @@ -680,7 +1174,11 @@ retrieve(cmd, name) dout = dataconn(name, st.st_size, "w"); if (dout == NULL) goto done; - send_data(fin, dout, st.st_blksize); + 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) + logxfer(name, st.st_size, start); (void) fclose(dout); data = -1; pdata = -1; @@ -699,7 +1197,7 @@ store(name, mode, unique) struct stat st; int (*closefunc) __P((FILE *)); - if (unique && stat(name, &st) == 0 && + if ((unique || guest) && stat(name, &st) == 0 && (name = gunique(name)) == NULL) { LOGCMD(*mode == 'w' ? "put" : "append", name); return; @@ -778,6 +1276,7 @@ getdatasock(mode) (char *) &on, sizeof(on)) < 0) goto bad; /* anchor socket to avoid multi-homing problems */ + data_source.sin_len = sizeof(struct sockaddr_in); data_source.sin_family = AF_INET; data_source.sin_addr = ctrl_addr.sin_addr; for (tries = 1; ; tries++) { @@ -794,6 +1293,23 @@ getdatasock(mode) if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); #endif +#ifdef TCP_NOPUSH + /* + * Turn off push flag to keep sender TCP from sending short packets + * at the boundaries of each write(). Should probably do a SO_SNDBUF + * to set the send buffer size as well, but that may not be desirable + * in heavy-load situations. + */ + on = 1; + if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *)&on, sizeof on) < 0) + syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m"); +#endif +#ifdef SO_SNDBUF + on = 65536; + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&on, sizeof on) < 0) + syslog(LOG_WARNING, "setsockopt (SO_SNDBUF): %m"); +#endif + return (fdopen(s, mode)); bad: /* Return the real value of errno (close may change it) */ @@ -823,9 +1339,17 @@ dataconn(name, size, mode) if (pdata >= 0) { struct sockaddr_in from; int s, fromlen = sizeof(from); + struct timeval timeout; + fd_set set; + + FD_ZERO(&set); + FD_SET(pdata, &set); - s = accept(pdata, (struct sockaddr *)&from, &fromlen); - if (s < 0) { + timeout.tv_usec = 0; + timeout.tv_sec = 120; + + if (select(pdata+1, &set, (fd_set *) 0, (fd_set *) 0, &timeout) == 0 || + (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) { reply(425, "Can't open data connection."); (void) close(pdata); pdata = -1; @@ -834,7 +1358,7 @@ dataconn(name, size, mode) (void) close(pdata); pdata = s; #ifdef IP_TOS - tos = IPTOS_LOWDELAY; + tos = IPTOS_THROUGHPUT; (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)); #endif @@ -878,17 +1402,20 @@ dataconn(name, size, mode) /* * Tranfer the contents of "instr" to "outstr" peer using the appropriate - * encapsulation of the data subject * to Mode, Structure, and Type. + * encapsulation of the data subject to Mode, Structure, and Type. * * NB: Form isn't handled. */ static void -send_data(instr, outstr, blksize) +send_data(instr, outstr, blksize, filesize, isreg) FILE *instr, *outstr; off_t blksize; + off_t filesize; + int isreg; { int c, cnt, filefd, netfd; - char *buf; + char *buf, *bp; + size_t len; transflag++; if (setjmp(urgcatch)) { @@ -918,13 +1445,45 @@ send_data(instr, outstr, blksize) case TYPE_I: case TYPE_L: + /* + * isreg is only set if we are not doing restart and we + * are sending a regular file + */ + netfd = fileno(outstr); + filefd = fileno(instr); + + if (isreg && filesize < (off_t)16 * 1024 * 1024) { + buf = mmap(0, filesize, PROT_READ, MAP_SHARED, filefd, + (off_t)0); + if (buf == MAP_FAILED) { + syslog(LOG_WARNING, "mmap(%lu): %m", + (unsigned long)filesize); + goto oldway; + } + bp = buf; + len = filesize; + do { + cnt = write(netfd, bp, len); + len -= cnt; + bp += cnt; + if (cnt > 0) byte_count += cnt; + } while(cnt > 0 && len > 0); + + transflag = 0; + munmap(buf, (size_t)filesize); + if (cnt < 0) + goto data_err; + reply(226, "Transfer complete."); + return; + } + +oldway: if ((buf = malloc((u_int)blksize)) == NULL) { transflag = 0; perror_reply(451, "Local resource failure: malloc"); return; } - netfd = fileno(outstr); - filefd = fileno(instr); + while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && write(netfd, buf, cnt) == cnt) byte_count += cnt; @@ -964,7 +1523,7 @@ receive_data(instr, outstr) FILE *instr, *outstr; { int c; - int cnt, bare_lfs = 0; + int cnt, bare_lfs; char buf[BUFSIZ]; transflag++; @@ -972,6 +1531,9 @@ receive_data(instr, outstr) transflag = 0; return (-1); } + + bare_lfs = 0; + switch (type) { case TYPE_I: @@ -1046,7 +1608,7 @@ statfilecmd(filename) int c; char line[LINE_MAX]; - (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename); + (void)snprintf(line, sizeof(line), _PATH_LS " -lgA %s", filename); fin = ftpd_popen(line, "r"); lreply(211, "status of %s:", filename); while ((c = getc(fin)) != EOF) { @@ -1209,7 +1771,7 @@ yyerror(s) { char *cp; - if (cp = strchr(cbuf,'\n')) + if ((cp = strchr(cbuf,'\n'))) *cp = '\0'; reply(500, "'%s': command not understood.", cbuf); } @@ -1304,8 +1866,15 @@ void renamecmd(from, to) char *from, *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 @@ -1325,12 +1894,26 @@ dolog(sin) (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), sizeof(remotehost)); #ifdef SETPROCTITLE - snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); - setproctitle(proctitle); +#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) - syslog(LOG_INFO, "connection from %s", remotehost); + if (logging) { +#ifdef VIRTUAL_HOSTING + if (thishost != firsthost) + syslog(LOG_INFO, "connection from %s (to %s)", + remotehost, hostname); + else +#endif + syslog(LOG_INFO, "connection from %s", remotehost); + } } /* @@ -1341,10 +1924,19 @@ void dologout(status) int status; { + /* + * Prevent reception of SIGURG from resulting in a resumption + * back to the main program loop. + */ + transflag = 0; if (logged_in) { (void) seteuid((uid_t)0); logwtmp(ttyline, "", ""); +#if defined(KERBEROS) + if (!notickets && krbtkfile_env) + unlink(krbtkfile_env); +#endif } /* beware of flushing buffers after a SIGPIPE */ _exit(status); @@ -1392,19 +1984,36 @@ passive() int len; char *p, *a; + if (pdata >= 0) /* close old port if one set */ + close(pdata); + pdata = socket(AF_INET, SOCK_STREAM, 0); if (pdata < 0) { perror_reply(425, "Can't open passive connection"); return; } + + (void) seteuid((uid_t)0); + +#ifdef IP_PORTRANGE + { + int on = restricted_data_ports ? IP_PORTRANGE_HIGH + : IP_PORTRANGE_DEFAULT; + + if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, + (char *)&on, sizeof(on)) < 0) + goto pasv_error; + } +#endif + pasv_addr = ctrl_addr; pasv_addr.sin_port = 0; - (void) seteuid((uid_t)0); - if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) { - (void) seteuid((uid_t)pw->pw_uid); + if (bind(pdata, (struct sockaddr *)&pasv_addr, + sizeof(pasv_addr)) < 0) goto pasv_error; - } + (void) seteuid((uid_t)pw->pw_uid); + len = sizeof(pasv_addr); if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) goto pasv_error; @@ -1420,6 +2029,7 @@ passive() return; pasv_error: + (void) seteuid((uid_t)pw->pw_uid); (void) close(pdata); pdata = -1; perror_reply(425, "Can't open passive connection"); @@ -1449,7 +2059,7 @@ gunique(local) } if (cp) *cp = '/'; - (void) strcpy(new, local); + (void) snprintf(new, sizeof(new), "%s", local); cp = new + strlen(new); *cp++ = '.'; for (count = 1; count < 100; count++) { @@ -1515,7 +2125,7 @@ send_file_list(whichf) transflag = 0; goto out; } - while (dirname = *dirlist++) { + while ((dirname = *dirlist++)) { if (stat(dirname, &st) < 0) { /* * If user typed "ls -l", etc, and the client @@ -1523,7 +2133,7 @@ send_file_list(whichf) */ if (dirname[0] == '-' && *dirlist == NULL && transflag == 0) { - retrieve("/bin/ls %s", dirname); + retrieve(_PATH_LS " %s", dirname); goto out; } perror_reply(550, whichf); @@ -1608,7 +2218,14 @@ out: } } -#ifdef SETPROCTITLE +void +reapchild(signo) + int signo; +{ + while (wait3(NULL, WNOHANG, NULL) > 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 @@ -1651,4 +2268,23 @@ setproctitle(fmt, va_alist) while (p < LastArgv) *p++ = ' '; } -#endif /* SETPROCTITLE */ +#endif /* OLD_SETPROCTITLE */ + +static void +logxfer(name, size, start) + char *name; + long size; + long start; +{ + char buf[1024]; + char path[MAXPATHLEN + 1]; + long now; + + if (statfd >= 0 && getwd(path) != NULL) { + time(&now); + snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s/%s!%ld!%ld\n", + ctime(&now)+4, ident, remotehost, + path, name, size, now - start + (now == start)); + write(statfd, buf, strlen(buf)); + } +} diff --git a/libexec/ftpd/logwtmp.c b/libexec/ftpd/logwtmp.c index d40840c..9562795 100644 --- a/libexec/ftpd/logwtmp.c +++ b/libexec/ftpd/logwtmp.c @@ -30,6 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * + * $Id$ */ #ifndef lint @@ -37,10 +38,13 @@ static char sccsid[] = "@(#)logwtmp.c 8.1 (Berkeley) 6/4/93"; #endif /* not lint */ #include <sys/types.h> -#include <sys/time.h> #include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> #include <fcntl.h> +#include <time.h> +#include <netdb.h> #include <utmp.h> #include <unistd.h> #include <stdio.h> @@ -61,6 +65,18 @@ logwtmp(line, name, host) struct utmp ut; struct stat buf; + if (strlen(host) > UT_HOSTSIZE) { + struct hostent *hp = gethostbyname(host); + + if (hp != NULL) { + struct in_addr in; + + memmove(&in, hp->h_addr, sizeof(in)); + host = inet_ntoa(in); + } else + host = "invalid hostname"; + } + if (fd < 0 && (fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) < 0) return; if (fstat(fd, &buf) == 0) { diff --git a/libexec/ftpd/pathnames.h b/libexec/ftpd/pathnames.h index 2a50063..d2f8b73 100644 --- a/libexec/ftpd/pathnames.h +++ b/libexec/ftpd/pathnames.h @@ -31,10 +31,14 @@ * SUCH DAMAGE. * * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + * $Id: pathnames.h,v 1.9 1997/04/26 12:12:10 davidn Exp $ */ #include <paths.h> -#define _PATH_FTPUSERS "/etc/ftpusers" +#define _PATH_FTPCHROOT "/etc/ftpchroot" #define _PATH_FTPWELCOME "/etc/ftpwelcome" -#define _PATH_FTPLOGINMESG "/etc/motd" +#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 index b26732e..ac0b76a 100644 --- a/libexec/ftpd/popen.c +++ b/libexec/ftpd/popen.c @@ -33,11 +33,14 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * + * $Id: popen.c,v 1.7 1997/02/22 14:21:31 peter Exp $ */ +#if 0 #ifndef lint static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 4/6/94"; #endif /* not lint */ +#endif #include <sys/types.h> #include <sys/wait.h> @@ -51,6 +54,12 @@ static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 4/6/94"; #include <unistd.h> #include "extern.h" +#ifdef INTERNAL_LS +#include "pathnames.h" +#endif + +#define MAXUSRARGS 100 +#define MAXGLOBARGS 1000 /* * Special version of popen which avoids call to shell. This ensures noone @@ -67,9 +76,9 @@ ftpd_popen(program, type) char *cp; FILE *iop; int argc, gargc, pdes[2], pid; - char **pop, *argv[100], *gargv[1000]; + char **pop, *argv[MAXUSRARGS], *gargv[MAXGLOBARGS]; - if (*type != 'r' && *type != 'w' || type[1]) + if (((*type != 'r') && (*type != 'w')) || type[1]) return (NULL); if (!pids) { @@ -83,13 +92,13 @@ ftpd_popen(program, type) return (NULL); /* break up string into pieces */ - for (argc = 0, cp = program;; cp = NULL) + for (argc = 0, cp = program; argc < MAXUSRARGS; cp = NULL) if (!(argv[argc++] = strtok(cp, " \t\n"))) break; /* glob each piece */ gargv[0] = argv[0]; - for (gargc = argc = 1; argv[argc]; argc++) { + for (gargc = argc = 1; argv[argc] && gargc < (MAXGLOBARGS-1); argc++) { glob_t gl; int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; @@ -97,14 +106,21 @@ ftpd_popen(program, type) if (glob(argv[argc], flags, NULL, &gl)) gargv[gargc++] = strdup(argv[argc]); else - for (pop = gl.gl_pathv; *pop; pop++) + for (pop = gl.gl_pathv; *pop && gargc < (MAXGLOBARGS-1); + pop++) gargv[gargc++] = strdup(*pop); globfree(&gl); } gargv[gargc] = NULL; iop = NULL; - switch(pid = vfork()) { +#ifdef INTERNAL_LS + fflush(NULL); + pid = (strcmp(gargv[0], _PATH_LS) == 0) ? fork() : vfork(); +#else + pid = vfork(); +#endif + switch(pid) { case -1: /* error */ (void)close(pdes[0]); (void)close(pdes[1]); @@ -125,6 +141,14 @@ ftpd_popen(program, type) } (void)close(pdes[1]); } +#ifdef INTERNAL_LS + if (strcmp(gargv[0], _PATH_LS) == 0) { + extern int optreset; + /* Reset getopt for ls_main() */ + optreset = optind = optopt = 1; + exit(ls_main(gargc, gargv)); + } +#endif execv(gargv[0], gargv); _exit(1); } diff --git a/libexec/ftpd/skey-stuff.c b/libexec/ftpd/skey-stuff.c new file mode 100644 index 0000000..b6aba40 --- /dev/null +++ b/libexec/ftpd/skey-stuff.c @@ -0,0 +1,29 @@ +/* Author: Wietse Venema, Eindhoven University of Technology. + * + * $Id$ + */ + +#include <stdio.h> +#include <string.h> +#include <pwd.h> + +#include <skey.h> + +/* skey_challenge - additional password prompt stuff */ + +char *skey_challenge(name, pwd, pwok) +char *name; +struct passwd *pwd; +int pwok; +{ + static char buf[128]; + struct skey skey; + + /* Display s/key challenge where appropriate. */ + + if (pwd == NULL || skeychallenge(&skey, pwd->pw_name, buf)) + sprintf(buf, "Password required for %s.", name); + else if (!pwok) + strcat(buf, " (s/key required)"); + return (buf); +} diff --git a/libexec/getNAME/Makefile b/libexec/getNAME/Makefile index a78bc28..6600d23 100644 --- a/libexec/getNAME/Makefile +++ b/libexec/getNAME/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 8.1 (Berkeley) 6/4/93 +# $Id$ PROG= getNAME -NOMAN= noman .include <bsd.prog.mk> diff --git a/libexec/getNAME/getNAME.1 b/libexec/getNAME/getNAME.1 new file mode 100644 index 0000000..c58bbe2 --- /dev/null +++ b/libexec/getNAME/getNAME.1 @@ -0,0 +1,65 @@ +.\" Copyright (c) July 1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must 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. +.\" +.\" $Id$ + +.Dd July 8, 1996 +.Dt getNAME 1 +.Os FreeBSD 2.2 +.Sh NAME +.Nm getNAME +.Nd get name sections from manual pages +.Sh SYNOPSIS +.Nm /usr/libexec/getNAME +.Op Fl itw +.Ar files ... +.Sh DESCRIPTION +The +.Nm +utility +get name sections from manual pages. Without options +.Nm +building +.Xr apropos 1 +database entries. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl i +for building intro entries. +.It Fl t +for building toc. +.It Fl w +print type of manual page (OLD, NEW, UNKNOWN). +.El +.\" .Sh BUGS +.Sh SEE ALSO +.Xr apropos 1 , +.Xr makewhatis 1 , +.Xr man 1 +.Sh HISTORY +The manual page for +.Nm +command appeared in +.Fx 2.2 . diff --git a/libexec/getNAME/getNAME.c b/libexec/getNAME/getNAME.c index 2ab64b4..2961ecf 100644 --- a/libexec/getNAME/getNAME.c +++ b/libexec/getNAME/getNAME.c @@ -29,6 +29,9 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id: getNAME.c,v 1.4 1997/02/22 14:21:35 peter Exp $ + * */ #ifndef lint @@ -70,7 +73,7 @@ main(argc, argv) extern int optind; int ch; - while ((ch = getopt(argc, argv, "itw")) != EOF) + while ((ch = getopt(argc, argv, "itw")) != -1) switch(ch) { case 'i': intro = 1; @@ -81,7 +84,6 @@ main(argc, argv) case 'w': typeflag = 1; break; - case '?': default: usage(); } @@ -334,6 +336,6 @@ again: void usage() { - (void)fprintf(stderr, "usage: getNAME [-it] file ...\n"); + (void)fprintf(stderr, "usage: getNAME [-itw] file ...\n"); exit(1); } diff --git a/libexec/getty/Makefile b/libexec/getty/Makefile new file mode 100644 index 0000000..f51fe24 --- /dev/null +++ b/libexec/getty/Makefile @@ -0,0 +1,14 @@ +# from: @(#)Makefile 8.1 (Berkeley) 6/4/93 +# $Id$ + +PROG= getty +SRCS= main.c init.c subr.c chat.c +DPADD= ${LIBUTIL} +LDADD= -lutil +MAN5= gettytab.5 ttys.5 +MAN8= getty.8 +# for the paranoid: +#CFLAGS+= -Wall -Wstrict-prototypes -Wno-unused -Wwrite-strings + +.include <bsd.prog.mk> + diff --git a/libexec/getty/chat.c b/libexec/getty/chat.c new file mode 100644 index 0000000..2474195 --- /dev/null +++ b/libexec/getty/chat.c @@ -0,0 +1,515 @@ +/*- + * 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. + * + * $Id$ + */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/resource.h> +#include <sys/ttydefaults.h> +#include <sys/utsname.h> +#include <errno.h> +#include <signal.h> +#include <fcntl.h> +#include <time.h> +#include <ctype.h> +#include <fcntl.h> +#include <libutil.h> +#include <locale.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 <sys/socket.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 __P((int)); +static int chat_unalarm __P((void)); +static int getdigit __P((unsigned char **, int, int)); +static char **read_chat __P((char **)); +static char *cleanchr __P((char **, unsigned char)); +static char *cleanstr __P((const unsigned char *, int)); +static const char *result __P((int)); +static int chat_expect __P((const char *)); +static int chat_send __P((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(signo) + int signo; +{ + 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() +{ + int off = 0; + return ioctl(STDIN_FILENO, FIONBIO, &off); +} + + +/* + * convert a string of a given base (octal/hex) to binary + */ + +static int +getdigit(ptr, base, max) + unsigned char **ptr; + int base, 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(chatstr) + 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) + { + int val; + + 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(buf, ch) + 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(s, l) + 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 an pseudo-english word + */ + +static const char * +result(r) + 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(str) + 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) != NULL) + 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(str) + 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(scrstr, timeout, debug) + char *scrstr; + int timeout, 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; + struct termios tneed; + + /* + * 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/telnetd/authenc.c b/libexec/getty/extern.h index ccb463c..bd84a13 100644 --- a/libexec/telnetd/authenc.c +++ b/libexec/getty/extern.h @@ -1,5 +1,5 @@ -/*- - * Copyright (c) 1991, 1993 +/* + * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,63 +29,35 @@ * LIABILITY, OR TORT (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 + * $Id$ */ -#ifndef lint -static char sccsid[] = "@(#)authenc.c 8.2 (Berkeley) 5/30/95"; -#endif /* not lint */ - -#if defined(AUTHENTICATION) || defined(ENCRYPTION) -#include "telnetd.h" -#include <libtelnet/misc.h> - - int -net_write(str, len) - unsigned char *str; - int len; -{ - if (nfrontp + len < netobuf + BUFSIZ) { - memmove((void *)nfrontp, (void *)str, len); - nfrontp += len; - return(len); - } - return(0); -} - - void -net_encrypt() -{ -#ifdef ENCRYPTION - char *s = (nclearto > nbackp) ? nclearto : nbackp; - if (s < nfrontp && encrypt_output) { - (*encrypt_output)((unsigned char *)s, nfrontp - s); - } - nclearto = nfrontp; -#endif /* ENCRYPTION */ -} - - int -telnet_spin() -{ - ttloop(); - return(0); -} +struct delayval; +struct termios; +struct gettyflags; +struct gettynums; +struct gettystrs; - char * -telnet_getenv(val) - char *val; -{ - extern char *getenv(); - return(getenv(val)); -} +extern char hostname[]; +extern int hopcount; +extern struct termios tmode, omode; +extern struct gettyflags gettyflags[]; +extern struct gettynums gettynums[]; +extern struct gettystrs gettystrs[]; - char * -telnet_gets(prompt, result, length, echo) - char *prompt; - char *result; - int length; - int echo; -{ - return((char *)0); -} -#endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */ +int adelay __P((int, struct delayval *)); +const char *autobaud __P((void)); +int delaybits __P((void)); +void edithost __P((const char *)); +void gendefaults __P((void)); +void gettable __P((const char *, char *)); +void makeenv __P((char *[])); +const char *portselector __P((void)); +void set_ttydefaults __P((int)); +void setchars __P((void)); +void setdefaults __P((void)); +void setflags __P((int)); +int speed __P((int)); +int getty_chat __P((char *, int, int)); diff --git a/libexec/getty/getty.8 b/libexec/getty/getty.8 new file mode 100644 index 0000000..c1b1936 --- /dev/null +++ b/libexec/getty/getty.8 @@ -0,0 +1,128 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)getty.8 8.1 (Berkeley) 6/4/93 +.\" $Id$ +.\" " +.Dd June 4, 1993 +.Dt GETTY 8 +.Os BSD 4 +.Sh NAME +.Nm getty +.Nd set terminal mode +.Sh SYNOPSIS +.Nm getty +.Oo +.Ar type +.Op Ar tty +.Oc +.Sh DESCRIPTION +The +.Nm getty +program +is called by +.Xr init 8 +to open and initialize the tty line, read a login name, and invoke +.Xr login 1 . +.Pp +The argument +.Ar tty +is the special device file in +.Pa /dev +to open for the terminal (for example, ``ttyh0''). +If there is no argument or the argument is +.Ql Fl , +the tty line is assumed to be open as file descriptor 0. +.Pp +The +.Ar type +argument can be used to make +.Nm getty +treat the terminal line specially. +This argument is used as an index into the +.Nm gettytab 5 +database, to determine the characteristics of the line. +If there is no argument, or there is no such table, the +.Em default +table is used. +If there is no +.Pa /etc/gettytab +a set of system defaults is used. +If indicated by the table located, +.Nm getty +will clear the terminal screen, +print a banner heading, +and prompt for a login name. +Usually either the banner or the login prompt will include +the system hostname. +.Pp +Most of the default actions of +.Nm getty +can be circumvented, or modified, by a suitable +.Nm gettytab +table. +.Pp +The +.Nm getty +program +can be set to timeout after some interval, +which will cause dial up lines to hang up +if the login name is not entered reasonably quickly. +.Sh DIAGNOSTICS +.Bl -diag +.It "ttyxx: No such device or address." +.It "ttyxx: No such file or address." +A terminal which is turned +on in the +.Xr ttys +file cannot be opened, likely because the requisite +lines are either not configured into the system, the associated device +was not attached during boot-time system configuration, +or the special file in +.Pa /dev +does not exist. +.El +.Sh FILES +.Bl -tag -width /etc/gettytab -compact +.It Pa /etc/gettytab +.El +.Sh SEE ALSO +.Xr login 1 , +.Xr ioctl 2 , +.Xr tty 4 , +.Xr gettytab 5 , +.Xr ttys 5 , +.Xr init 8 +.Sh HISTORY +A +.Nm getty +program appeared in +.At v6 . diff --git a/libexec/getty/gettytab.5 b/libexec/getty/gettytab.5 new file mode 100644 index 0000000..ab92e0b --- /dev/null +++ b/libexec/getty/gettytab.5 @@ -0,0 +1,516 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)gettytab.5 8.4 (Berkeley) 4/19/94 +.\" $Id$ +.\" " +.Dd April 19, 1994 +.Dt GETTYTAB 5 +.Os BSD 4.2 +.Sh NAME +.Nm gettytab +.Nd terminal configuration data base +.Sh SYNOPSIS +.Nm gettytab +.Sh DESCRIPTION +The +.Nm gettytab +file +is a simplified version of the +.Xr termcap 5 +data base +used to describe terminal lines. +The initial terminal login process +.Xr getty 8 +accesses the +.Nm gettytab +file each time it starts, allowing simpler +reconfiguration of terminal characteristics. +Each entry in the data base +is used to describe one class of terminals. +.Pp +There is a default terminal class, +.Em default , +that is used to set global defaults for all other classes. +(That is, the +.Em default +entry is read, then the entry for the class required +is used to override particular settings.) +.Sh CAPABILITIES +Refer to +.Xr termcap 5 +for a description of the file layout. +The +.Em default +column below lists defaults obtained if there is +no entry in the table obtained, nor one in the special +.Em default +table. +.Bl -column Namexx /usr/bin/login Default +.It Sy Name Type Default Description +.It "ac str unused expect-send chat script for modem answer" +.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 ac/ic scripts" +.It "dc num 0 chat debug bitmask" +.It "de num 0 delay secs and flush input before writing first prompt" +.It "ds str" Ta So Li ^Y Sc Ta +.No "delayed suspend character" +.It "dx bool false set" +.Dv DECCTLQ +.It "ec bool false leave echo" +.Tn OFF +.It "ep bool false terminal uses even parity" +.It "er str" Ta So Li ^? Sc Ta +.No "erase character" +.It "et str" Ta So Li ^D Sc Ta +.No "end of text" +.Pq Dv EOF +character +.It "ev str" Ta Dv NULL Ta +.No "initial environment" +.It "f0 num unused tty mode flags to write messages" +.It "f1 num unused tty mode flags to read login name" +.It "f2 num unused tty mode flags to leave terminal as" +.It "fl str" Ta So Li ^O Sc Ta +.No "output flush character" +.It "hc bool false do" +.Tn NOT +hangup line on last close +.It "he str" Ta Dv NULL Ta +.No "hostname editing string" +.It "hn str hostname hostname" +.It "ht bool false terminal has real tabs" +.It "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" +.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 "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 "pp str unused PPP authentication 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 ac" +.It "rw bool false do" +.Tn NOT +use raw for input, use cbreak +.It "sp num unused line speed (input and output)" +.It "su str" Ta So Li ^Z Sc Ta +.No "suspend character" +.It "tc str none table continuation" +.It "to num 0 timeout (seconds)" +.It "tt str" Ta Dv NULL Ta +.No "terminal type (for environment)" +.It "ub bool false do unbuffered output (of prompts etc)" +.It "we str" Ta So Li ^W Sc Ta +.No "word erase character" +.It xc bool false do +.Tn NOT +echo control chars as +.Ql ^X +.It "xf str" Ta So Li ^S Sc Ta Dv XOFF +(stop output) character +.It "xn str" Ta So Li ^Q Sc Ta Dv XON +(start output) character +.It "Lo str C the locale name used for \&%d in the banner message" +.El +.Pp +The following capabilities are no longer supported by getty(8): +.Bl -column Namexx /usr/bin/login Default +.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 +.Em \&c0 , +.Em \&c1 , +.Em \&c2 , +.Em \&i0 , +.Em \&i1 , +.Em \&i2 , +.Em \&l0 , +.Em \&l1 , +.Em \&l2 , +.Em \&o0 , +.Em \&o1 , +or +.Em \&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 +.Em c_cflag , +.Em c_iflag , +.Em c_lflag , +and +.Em c_oflag +fields, respectively. Each these sets must be completely specified to be +effective. +The +.Em \&f0 , +.Em \&f1 , +and +.Em \&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 +.Em 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 +.Em nx +entry. If there is none, it will re-use its original table. +.Pp +Delays are specified in milliseconds, the nearest possible +delay available in the tty driver will be used. +Should greater certainty be desired, delays +with values 0, 1, 2, and 3 are interpreted as +choosing that particular delay algorithm from the driver. +.Pp +The +.Em \&cl +screen clear string may be preceded by a (decimal) number +of milliseconds of delay required (a la termcap). +This delay is simulated by repeated use of the pad character +.Em \&pc . +.Pp +The initial message, and login message, +.Em \&im +and +.Em \&lm +may include any of the following character sequences, which expand to +information about the environment in which +.Xr getty 8 +is running. +.Pp +.Bl -tag -offset indent -width \&%xxxxxxxxxxxxxx +.It \&%d +The current date and time in the locale's representation as of the +.Em \&Lo +string +(the \&%+ format of +.Xr strftime 3 ). +.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 +.Em \&hn +table entry. +In either case it may be edited with the +.Em \&he +string. +A '@' in the +.Em \&he +string causes one character from the real hostname to +be copied to the final hostname. +A '#' in the +.Em \&he +string causes the next character of the real hostname +to be skipped. +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 +.Em \&lo +string (usually +.Dq Pa /usr/bin/login ) , +it will have set +the environment to include the terminal type, as indicated +by the +.Em \&tt +string (if it exists). +The +.Em \&ev +string, can be used to enter additional data into +the environment. +It is a list of comma separated strings, each of which +will presumably be of the form +.Em name=value . +.Pp +If a non-zero timeout is specified, with +.Em \&to , +then getty will exit within the indicated +number of seconds, either having +received a login name and passed control +to +.Xr login 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 +.Em \&op +or +.Em \&np +is specified. +The +.Em \&op +string +may be specified with +.Em \&ap +to allow any parity on input, but generate odd parity output. +Note: this only applies while getty is being run, +terminal driver limitations prevent a more complete +implementation. +.Xr Getty 8 +does not check parity of input characters in +.Dv RAW +mode. +.Pp +If +.Em \&pp +string is specified and a PPP link bringup sequence is recognized, +getty will invoke the program referenced by the pp option. This +can be used to handle incoming PPP calls. +.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. +Hexidecimal values are, at most, 2 hex digits long, and octal +values are a maximum of 3 octal digits. +.Pp +The +.Em \&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 +.Em \&ct timeout, then a normal login sequence commences. +.Pp +The +.Em \&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 +.Em \&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. +.Pp +.Sh SEE ALSO +.Xr login 1 , +.Xr gethostname 3 , +.Xr uname 3 , +.Xr termcap 5 , +.Xr getty 8 , +.Xr telnetd 8 . +.Sh BUGS +The special characters (erase, kill, etc.) are reset to system defaults +by +.Xr login 1 . +In +.Em all +cases, '#' or '^H' typed in a login name will be treated as +an erase character, and '@' will be treated as a kill character. +.Pp +The delay stuff is a real crock. +Apart form its general lack of flexibility, some +of the delay algorithms are not implemented. +The terminal driver should support sane delay settings. +.Pp +The +.Em \&he +capability is stupid. +.Pp +The +.Xr termcap +format is horrid, something more rational should +have been chosen. +.Sh HISTORY +The +.Nm gettytab +file format appeared in +.Bx 4.2 . diff --git a/libexec/getty/gettytab.h b/libexec/getty/gettytab.h new file mode 100644 index 0000000..1321ad1 --- /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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)gettytab.h 8.2 (Berkeley) 3/30/94 + * $Id$ + */ + +/* + * 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 + +/* + * 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 MB gettyflags[22].value +#define HW gettyflags[23].value + diff --git a/libexec/getty/init.c b/libexec/getty/init.c new file mode 100644 index 0000000..9df0728 --- /dev/null +++ b/libexec/getty/init.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)init.c 8.1 (Berkeley) 6/4/93";*/ +static char rcsid[] = "$Id$"; +#endif /* not lint */ + +/* + * Getty table initializations. + * + * Melbourne getty. + */ +#include <termios.h> +#include "extern.h" +#include "gettytab.h" +#include "pathnames.h" + +static char loginmsg[] = "login: "; +static char nullstr[] = ""; +static char loginprg[] = _PATH_LOGIN; + +struct gettystrs gettystrs[] = { + { "nx" }, /* next table */ + { "cl" }, /* screen clear characters */ + { "im" }, /* initial message */ + { "lm", loginmsg }, /* login message */ + { "er", &tmode.c_cc[VERASE] }, /* erase character */ + { "kl", &tmode.c_cc[VKILL] }, /* kill character */ + { "et", &tmode.c_cc[VEOF] }, /* eof chatacter (eot) */ + { "pc", nullstr }, /* pad character */ + { "tt" }, /* terminal type */ + { "ev" }, /* enviroment */ + { "lo", loginprg }, /* login program */ + { "hn", hostname }, /* host name */ + { "he" }, /* host name edit */ + { "in", &tmode.c_cc[VINTR] }, /* interrupt char */ + { "qu", &tmode.c_cc[VQUIT] }, /* quit char */ + { "xn", &tmode.c_cc[VSTART] }, /* XON (start) char */ + { "xf", &tmode.c_cc[VSTOP] }, /* XOFF (stop) char */ + { "bk", &tmode.c_cc[VEOL] }, /* brk char (alt \n) */ + { "su", &tmode.c_cc[VSUSP] }, /* suspend char */ + { "ds", &tmode.c_cc[VDSUSP] }, /* delayed suspend */ + { "rp", &tmode.c_cc[VREPRINT] },/* reprint char */ + { "fl", &tmode.c_cc[VDISCARD] },/* flush output */ + { "we", &tmode.c_cc[VWERASE] }, /* word erase */ + { "ln", &tmode.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 */ + { 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 */ + { 0 } +}; diff --git a/libexec/getty/main.c b/libexec/getty/main.c new file mode 100644 index 0000000..cead481 --- /dev/null +++ b/libexec/getty/main.c @@ -0,0 +1,763 @@ +/*- + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)main.c 8.1 (Berkeley) 6/20/93";*/ +static char rcsid[] = "$Id: main.c,v 1.20 1997/06/03 12:56:47 davidn Exp $"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/resource.h> +#include <sys/ttydefaults.h> +#include <sys/utsname.h> +#include <errno.h> +#include <signal.h> +#include <fcntl.h> +#include <time.h> +#include <ctype.h> +#include <fcntl.h> +#include <libutil.h> +#include <locale.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 "pathnames.h" +#include "extern.h" + +/* + * Set the amount of running time that getty should accumulate + * before deciding that something is wrong and exit. + */ +#define GETTY_TIMEOUT 60 /* seconds */ + +#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 */ + +struct termios tmode, omode; + +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]; + +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] + +static void dingdong __P((int)); +static int getname __P((void)); +static void interrupt __P((int)); +static void oflush __P((void)); +static void prompt __P((void)); +static void putchr __P((int)); +static void putf __P((const char *)); +static void putpad __P((const char *)); +static void puts __P((const char *)); +static void timeoverrun __P((int)); +static char *getline __P((int)); +static void setttymode __P((const char *, int)); +static void setdefttymode __P((const char *)); +static int opentty __P((const char *, int)); + +int main __P((int, char **)); + +jmp_buf timeout; + +static void +dingdong(signo) + int signo; +{ + alarm(0); + longjmp(timeout, 1); +} + +jmp_buf intrupt; + +static void +interrupt(signo) + int signo; +{ + longjmp(intrupt, 1); +} + +/* + * Action to take when getty is running too long. + */ +static void +timeoverrun(signo) + int signo; +{ + + syslog(LOG_ERR, "getty exiting due to excessive running time\n"); + exit(1); +} + +int +main(argc, argv) + int argc; + char **argv; +{ + extern char **environ; + const char *tname; + int first_sleep = 1, first_time = 1; + struct rlimit limit; + int rval; + + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + + openlog("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH); + gethostname(hostname, sizeof(hostname)); + if (hostname[0] == '\0') + strcpy(hostname, "Amnesiac"); + + /* + * Limit running time to deal with broken or dead lines. + */ + (void)signal(SIGXCPU, timeoverrun); + limit.rlim_max = RLIM_INFINITY; + limit.rlim_cur = GETTY_TIMEOUT; + (void)setrlimit(RLIMIT_CPU, &limit); + + 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); + + gettable(tname, tabent); + + /* Init modem sequence has been specified + */ + if (IC) { + if (!opentty(ttyn, O_RDWR|O_NONBLOCK)) + exit(1); + setdefttymode(tname); + 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 timeout; + + if (!opentty(ttyn, O_RDWR|O_NONBLOCK)) + exit(1); + setdefttymode(tname); + rfds = 1 << 0; /* FD_SET */ + timeout.tv_sec = RT; + timeout.tv_usec = 0; + i = select(32, (fd_set*)&rfds, (fd_set*)NULL, + (fd_set*)NULL, RT ? &timeout : 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 { /* blocking open */ + if (!opentty(ttyn, O_RDWR)) + exit(1); + } + } + } + + /* Start with default tty settings */ + if (tcgetattr(STDIN_FILENO, &tmode) < 0) { + syslog(LOG_ERR, "tcgetattr %s: %m", ttyn); + exit(1); + } + /* + * 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. + */ + tmode.c_iflag = TTYDEF_IFLAG; + tmode.c_oflag = TTYDEF_OFLAG; + tmode.c_lflag = TTYDEF_LFLAG; + tmode.c_cflag = TTYDEF_CFLAG; + omode = tmode; + + 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(tname, 0); + if (AB) { + tname = autobaud(); + continue; + } + if (PS) { + tname = portselector(); + 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) + 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); + } + if ((rval = getname()) == 2) { + oflush(); + alarm(0); + execle(PP, "ppplogin", ttyn, (char *) 0, env); + syslog(LOG_ERR, "%s: %m", PP); + exit(1); + } else if (rval) { + register int i; + + oflush(); + alarm(0); + signal(SIGALRM, SIG_DFL); + if (name[0] == '-') { + puts("user names may not start with '-'."); + continue; + } + if (!(upper || lower || digit)) + continue; + setflags(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", "-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; + } +} + +static int +opentty(const char *ttyn, int flags) +{ + int i, j = 0; + int failopenlogged = 0; + + while (j < 10 && (i = open(ttyn, flags)) == -1) + { + if (((j % 10) == 0) && (errno != ENXIO || !failopenlogged)) { + syslog(LOG_ERR, "open %s: %m", ttyn); + failopenlogged = 1; + } + j++; + sleep(60); + } + if (i == -1) { + syslog(LOG_ERR, "open %s: %m", ttyn); + return 0; + } + else { + login_tty(i); + return 1; + } +} + +static void +setdefttymode(tname) + const char * tname; +{ + if (tcgetattr(STDIN_FILENO, &tmode) < 0) { + syslog(LOG_ERR, "tcgetattr %s: %m", ttyn); + exit(1); + } + tmode.c_iflag = TTYDEF_IFLAG; + tmode.c_oflag = TTYDEF_OFLAG; + tmode.c_lflag = TTYDEF_LFLAG; + tmode.c_cflag = TTYDEF_CFLAG; + omode = tmode; + setttymode(tname, 1); +} + +static void +setttymode(tname, raw) + const char * tname; + int raw; +{ + int off = 0; + + gettable(tname, tabent); + if (OPset || EPset || APset) + APset++, OPset++, EPset++; + setdefaults(); + (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)); + setflags(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() +{ + register int c; + register 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); + setflags(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(1); + 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(); + np = name; + continue; + } else if (isdigit(c)) + digit++; + if (IG && (c <= ' ' || c > 0176)) + continue; + *np++ = c; + putchr(cs); + } + signal(SIGINT, SIG_IGN); + *np = 0; + if (c == '\r') + crmod = 1; + if ((upper && !lower && !LC) || UC) + for (np = name; *np; np++) + if (isupper(*np)) + *np = tolower(*np); + return (1 + ppp_connection); +} + +static void +putpad(s) + register const char *s; +{ + register 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(s) + register const char *s; +{ + while (*s) + putchr(*s++); +} + +char outbuf[OBUFSIZ]; +int obufcnt = 0; + +static void +putchr(cc) + 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() +{ + if (obufcnt) + write(STDOUT_FILENO, outbuf, obufcnt); + obufcnt = 0; +} + +static void +prompt() +{ + + putf(LM); + if (CO) + putchr('\n'); +} + + +static char * +getline(fd) + 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(cp) + register 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), "%+", 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++; + } +} diff --git a/libexec/bugfiler/pathnames.h b/libexec/getty/pathnames.h index 7ff5ab6..fd397dd 100644 --- a/libexec/bugfiler/pathnames.h +++ b/libexec/getty/pathnames.h @@ -30,9 +30,11 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + * from: @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + * $Id$ */ -#define MAIL_CMD "/usr/sbin/sendmail -i -t -F \"Bugs Bunny\" -f owner-bugs" -#undef _PATH_TMP -#define _PATH_TMP "/tmp/BUG_XXXXXX" +#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..84cbefb --- /dev/null +++ b/libexec/getty/subr.c @@ -0,0 +1,853 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +/*static char sccsid[] = "from: @(#)subr.c 8.1 (Berkeley) 6/4/93";*/ +static char rcsid[] = "$Id: subr.c,v 1.9 1997/02/22 14:21:40 peter Exp $"; +#endif /* not lint */ + +/* + * Melbourne getty. + */ +#define COMPAT_43 +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <termios.h> +#include <sys/ioctl.h> +#include <sys/param.h> +#include <syslog.h> +#ifdef DEBUG +#include <stdio.h> +#endif + +#include "gettytab.h" +#include "pathnames.h" +#include "extern.h" + + +#ifdef COMPAT_43 +static void compatflags __P((long)); +#endif + +/* + * Get a table entry. + */ +void +gettable(name, buf) + const char *name; + char *buf; +{ + register struct gettystrs *sp; + register struct gettynums *np; + register 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() +{ + register struct gettystrs *sp; + register struct gettynums *np; + register 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() +{ + register struct gettystrs *sp; + register struct gettynums *np; + register struct gettyflags *fp; + + for (sp = gettystrs; sp->field; sp++) + if (!sp->value) + sp->value = !sp->defalt ? 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() +{ + register int i; + register 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 +setflags(n) + int n; +{ + register tcflag_t iflag, oflag, cflag, lflag; + +#ifdef COMPAT_43 + switch (n) { + case 0: + if (F0set) { + compatflags(F0); + return; + } + break; + case 1: + if (F1set) { + compatflags(F1); + return; + } + break; + default: + if (F2set) { + compatflags(F2); + return; + } + break; + } +#endif + + 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 COMPAT_43 +/* + * Old TTY => termios, snatched from <sys/kern/tty_compat.c> + */ +void +compatflags(flags) +register long flags; +{ + register tcflag_t iflag, oflag, cflag, lflag; + + iflag = BRKINT|ICRNL|IMAXBEL|IXON|IXANY; + oflag = OPOST|ONLCR|OXTABS; + cflag = CREAD; + lflag = ICANON|ISIG|IEXTEN; + + if (ISSET(flags, TANDEM)) + SET(iflag, IXOFF); + else + CLR(iflag, IXOFF); + if (ISSET(flags, ECHO)) + SET(lflag, ECHO); + else + CLR(lflag, ECHO); + if (ISSET(flags, CRMOD)) { + SET(iflag, ICRNL); + SET(oflag, ONLCR); + } else { + CLR(iflag, ICRNL); + CLR(oflag, ONLCR); + } + if (ISSET(flags, XTABS)) + SET(oflag, OXTABS); + else + CLR(oflag, OXTABS); + + + if (ISSET(flags, RAW)) { + iflag &= IXOFF; + CLR(lflag, ISIG|ICANON|IEXTEN); + CLR(cflag, PARENB); + } else { + SET(iflag, BRKINT|IXON|IMAXBEL); + SET(lflag, ISIG|IEXTEN); + if (ISSET(flags, CBREAK)) + CLR(lflag, ICANON); + else + SET(lflag, ICANON); + switch (ISSET(flags, ANYP)) { + case 0: + CLR(cflag, PARENB); + break; + case ANYP: + SET(cflag, PARENB); + CLR(iflag, INPCK); + break; + case EVENP: + SET(cflag, PARENB); + SET(iflag, INPCK); + CLR(cflag, PARODD); + break; + case ODDP: + SET(cflag, PARENB); + SET(iflag, INPCK); + SET(cflag, PARODD); + break; + } + } + + /* Nothing we can do with CRTBS. */ + if (ISSET(flags, PRTERA)) + SET(lflag, ECHOPRT); + else + CLR(lflag, ECHOPRT); + if (ISSET(flags, CRTERA)) + SET(lflag, ECHOE); + else + CLR(lflag, ECHOE); + /* Nothing we can do with TILDE. */ + if (ISSET(flags, MDMBUF)) + SET(cflag, MDMBUF); + else + CLR(cflag, MDMBUF); + if (ISSET(flags, NOHANG)) + CLR(cflag, HUPCL); + else + SET(cflag, HUPCL); + if (ISSET(flags, CRTKIL)) + SET(lflag, ECHOKE); + else + CLR(lflag, ECHOKE); + if (ISSET(flags, CTLECH)) + SET(lflag, ECHOCTL); + else + CLR(lflag, ECHOCTL); + if (!ISSET(flags, DECCTQ)) + SET(iflag, IXANY); + else + CLR(iflag, IXANY); + CLR(lflag, TOSTOP|FLUSHO|PENDIN|NOFLSH); + SET(lflag, ISSET(flags, TOSTOP|FLUSHO|PENDIN|NOFLSH)); + + if (ISSET(flags, RAW|LITOUT|PASS8)) { + CLR(cflag, CSIZE); + SET(cflag, CS8); + if (!ISSET(flags, RAW|PASS8)) + SET(iflag, ISTRIP); + else + CLR(iflag, ISTRIP); + if (!ISSET(flags, RAW|LITOUT)) + SET(oflag, OPOST); + else + CLR(oflag, OPOST); + } else { + CLR(cflag, CSIZE); + SET(cflag, CS7); + SET(iflag, ISTRIP); + SET(oflag, OPOST); + } + + tmode.c_iflag = iflag; + tmode.c_oflag = oflag; + tmode.c_cflag = cflag; + tmode.c_lflag = lflag; +} +#endif + +#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() +{ + register 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(ms, dp) + register ms; + register struct delayval *dp; +{ + if (ms == 0) + return (0); + while (dp->delay && ms > dp->delay) + dp++; + return (dp->bits); +} +#endif + +char editedhost[MAXHOSTNAMELEN]; + +void +edithost(pat) + register const char *pat; +{ + register const char *host = HN; + register char *res = editedhost; + + if (!pat) + pat = ""; + while (*pat) { + switch (*pat) { + + case '#': + if (*host) + host++; + break; + + case '@': + if (*host) + *res++ = *host++; + break; + + default: + *res++ = *pat; + break; + + } + if (res == &editedhost[sizeof editedhost - 1]) { + *res = '\0'; + return; + } + pat++; + } + if (*host) + strncpy(res, host, sizeof editedhost - (res - editedhost) - 1); + else + *res = '\0'; + editedhost[sizeof editedhost - 1] = '\0'; +} + +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 }, + { 0 } +}; + +int +speed(val) + int val; +{ + register struct speedtab *sp; + + if (val <= B115200) + return (val); + + for (sp = speedtab; sp->speed; sp++) + if (sp->speed == val) + return (sp->uxname); + + return (B300); /* default in impossible cases */ +} + +void +makeenv(env) + char *env[]; +{ + static char termbuf[128] = "TERM="; + register char *p, *q; + register char **ep; + + ep = env; + if (TT && *TT) { + strcat(termbuf, TT); + *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() +{ + char c, baud[20]; + const char *type = "default"; + register struct portselect *ps; + int len; + + alarm(5*60); + for (len = 0; len < sizeof (baud) - 1; len++) { + if (read(STDIN_FILENO, &c, 1) <= 0) + break; + c &= 0177; + if (c == '\n' || c == '\r') + break; + if (c == 'B') + len = 0; /* in case of leading garbage */ + baud[len] = c; + } + baud[len] = '\0'; + for (ps = portspeeds; ps->ps_baud; ps++) + if (strcmp(ps->ps_baud, baud) == 0) { + type = ps->ps_type; + break; + } + sleep(2); /* wait for connection to complete */ + return (type); +} + +/* + * This auto-baud speed select mechanism is written for the Micom 600 + * portselector. Selection is done by looking at how the character '\r' + * is garbled at the different speeds. + */ +#include <sys/time.h> + +const char * +autobaud() +{ + 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..4130ab8 --- /dev/null +++ b/libexec/getty/ttys.5 @@ -0,0 +1,163 @@ +.\" 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. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)ttys.5 8.1 (Berkeley) 6/4/93 +.\" $Id: ttys.5,v 1.7 1997/04/13 21:29:50 davidn Exp $ +.\" " +.Dd November 17, 1996 +.Dt TTYS 5 +.Os +.Sh NAME +.Nm ttys +.Nd terminal initialization information +.Sh DESCRIPTION +The file +.Nm ttys +contains information that is used by various routines to initialize +and control the use of terminal special files. +This information is read with the +.Xr getttyent 3 +library routines. +There is one line in the +.Nm ttys +file per special device file. +Fields are separated by tabs and/or spaces. +Fields comprised of more than one word should be enclosed in double +quotes (``"''). +Blank lines and comments may appear anywhere in the file; comments +are delimited by hash marks (``#'') and new lines. +Any unspecified fields will default to null. +.Pp +The first field is the +name of the terminal special file as it is found in +.Pa /dev . +.Pp +The second field of the file is the command to execute for the line, +usually +.Xr getty 8 , +which initializes and opens the line, setting the speed, waiting for +a user name and executing the +.Xr login 1 +program. +It can be, however, any desired command, for example +the start up for a window system terminal emulator or some other +daemon process, and can contain multiple words if quoted. +.Pp +The third field is the type of terminal usually connected to that +tty line, normally the one found in the +.Xr termcap 5 +data base file. +The environment variable +.Dv TERM +is initialized with the value by +either +.Xr getty 8 +or +.Xr login 1 . +.Pp +The remaining fields set flags in the +.Fa ty_status +entry (see +.Xr getttyent 3 ), +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 +should (should not) execute the command given in the second field, +while ``secure'' (if ``on'' is also specified) allows users with a +uid of 0 to login on +this line. +The flag ``dialin'' indicates that a tty entry describes a dialin +line, and ``network'' indicates that a tty entry provides a +network connection. +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 +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. +.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/new/xterm -L :0" vs100 on window="/usr/new/Xvs100 0" +# Network pseudo ttys -- don't enable getty +ttyp0 none network group=pty +ttyp1 none network off group=pty +.Ed +.Sh FILES +.Bl -tag -width /etc/ttys -compact +.It Pa /etc/ttys +.El +.Sh SEE ALSO +.Xr login 1 , +.Xr getttyent 3 , +.Xr ttyslot 3 , +.Xr gettytab 5 , +.Xr 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/kpasswdd/Makefile b/libexec/kpasswdd/Makefile deleted file mode 100644 index a01cbf8..0000000 --- a/libexec/kpasswdd/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# @(#)Makefile 8.1 (Berkeley) 6/4/93 - -PROG= kpasswdd -SRCS= kpasswdd.c des_rw.c -CFLAGS+=-DCRYPT -DKERBEROS -I${.CURDIR}/../../usr.bin/passwd -DPADD= ${LIBKDB} ${LIBKRB} ${LIBDES} -LDADD= -lkdb -lkrb -ldes -.PATH: ${.CURDIR}/../../usr.bin/rlogin -MAN8= kpasswdd.0 - -.include <bsd.prog.mk> diff --git a/libexec/kpasswdd/kpasswdd.c b/libexec/kpasswdd/kpasswdd.c deleted file mode 100644 index 23ff1f8..0000000 --- a/libexec/kpasswdd/kpasswdd.c +++ /dev/null @@ -1,271 +0,0 @@ -/*- - * Copyright (c) 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static char copyright[] = -"@(#) Copyright (c) 1990, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)kpasswdd.c 8.1 (Berkeley) 6/4/93"; -#endif /* not lint */ - -/* - * kpasswdd - update a principal's passwd field in the Kerberos - * database. Called from inetd. - * K. Fall - * 12-Dec-88 - */ - -#include <sys/types.h> -#include <sys/time.h> -#include <sys/resource.h> -#include <sys/signal.h> -#include <netinet/in.h> -#include <pwd.h> -#include <syslog.h> -#include <kerberosIV/des.h> -#include <kerberosIV/krb.h> -#include <kerberosIV/krb_db.h> -#include <stdio.h> -#include "kpasswd_proto.h" - -static struct kpasswd_data kpwd_data; -static des_cblock master_key, key; -static Key_schedule master_key_schedule, - key_schedule, random_sched; -long mkeyversion; -AUTH_DAT kdata; -static Principal principal_data; -static struct update_data ud_data; - -char inst[INST_SZ]; -char version[9]; -KTEXT_ST ticket; - -char *progname; /* for the library */ - -main() -{ - struct sockaddr_in foreign; - int foreign_len = sizeof(foreign); - int rval, more; - static char name[] = "kpasswdd"; - - static struct rlimit rl = { 0, 0 }; - - progname = name; - openlog("kpasswdd", LOG_CONS | LOG_PID, LOG_AUTH); - - signal(SIGHUP, SIG_IGN); - signal(SIGINT, SIG_IGN); - signal(SIGTSTP, SIG_IGN); - if (setrlimit(RLIMIT_CORE, &rl) < 0) { - syslog(LOG_ERR, "setrlimit: %m"); - exit(1); - } - - if (getpeername(0, &foreign, &foreign_len) < 0) { - syslog(LOG_ERR,"getpeername: %m"); - exit(1); - } - - strcpy(inst, "*"); - rval = krb_recvauth( - 0L, /* options--!MUTUAL */ - 0, /* file desc */ - &ticket, /* client's ticket */ - SERVICE, /* expected service */ - inst, /* expected instance */ - &foreign, /* foreign addr */ - (struct sockaddr_in *) 0, /* local addr */ - &kdata, /* returned krb data */ - "", /* service keys file */ - (bit_64 *) NULL, /* returned key schedule */ - version - ); - - - if (rval != KSUCCESS) { - syslog(LOG_NOTICE, "krb_recvauth: %s", krb_err_txt[rval]); - cleanup(); - exit(1); - } - - if (*version == '\0') { - /* indicates error on client's side (no tickets, etc.) */ - cleanup(); - exit(0); - } else if (strcmp(version, "KPWDV0.1") != 0) { - syslog(LOG_NOTICE, - "kpasswdd version conflict (recv'd %s)", - version); - cleanup(); - exit(1); - } - - - /* get master key */ - if (kdb_get_master_key(0, master_key, master_key_schedule) != 0) { - syslog(LOG_ERR, "couldn't get master key"); - cleanup(); - exit(1); - } - - mkeyversion = kdb_get_master_key(NULL, master_key, master_key_schedule); - - if (mkeyversion < 0) { - syslog(LOG_NOTICE, "couldn't verify master key"); - cleanup(); - exit(1); - } - - /* get principal info */ - rval = kerb_get_principal( - kdata.pname, - kdata.pinst, - &principal_data, - 1, - &more - ); - - if (rval < 0) { - syslog(LOG_NOTICE, - "error retrieving principal record for %s.%s", - kdata.pname, kdata.pinst); - cleanup(); - exit(1); - } - - if (rval != 1 || (more != 0)) { - syslog(LOG_NOTICE, "more than 1 dbase entry for %s.%s", - kdata.pname, kdata.pinst); - cleanup(); - exit(1); - } - - /* get the user's key */ - - bcopy(&principal_data.key_low, key, 4); - bcopy(&principal_data.key_high, ((long *) key) + 1, 4); - kdb_encrypt_key(key, key, master_key, master_key_schedule, - DECRYPT); - key_sched(key, key_schedule); - des_set_key(key, key_schedule); - - - /* get random key and send it over {random} Kperson */ - - random_key(kpwd_data.random_key); - strcpy(kpwd_data.secure_msg, SECURE_STRING); - if (des_write(0, &kpwd_data, sizeof(kpwd_data)) != sizeof(kpwd_data)) { - syslog(LOG_NOTICE, "error writing initial data"); - cleanup(); - exit(1); - } - - bzero(key, sizeof(key)); - bzero(key_schedule, sizeof(key_schedule)); - - /* now read update info: { info }Krandom */ - - key_sched(kpwd_data.random_key, random_sched); - des_set_key(kpwd_data.random_key, random_sched); - if (des_read(0, &ud_data, sizeof(ud_data)) != sizeof(ud_data)) { - syslog(LOG_NOTICE, "update aborted"); - cleanup(); - exit(1); - } - - /* validate info string by looking at the embedded string */ - - if (strcmp(ud_data.secure_msg, SECURE_STRING) != 0) { - syslog(LOG_NOTICE, "invalid update from %s", - inet_ntoa(foreign.sin_addr)); - cleanup(); - exit(1); - } - - /* produce the new key entry in the database { key }Kmaster */ - string_to_key(ud_data.pw, key); - kdb_encrypt_key(key, key, - master_key, master_key_schedule, - ENCRYPT); - bcopy(key, &principal_data.key_low, 4); - bcopy(((long *) key) + 1, - &principal_data.key_high, 4); - bzero(key, sizeof(key)); - principal_data.key_version++; - if (kerb_put_principal(&principal_data, 1)) { - syslog(LOG_ERR, "couldn't write new record for %s.%s", - principal_data.name, principal_data.instance); - cleanup(); - exit(1); - } - - syslog(LOG_NOTICE,"wrote new password field for %s.%s from %s", - principal_data.name, - principal_data.instance, - inet_ntoa(foreign.sin_addr) - ); - - send_ack(0, "Update complete.\n"); - cleanup(); - exit(0); -} - -cleanup() -{ - bzero(&kpwd_data, sizeof(kpwd_data)); - bzero(master_key, sizeof(master_key)); - bzero(master_key_schedule, sizeof(master_key_schedule)); - bzero(key, sizeof(key)); - bzero(key_schedule, sizeof(key_schedule)); - bzero(random_sched, sizeof(random_sched)); - bzero(&principal_data, sizeof(principal_data)); - bzero(&ud_data, sizeof(ud_data)); -} - -send_ack(remote, msg) - int remote; - char *msg; -{ - int cc; - cc = des_write(remote, msg, strlen(msg) + 1); - if (cc <= 0) { - syslog(LOG_NOTICE, "error writing ack"); - cleanup(); - exit(1); - } -} diff --git a/libexec/lfs_cleanerd/Makefile b/libexec/lfs_cleanerd/Makefile index 6993cf7..cda9236 100644 --- a/libexec/lfs_cleanerd/Makefile +++ b/libexec/lfs_cleanerd/Makefile @@ -1,10 +1,11 @@ # @(#)Makefile 8.1 (Berkeley) 6/5/93 +# $Id$ PROG= lfs_cleanerd -CFLAGS+=-I/sys/ufs/lfs -I${.CURDIR} -DDIAGNOSTIC -MAN8= lfs_cleanerd.0 +CFLAGS+=-I/sys/ufs/lfs -I${.CURDIR} ${DEBUG} +MAN8= lfs_cleanerd.8 SRCS= cleanerd.c lfs_cksum.c library.c misc.c print.c -.PATH: /sys/ufs/lfs +.PATH: ${.CURDIR}/../../sys/ufs/lfs .include <bsd.prog.mk> diff --git a/libexec/lfs_cleanerd/clean.h b/libexec/lfs_cleanerd/clean.h index f735ea1..b28cffe 100644 --- a/libexec/lfs_cleanerd/clean.h +++ b/libexec/lfs_cleanerd/clean.h @@ -30,7 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)clean.h 8.2 (Berkeley) 5/4/95 + * @(#)clean.h 8.1 (Berkeley) 6/4/93 + * $Id$ */ /* @@ -63,7 +64,7 @@ #define BUSY_LIM 0.50 #define IDLE_LIM 0.90 -#define MIN_SEGS(lfsp) (3) +#define MIN_SEGS(lfsp) (5) #define NUM_TO_CLEAN(fsp) (1) #define MAXLOADS 3 @@ -82,9 +83,9 @@ typedef struct fs_info { off_t fi_ifile_length; /* length of the ifile */ } FS_INFO; -/* +/* * XXX: size (in bytes) of a segment - * should lfs_bsize be fsbtodb(fs,1), blksize(fs), or lfs_dsize? + * should lfs_bsize be fsbtodb(fs,1), blksize(fs), or lfs_dsize? */ #define seg_size(fs) ((fs)->lfs_ssize << (fs)->lfs_bshift) @@ -106,7 +107,7 @@ typedef struct fs_info { __BEGIN_DECLS int dump_summary __P((struct lfs *, SEGSUM *, u_long, daddr_t **)); void err __P((const int, const char *, ...)); -int fs_getmntinfo __P((struct statfs **, char *, char *)); +int fs_getmntinfo __P((struct statfs **, char *, int)); int get __P((int, off_t, void *, size_t)); FS_INFO *get_fs_info __P((struct statfs *, int)); int lfs_segmapv __P((FS_INFO *, int, caddr_t, BLOCK_INFO **, int *)); diff --git a/libexec/lfs_cleanerd/cleanerd.c b/libexec/lfs_cleanerd/cleanerd.c index 6d06d75..f8877a8 100644 --- a/libexec/lfs_cleanerd/cleanerd.c +++ b/libexec/lfs_cleanerd/cleanerd.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id: cleanerd.c,v 1.6 1997/02/22 14:21:44 peter Exp $ */ #ifndef lint @@ -38,7 +40,7 @@ static char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)cleanerd.c 8.5 (Berkeley) 6/10/95"; +static char sccsid[] = "@(#)cleanerd.c 8.2 (Berkeley) 1/13/94"; #endif /* not lint */ #include <sys/param.h> @@ -57,10 +59,7 @@ static char sccsid[] = "@(#)cleanerd.c 8.5 (Berkeley) 6/10/95"; char *special = "cleanerd"; int do_small = 0; int do_mmap = 0; -int stat_report = 0; struct cleaner_stats { - double util_tot; - double util_sos; int blocks_read; int blocks_written; int segs_cleaned; @@ -68,10 +67,10 @@ struct cleaner_stats { int segs_error; } cleaner_stats; -struct seglist { +struct seglist { int sl_id; /* segment number */ int sl_cost; /* cleaning cost */ - char sl_bytes; /* bytes in segment */ + char sl_empty; /* is segment empty */ }; struct tossstruct { @@ -79,8 +78,6 @@ struct tossstruct { int seg; }; -#define CLEAN_BYTES 0x1 - /* function prototypes for system calls; not sure where they should go */ int lfs_segwait __P((fsid_t *, struct timeval *)); int lfs_segclean __P((fsid_t *, u_long)); @@ -89,10 +86,10 @@ int lfs_markv __P((fsid_t *, BLOCK_INFO *, int)); /* function prototypes */ int bi_tossold __P((const void *, const void *, const void *)); -int choose_segments __P((FS_INFO *, struct seglist *, +int choose_segments __P((FS_INFO *, struct seglist *, int (*)(FS_INFO *, SEGUSE *))); -void clean_fs __P((FS_INFO *, int (*)(FS_INFO *, SEGUSE *), int, long)); -int clean_loop __P((FS_INFO *, int, long)); +void clean_fs __P((FS_INFO *, int (*)(FS_INFO *, SEGUSE *))); +int clean_loop __P((FS_INFO *)); int clean_segment __P((FS_INFO *, int)); int cost_benefit __P((FS_INFO *, SEGUSE *)); int cost_compare __P((const void *, const void *)); @@ -122,14 +119,14 @@ cost_benefit(fsp, su) gettimeofday(&t, NULL); - live = su->su_nbytes; + live = su->su_nbytes; age = t.tv_sec < su->su_lastmod ? 0 : t.tv_sec - su->su_lastmod; - + lfsp = &fsp->fi_lfs; if (live == 0) return (t.tv_sec * lblkno(lfsp, seg_size(lfsp))); else { - /* + /* * from lfsSegUsage.c (Mendel's code). * priority calculation is done using INTEGER arithmetic. * sizes are in BLOCKS (that is why we use lblkno below). @@ -157,37 +154,22 @@ main(argc, argv) struct statfs *lstatfsp; /* file system stats */ struct timeval timeout; /* sleep timeout */ fsid_t fsid; - long clean_opts; /* cleaning options */ - int i, nodaemon, segs_per_clean; + int i, nodaemon; int opt, cmd_err; char *fs_name; /* name of filesystem to clean */ extern int optind; cmd_err = nodaemon = 0; - clean_opts = 0; - segs_per_clean = 1; - while ((opt = getopt(argc, argv, "bdmn:r:s")) != EOF) { + while ((opt = getopt(argc, argv, "smd")) != -1) { switch (opt) { - case 'b': /* - * Use live bytes to determine - * how many segs to clean. - */ - clean_opts |= CLEAN_BYTES; - break; - case 'd': /* Debug mode. */ - nodaemon = 1; + case 's': /* small writes */ + do_small = 1; break; - case 'm': /* Use mmap instead of read/write */ + case 'm': do_mmap = 1; break; - case 'n': /* How many segs to clean at once */ - segs_per_clean = atoi(optarg); - break; - case 'r': /* Report every stat_report segments */ - stat_report = atoi(optarg); - break; - case 's': /* small writes */ - do_small = 1; + case 'd': + nodaemon = 1; break; default: ++cmd_err; @@ -203,7 +185,7 @@ main(argc, argv) signal(SIGINT, sig_report); signal(SIGUSR1, sig_report); signal(SIGUSR2, sig_report); - if (fs_getmntinfo(&lstatfsp, fs_name, "lfs") == 0) { + if (fs_getmntinfo(&lstatfsp, fs_name, MOUNT_LFS) == 0) { /* didn't find the filesystem */ err(1, "lfs_cleanerd: filesystem %s isn't an LFS!", fs_name); } @@ -225,14 +207,14 @@ main(argc, argv) * to make sure that some nasty process hasn't just * filled the disk system up. */ - if (clean_loop(fsp, segs_per_clean, clean_opts)) + if (clean_loop(fsp)) continue; #ifdef VERBOSE (void)printf("Cleaner going to sleep.\n"); #endif if (lfs_segwait(&fsid, &timeout) < 0) - err(0, "lfs_segwait: returned error\n"); + err(0, "lfs_segwait: returned error\n"); #ifdef VERBOSE (void)printf("Cleaner waking up.\n"); #endif @@ -241,45 +223,33 @@ main(argc, argv) /* return the number of segments cleaned */ int -clean_loop(fsp, nsegs, options) +clean_loop(fsp) FS_INFO *fsp; /* file system information */ - int nsegs; - long options; { double loadavg[MAXLOADS]; time_t now; u_long max_free_segs; - u_long db_per_seg; /* * Compute the maximum possible number of free segments, given the * number of free blocks. */ - db_per_seg = fsbtodb(&fsp->fi_lfs, fsp->fi_lfs.lfs_ssize); - max_free_segs = fsp->fi_lfs.lfs_bfree / db_per_seg; - - /* + max_free_segs = fsp->fi_statfsp->f_bfree / fsp->fi_lfs.lfs_ssize; + + /* * We will clean if there are not enough free blocks or total clean * space is less than BUSY_LIM % of possible clean space. */ now = time((time_t *)NULL); -#ifdef VERBOSE - printf("db_er_seg = %d max_free_segs = %d, bfree = %d avail = %d ", - db_per_seg, max_free_segs, fsp->fi_lfs.lfs_bfree, - fsp->fi_lfs.lfs_avail); - printf("clean = %d\n", fsp->fi_cip->clean); -#endif - if ((fsp->fi_lfs.lfs_bfree - fsp->fi_lfs.lfs_avail > db_per_seg && - fsp->fi_lfs.lfs_avail < db_per_seg) || - (fsp->fi_cip->clean < max_free_segs && + if (fsp->fi_cip->clean < max_free_segs && (fsp->fi_cip->clean <= MIN_SEGS(&fsp->fi_lfs) || - fsp->fi_cip->clean < max_free_segs * BUSY_LIM))) { + fsp->fi_cip->clean < max_free_segs * BUSY_LIM)) { printf("Cleaner Running at %s (%d of %d segments available)\n", ctime(&now), fsp->fi_cip->clean, max_free_segs); - clean_fs(fsp, cost_benefit, nsegs, options); + clean_fs(fsp, cost_benefit); return (1); } else { - /* + /* * We will also clean if the system is reasonably idle and * the total clean space is less then IDLE_LIM % of possible * clean space. @@ -288,28 +258,27 @@ clean_loop(fsp, nsegs, options) perror("getloadavg: failed\n"); return (-1); } - if (loadavg[ONE_MIN] == 0.0 && loadavg[FIVE_MIN] && + if (loadavg[ONE_MIN] == 0.2 && loadavg[FIVE_MIN] && fsp->fi_cip->clean < max_free_segs * IDLE_LIM) { - clean_fs(fsp, cost_benefit, nsegs, options); - printf("Cleaner Running at %s (system idle)\n", + clean_fs(fsp, cost_benefit); + printf("Cleaner running (system idle) at %s", ctime(&now)); return (1); } - } - printf("Cleaner Not Running at %s\n", ctime(&now)); + } +#ifdef VERBOSE + printf("Cleaner not running at %s", ctime(&now)); +#endif return (0); } void -clean_fs(fsp, cost_func, nsegs, options) +clean_fs(fsp, cost_func) FS_INFO *fsp; /* file system information */ int (*cost_func) __P((FS_INFO *, SEGUSE *)); - int nsegs; - long options; { struct seglist *segs, *sp; - int to_clean, cleaned_bytes; int i; if ((segs = @@ -323,33 +292,15 @@ clean_fs(fsp, cost_func, nsegs, options) i, fsp->fi_statfsp->f_mntonname); fflush(stdout); #endif - if (i) { - /* Check which cleaning algorithm to use. */ - if (options & CLEAN_BYTES) { - cleaned_bytes = 0; - to_clean = nsegs << - (fsp->fi_lfs.lfs_segshift + fsp->fi_lfs.lfs_bshift); - for (sp = segs; i && cleaned_bytes < to_clean; - i--, ++sp) { - if (clean_segment(fsp, sp->sl_id) < 0) - perror("clean_segment failed"); - else if (lfs_segclean(&fsp->fi_statfsp->f_fsid, - sp->sl_id) < 0) - perror("lfs_segclean failed"); - printf("Cleaned segment %d (%d bytes)\n", - sp->sl_id, sp->sl_bytes); - cleaned_bytes += sp->sl_bytes; - } - } else - for (i = MIN(i, nsegs), sp = segs; i-- ; ++sp) { - if (clean_segment(fsp, sp->sl_id) < 0) - perror("clean_segment failed"); - else if (lfs_segclean(&fsp->fi_statfsp->f_fsid, - sp->sl_id) < 0) - perror("lfs_segclean failed"); - printf("Completed cleaning segment %d\n", sp->sl_id); - } - } + if (i) + for (i = MIN(i, NUM_TO_CLEAN(fsp)), sp = segs; i-- ; ++sp) { + if (clean_segment(fsp, sp->sl_id) < 0) + perror("clean_segment failed"); + else if (lfs_segclean(&fsp->fi_statfsp->f_fsid, + sp->sl_id) < 0) + perror("lfs_segclean failed"); + printf("Completed cleaning segment %d\n", sp->sl_id); + } free(segs); } @@ -402,7 +353,7 @@ choose_segments(fsp, seglist, cost_func) #endif sp->sl_cost = (*cost_func)(fsp, sup); sp->sl_id = i; - sp->sl_bytes = sup->su_nbytes; + sp->sl_empty = sup->su_nbytes ? 0 : 1; ++sp; } nsegs = sp - seglist; @@ -424,7 +375,6 @@ clean_segment(fsp, id) struct lfs *lfsp; struct tossstruct t; caddr_t seg_buf; - double util; int num_blocks, maxblocks, clean_blocks; lfsp = &fsp->fi_lfs; @@ -491,14 +441,8 @@ clean_segment(fsp, id) lp = (u_long *)_bip->bi_bp; } } - #endif - ++cleaner_stats.segs_cleaned; cleaner_stats.blocks_written += num_blocks; - util = ((double)num_blocks / fsp->fi_lfs.lfs_ssize); - cleaner_stats.util_tot += util; - cleaner_stats.util_sos += util * util; - if (do_small) maxblocks = MAXPHYS / fsp->fi_lfs.lfs_bsize - 1; else @@ -514,11 +458,10 @@ clean_segment(fsp, id) } num_blocks -= clean_blocks; } - + free(block_array); munmap_segment(fsp, seg_buf, do_mmap); - if (stat_report && cleaner_stats.segs_cleaned % stat_report == 0) - sig_report(SIGUSR1); + ++cleaner_stats.segs_cleaned; return (0); } @@ -541,30 +484,18 @@ void sig_report(sig) int sig; { - double avg; - printf("lfs_cleanerd:\t%s%d\n\t\t%s%d\n\t\t%s%d\n\t\t%s%d\n\t\t%s%d\n", "blocks_read ", cleaner_stats.blocks_read, "blocks_written ", cleaner_stats.blocks_written, "segs_cleaned ", cleaner_stats.segs_cleaned, "segs_empty ", cleaner_stats.segs_empty, "seg_error ", cleaner_stats.segs_error); - printf("\t\t%s%5.2f\n\t\t%s%5.2f\n", - "util_tot ", cleaner_stats.util_tot, - "util_sos ", cleaner_stats.util_sos); - printf("\t\tavg util: %4.2f std dev: %9.6f\n", - avg = cleaner_stats.util_tot / cleaner_stats.segs_cleaned, - cleaner_stats.util_sos / cleaner_stats.segs_cleaned - avg * avg); - - if (sig == SIGUSR2) { cleaner_stats.blocks_read = 0; cleaner_stats.blocks_written = 0; cleaner_stats.segs_cleaned = 0; cleaner_stats.segs_empty = 0; cleaner_stats.segs_error = 0; - cleaner_stats.util_tot = 0.0; - cleaner_stats.util_sos = 0.0; } if (sig == SIGINT) exit(0); diff --git a/libexec/lfs_cleanerd/lfs_cleanerd.8 b/libexec/lfs_cleanerd/lfs_cleanerd.8 index 3d134db..cd09119 100644 --- a/libexec/lfs_cleanerd/lfs_cleanerd.8 +++ b/libexec/lfs_cleanerd/lfs_cleanerd.8 @@ -30,8 +30,9 @@ .\" SUCH DAMAGE. .\" .\" @(#)lfs_cleanerd.8 8.2 (Berkeley) 12/11/93 +.\" $Id: lfs_cleanerd.8,v 1.5 1997/02/22 14:21:45 peter Exp $ .\" -.Dd "December 11, 1993" +.Dd December 11, 1993 .Dt LFS_CLEANERD 8 .Os BSD 4.4 .Sh NAME @@ -74,4 +75,5 @@ When cleaning the file system, read data in small chunks. .Sh HISTORY The .Nm lfs_cleanerd -utility first appeared in 4.4BSD. +utility first appeared in +.Bx 4.4 . diff --git a/libexec/lfs_cleanerd/library.c b/libexec/lfs_cleanerd/library.c index 32f7d37..b72aad8 100644 --- a/libexec/lfs_cleanerd/library.c +++ b/libexec/lfs_cleanerd/library.c @@ -29,10 +29,12 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id$ */ #ifndef lint -static char sccsid[] = "@(#)library.c 8.3 (Berkeley) 5/24/95"; +static char sccsid[] = "@(#)library.c 8.1 (Berkeley) 6/4/93"; #endif /* not lint */ #include <sys/param.h> @@ -73,7 +75,7 @@ int fs_getmntinfo(buf, name, type) struct statfs **buf; char *name; - char *type; + int type; { /* allocate space for the filesystem info */ *buf = (struct statfs *)malloc(sizeof(struct statfs)); @@ -87,9 +89,9 @@ fs_getmntinfo(buf, name, type) } /* check to see if it's the one we want */ - if (strcmp((*buf)->f_fstypename, type) || + if (((*buf)->f_type != type) || strncmp(name, (*buf)->f_mntonname, MNAMELEN)) { - /* "this is not the filesystem you're looking for */ + /* "this is not the filesystem you're looking for" */ free(*buf); return 0; } @@ -108,7 +110,7 @@ get_fs_info (lstatfsp, use_mmap) { FS_INFO *fsp; int i; - + fsp = (FS_INFO *)malloc(sizeof(FS_INFO)); if (fsp == NULL) return NULL; @@ -125,7 +127,7 @@ get_fs_info (lstatfsp, use_mmap) /* * If we are reading the ifile then we need to refresh it. Even if - * we are mmapping it, it might have grown. Finally, we need to + * we are mmapping it, it might have grown. Finally, we need to * refresh the file system information (statfs) info. */ void @@ -134,14 +136,14 @@ reread_fs_info(fsp, use_mmap) int use_mmap; { int i; - + if (statfs(fsp->fi_statfsp->f_mntonname, fsp->fi_statfsp)) err(1, "reread_fs_info: statfs failed"); get_ifile (fsp, use_mmap); } -/* - * Gets the superblock from disk (possibly in face of errors) +/* + * Gets the superblock from disk (possibly in face of errors) */ int get_superblock (fsp, sbp) @@ -161,11 +163,11 @@ get_superblock (fsp, sbp) get(fid, LFS_LABELPAD, sbp, sizeof(struct lfs)); close (fid); - + return (0); } -/* +/* * This function will map the ifile into memory. It causes a * fatal error on failure. */ @@ -202,23 +204,23 @@ get_ifile (fsp, use_mmap) if (fsp->fi_cip) munmap((caddr_t)fsp->fi_cip, fsp->fi_ifile_length); ifp = mmap ((caddr_t)0, file_stat.st_size, - PROT_READ|PROT_WRITE, 0, fid, (off_t)0); - if (ifp == (caddr_t)(-1)) + PROT_READ|PROT_WRITE, MAP_SHARED, fid, (off_t)0); + if (ifp == MAP_FAILED) err(1, "get_ifile: mmap failed"); } else { if (fsp->fi_cip) free(fsp->fi_cip); if (!(ifp = malloc (file_stat.st_size))) - err (1, "get_ifile: malloc failed"); + err (1, "get_ifile: malloc failed"); redo_read: count = read (fid, ifp, (size_t) file_stat.st_size); if (count < 0) - err(1, "get_ifile: bad ifile read"); + err(1, "get_ifile: bad ifile read"); else if (count < file_stat.st_size) { err(0, "get_ifile"); if (lseek(fid, 0, SEEK_SET) < 0) - err(1, "get_ifile: bad ifile lseek"); + err(1, "get_ifile: bad ifile lseek"); goto redo_read; } } @@ -249,7 +251,7 @@ redo_read: * summary was read (it may have "died" since then). Any given * pair will be listed at most once. */ -int +int lfs_segmapv(fsp, seg, seg_buf, blocks, bcount) FS_INFO *fsp; /* pointer to local file system information */ int seg; /* the segment number */ @@ -264,7 +266,7 @@ lfs_segmapv(fsp, seg, seg_buf, blocks, bcount) struct lfs *lfsp; caddr_t s, segend; daddr_t pseg_addr, seg_addr; - int i, nelem, nblocks, nsegs, sumsize; + int i, nelem, nblocks, sumsize; time_t timestamp; lfsp = &fsp->fi_lfs; @@ -281,32 +283,29 @@ lfs_segmapv(fsp, seg, seg_buf, blocks, bcount) #endif /* VERBOSE */ *bcount = 0; - for (nsegs = 0, timestamp = 0; nsegs < sup->su_nsums; nsegs++) { + for (segend = seg_buf + seg_size(lfsp), timestamp = 0; s < segend; ) { sp = (SEGSUM *)s; - nblocks = pseg_valid(fsp, sp); - if (nblocks <= 0) { - printf("Warning: invalid segment summary at 0x%x\n", - pseg_addr); - break; - } - #ifdef VERBOSE printf("\tpartial at: 0x%x\n", pseg_addr); print_SEGSUM(lfsp, sp); fflush(stdout); #endif /* VERBOSE */ + nblocks = pseg_valid(fsp, sp); + if (nblocks <= 0) + break; + /* Check if we have hit old data */ if (timestamp > ((SEGSUM*)s)->ss_create) break; timestamp = ((SEGSUM*)s)->ss_create; #ifdef DIAGNOSTIC - /* Verfiy size of summary block */ + /* Verify size of summary block */ sumsize = sizeof(SEGSUM) + (sp->ss_ninos + INOPB(lfsp) - 1) / INOPB(lfsp); - for (i = 0, fip = (FINFO *)(sp + 1); i < sp->ss_nfinfo; ++i) { + for (fip = (FINFO *)(sp + 1); i < sp->ss_nfinfo; ++i) { sumsize += sizeof(FINFO) + (fip->fi_nblocks - 1) * sizeof(daddr_t); fip = (FINFO *)(&fip->fi_blocks[fip->fi_nblocks]); @@ -348,13 +347,13 @@ lfs_segmapv(fsp, seg, seg_buf, blocks, bcount) err0: *bcount = 0; return (-1); - + } -/* +/* * This will parse a partial segment and fill in BLOCK_INFO structures * for each block described in the segment summary. It will not include - * blocks or inodes from files with new version numbers. + * blocks or inodes from files with new version numbers. */ void add_blocks (fsp, bip, countp, sp, seg_buf, segaddr, psegaddr) @@ -371,9 +370,7 @@ add_blocks (fsp, bip, countp, sp, seg_buf, segaddr, psegaddr) caddr_t bp; daddr_t *dp, *iaddrp; int db_per_block, i, j; - int db_frag; u_long page_size; -long *lp; #ifdef VERBOSE printf("FILE INFOS\n"); @@ -405,24 +402,8 @@ long *lp; bip->bi_segcreate = (time_t)(sp->ss_create); bip->bi_bp = bp; bip->bi_version = ifp->if_version; - if (fip->fi_lastlength == page_size) { - bip->bi_size = page_size; - psegaddr += db_per_block; - bp += page_size; - } else { - db_frag = fragstodb(&(fsp->fi_lfs), - numfrags(&(fsp->fi_lfs), - fip->fi_lastlength)); -#ifdef VERBOSE - printf("lastlength, frags: %d, %d, %d\n", - fip->fi_lastlength, temp, - bytetoda(fsp, temp)); - fflush(stdout); -#endif - bip->bi_size = fip->fi_lastlength; - bp += fip->fi_lastlength; - psegaddr += db_frag; - } + psegaddr += db_per_block; + bp += page_size; ++bip; ++(*countp); } @@ -450,10 +431,10 @@ add_inodes (fsp, bip, countp, sp, seg_buf, seg_addr) daddr_t *daddrp; ino_t inum; int i; - + if (sp->ss_ninos <= 0) return; - + bp = bip + *countp; lfsp = &fsp->fi_lfs; #ifdef VERBOSE @@ -465,9 +446,9 @@ add_inodes (fsp, bip, countp, sp, seg_buf, seg_addr) --daddrp; di = (struct dinode *)(seg_buf + ((*daddrp - seg_addr) << fsp->fi_daddr_shift)); - } else + } else ++di; - + inum = di->di_inumber; bp->bi_lbn = LFS_UNUSED_LBN; bp->bi_inode = inum; @@ -487,7 +468,7 @@ add_inodes (fsp, bip, countp, sp, seg_buf, seg_addr) if (ifp->if_daddr == *daddrp) { bp++; ++(*countp); - } + } } } } @@ -497,7 +478,7 @@ add_inodes (fsp, bip, countp, sp, seg_buf, seg_addr) * segment is valid or not. Returns the size of the partial segment if it * is valid, * and 0 otherwise. Use dump_summary to figure out size of the * the partial as well as whether or not the checksum is valid. - */ + */ int pseg_valid (fsp, ssp) FS_INFO *fsp; /* pointer to file system info */ @@ -507,13 +488,10 @@ pseg_valid (fsp, ssp) int i, nblocks; u_long *datap; - if (ssp->ss_magic != SS_MAGIC) - return(0); - if ((nblocks = dump_summary(&fsp->fi_lfs, ssp, 0, NULL)) <= 0 || nblocks > fsp->fi_lfs.lfs_ssize - 1) return(0); - + /* check data/inode block(s) checksum too */ datap = (u_long *)malloc(nblocks * sizeof(u_long)); p = (caddr_t)ssp + LFS_SUMMARY_SIZE; @@ -523,13 +501,13 @@ pseg_valid (fsp, ssp) } if (cksum ((void *)datap, nblocks * sizeof(u_long)) != ssp->ss_datasum) return (0); - + return (nblocks); } /* #define MMAP_SEGMENT */ -/* +/* * read a segment into a memory buffer */ int @@ -563,8 +541,8 @@ mmap_segment (fsp, segment, segbuf, use_mmap) if (use_mmap) { *segbuf = mmap ((caddr_t)0, seg_size(lfsp), PROT_READ, - 0, fid, seg_byte); - if (*(long *)segbuf < 0) { + MAP_SHARED, fid, seg_byte); + if (*segbuf == MAP_FAILED) { err(0, "mmap_segment: mmap failed"); return (NULL); } @@ -586,7 +564,7 @@ mmap_segment (fsp, segment, segbuf, use_mmap) free(*segbuf); return (-1); } - + if (read (fid, *segbuf, ssize) != ssize) { err (0, "mmap_segment: bad read"); free(*segbuf); @@ -654,7 +632,7 @@ bi_compare(a, b) return (diff); diff = (int)(ba->bi_daddr - bb->bi_daddr); return (diff); -} +} int bi_toss(dummy, a, b) @@ -689,7 +667,7 @@ toss(p, nump, size, dotoss, client) if (dotoss(client, p, p1)) { memmove(p, p1, i * size); --(*nump); - } else + } else p += size; } } diff --git a/libexec/lfs_cleanerd/print.c b/libexec/lfs_cleanerd/print.c index 2978a0c..8468f12 100644 --- a/libexec/lfs_cleanerd/print.c +++ b/libexec/lfs_cleanerd/print.c @@ -29,10 +29,12 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id$ */ #ifndef lint -static char sccsid[] = "@(#)print.c 8.2 (Berkeley) 5/24/95"; +static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/4/93"; #endif /* not lint */ #include <sys/param.h> @@ -65,32 +67,22 @@ dump_summary(lfsp, sp, flags, iaddrp) FINFO *fp; int ck; - if (sp->ss_magic != SS_MAGIC) - return(-1); - - if (sp->ss_sumsum != (ck = cksum(&sp->ss_datasum, + if (sp->ss_sumsum != (ck = cksum(&sp->ss_datasum, LFS_SUMMARY_SIZE - sizeof(sp->ss_sumsum)))) return(-1); - numblocks = (sp->ss_ninos + INOPB(lfsp) - 1) / INOPB(lfsp); - - /* Do some basic sanity checking. */ - if (sp->ss_nfinfo > LFS_SUMMARY_SIZE / sizeof(FINFO) || - numblocks > lfsp->lfs_ssize || - numblocks > LFS_SUMMARY_SIZE / sizeof(daddr_t)) - return(-1); - if (flags & DUMP_SUM_HEADER) { - (void)printf(" %s0x%X\t%s%d\t%s%d\n %s0x%X\t%s0x%X\t%s0x%X\n", + (void)printf(" %s0x%X\t%s%d\t%s%d\n %s0x%X\t%s0x%X", "next ", sp->ss_next, "nfinfo ", sp->ss_nfinfo, "ninos ", sp->ss_ninos, "sumsum ", sp->ss_sumsum, - "datasum ", sp->ss_datasum, - "magic ", sp->ss_magic); - (void)printf(" create %s", ctime((time_t *)&sp->ss_create)); + "datasum ", sp->ss_datasum ); + (void)printf("\tcreate %s", ctime((time_t *)&sp->ss_create)); } + numblocks = (sp->ss_ninos + INOPB(lfsp) - 1) / INOPB(lfsp); + /* Dump out inode disk addresses */ if (flags & DUMP_INODE_ADDRS) printf(" Inode addresses:"); @@ -171,13 +163,13 @@ dump_super(lfsp) "cleansz ", lfsp->lfs_cleansz, "segtabsz ", lfsp->lfs_segtabsz); - (void)printf("%s0x%X\t%s%d\t%s0x%qX\t%s%d\n", + (void)printf("%s0x%X\t%s%d\t%s0x%X\t%s%d\n", "segmask ", lfsp->lfs_segmask, "segshift ", lfsp->lfs_segshift, "bmask ", lfsp->lfs_bmask, "bshift ", lfsp->lfs_bshift); - (void)printf("%s0x%qX\t\t%s%d\t%s0x%qX\t%s%d\n", + (void)printf("%s0x%X\t\t%s%d\t%s0x%X\t%s%d\n", "ffmask ", lfsp->lfs_ffmask, "ffshift ", lfsp->lfs_ffshift, "fbmask ", lfsp->lfs_fbmask, diff --git a/libexec/mail.local/Makefile b/libexec/mail.local/Makefile index e8556d8..59eaed2 100644 --- a/libexec/mail.local/Makefile +++ b/libexec/mail.local/Makefile @@ -1,7 +1,11 @@ # @(#)Makefile 8.1 (Berkeley) 7/19/93 +# $Id$ PROG= mail.local -MAN8= mail.local.0 +MAN8= mail.local.8 +.if defined(DONT_FSYNC) +CFLAGS+= -DDONT_FSYNC +.endif BINOWN= root BINMODE=4555 INSTALLFLAGS=-fschg diff --git a/libexec/mail.local/mail.local.8 b/libexec/mail.local/mail.local.8 index 661615c..8ffd535 100644 --- a/libexec/mail.local/mail.local.8 +++ b/libexec/mail.local/mail.local.8 @@ -30,6 +30,7 @@ .\" SUCH DAMAGE. .\" .\" @(#)mail.local.8 8.2 (Berkeley) 12/11/93 +.\" $Id$ .\" .Dd December 11, 1993 .Dt MAIL.LOCAL 8 @@ -40,6 +41,7 @@ .Sh SYNOPSIS .Nm mail.local .Op Fl f Ar from +.Op Fl b .Ar user ... .Sh DESCRIPTION .Nm Mail.local @@ -55,6 +57,10 @@ The options are as follows: .Bl -tag -width xxxfrom .It Fl f Ar from Specify the sender's name. +.It Fl b +Turn off the attempts to notify the +.Dq biff +service. .El .Pp Individual mail messages in the mailbox are delimited by an empty @@ -90,7 +96,6 @@ user's mailbox directory .El .Sh SEE ALSO .Xr mail 1 , -.Xr xsend 1 , .Xr flock 2 , .Xr getservbyname 3 , .Xr comsat 8 , diff --git a/libexec/mail.local/mail.local.c b/libexec/mail.local/mail.local.c index 1ee4466..cb47bfb 100644 --- a/libexec/mail.local/mail.local.c +++ b/libexec/mail.local/mail.local.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id: mail.local.c,v 1.12 1997/02/22 14:21:48 peter Exp $ */ #ifndef lint @@ -38,18 +40,9 @@ static char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)mail.local.c 8.22 (Berkeley) 6/21/95"; +static char sccsid[] = "@(#)mail.local.c 8.6 (Berkeley) 4/8/94"; #endif /* not lint */ -/* - * This is not intended to compile on System V derived systems - * such as Solaris or HP-UX, since they use a totally different - * approach to mailboxes (essentially, they have a setgid program - * rather than setuid, and they rely on the ability to "give away" - * files to do their work). IT IS NOT A BUG that this doesn't - * compile on such architectures. - */ - #include <sys/param.h> #include <sys/stat.h> #include <sys/socket.h> @@ -67,7 +60,6 @@ static char sccsid[] = "@(#)mail.local.c 8.22 (Berkeley) 6/21/95"; #include <syslog.h> #include <time.h> #include <unistd.h> -#include <ctype.h> #if __STDC__ #include <stdarg.h> @@ -75,61 +67,13 @@ static char sccsid[] = "@(#)mail.local.c 8.22 (Berkeley) 6/21/95"; #include <varargs.h> #endif -#ifndef LOCK_EX -# include <sys/file.h> -#endif - -#ifdef BSD4_4 -# include "pathnames.h" -#endif - -#ifndef __P -# ifdef __STDC__ -# define __P(protos) protos -# else -# define __P(protos) () -# define const -# endif -#endif -#ifndef __dead -# if defined(__GNUC__) && (__GNUC__ < 2 || __GNUC_MINOR__ < 5) && !defined(__STRICT_ANSI__) -# define __dead __volatile -# else -# define __dead -# endif -#endif - -#ifndef BSD4_4 -# define _BSD_VA_LIST_ va_list -extern char *strerror __P((int)); -extern int snprintf __P((char *, int, const char *, ...)); -#endif - -/* - * If you don't have setreuid, and you have saved uids, and you have - * a seteuid() call that doesn't try to emulate using setuid(), then - * you can try defining USE_SETEUID. - */ -#ifdef USE_SETEUID -# define setreuid(r, e) seteuid(e) -#endif - -#ifndef _PATH_LOCTMP -# define _PATH_LOCTMP "/tmp/local.XXXXXX" -#endif -#ifndef _PATH_MAILDIR -# define _PATH_MAILDIR "/var/spool/mail" -#endif - -#ifndef S_ISREG -# define S_ISREG(mode) (((mode) & _S_IFMT) == S_IFREG) -#endif +#include "pathnames.h" int eval = EX_OK; /* sysexits.h error value. */ -void deliver __P((int, char *)); +void deliver __P((int, char *, int)); void e_to_sys __P((int)); -__dead void err __P((const char *, ...)); +void err __P((const char *, ...)) __dead2; void notifybiff __P((char *)); int store __P((char *)); void usage __P((void)); @@ -142,28 +86,19 @@ main(argc, argv) char *argv[]; { struct passwd *pw; - int ch, fd; + int ch, fd, nobiff; uid_t uid; char *from; - extern char *optarg; - extern int optind; - - /* make sure we have some open file descriptors */ - for (fd = 10; fd < 30; fd++) - (void) close(fd); - - /* use a reasonable umask */ - (void) umask(0077); -#ifdef LOG_MAIL openlog("mail.local", 0, LOG_MAIL); -#else - openlog("mail.local", 0); -#endif from = NULL; - while ((ch = getopt(argc, argv, "df:r:")) != EOF) + nobiff = 0; + while ((ch = getopt(argc, argv, "bdf:r:")) != -1) switch(ch) { + case 'b': + nobiff++; + break; case 'd': /* Backward compatible. */ break; case 'f': @@ -204,7 +139,7 @@ main(argc, argv) * at the expense of repeated failures and multiple deliveries. */ for (fd = store(from); *argv; ++argv) - deliver(fd, *argv); + deliver(fd, *argv, nobiff); exit(eval); } @@ -215,15 +150,15 @@ store(from) FILE *fp; time_t tval; int fd, eline; - char line[2048]; - char tmpbuf[sizeof _PATH_LOCTMP + 1]; + char *tn, line[2048]; - strcpy(tmpbuf, _PATH_LOCTMP); - if ((fd = mkstemp(tmpbuf)) == -1 || (fp = fdopen(fd, "w+")) == NULL) { + tn = strdup(_PATH_LOCTMP); + if ((fd = mkstemp(tn)) == -1 || (fp = fdopen(fd, "w+")) == NULL) { e_to_sys(errno); err("unable to open temporary file"); } - (void)unlink(tmpbuf); + (void)unlink(tn); + free(tn); (void)time(&tval); (void)fprintf(fp, "From %s %s", from, ctime(&tval)); @@ -259,14 +194,13 @@ store(from) } void -deliver(fd, name) - int fd; +deliver(fd, name, nobiff) + int fd, nobiff; char *name; { struct stat fsb, sb; struct passwd *pw; int mbfd, nr, nw, off; - char *p; char biffmsg[100], buf[8*1024], path[MAXPATHLEN]; off_t curoff; @@ -280,25 +214,6 @@ deliver(fd, name) warn("unknown name: %s", name); return; } - endpwent(); - - /* - * Keep name reasonably short to avoid buffer overruns. - * This isn't necessary on BSD because of the proper - * definition of snprintf(), but it can cause problems - * on other systems. - * Also, clear out any bogus characters. - */ - - if (strlen(name) > 40) - name[40] = '\0'; - for (p = name; *p != '\0'; p++) - { - if (!isascii(*p)) - *p &= 0x7f; - else if (!isprint(*p)) - *p = '.'; - } (void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name); @@ -324,7 +239,6 @@ deliver(fd, name) * open(2) should support flock'ing the file. */ tryagain: - lockmbox(path); if (lstat(path, &sb)) { mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR); @@ -334,31 +248,28 @@ tryagain: } else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) { e_to_sys(errno); warn("chown %u.%u: %s", pw->pw_uid, pw->pw_gid, name); - goto err1; + return; } - } else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) { + } else if (sb.st_nlink != 1 || S_ISLNK(sb.st_mode)) { e_to_sys(errno); - warn("%s: irregular file", path); - goto err0; - } else if (sb.st_uid != pw->pw_uid) { - warn("%s: wrong ownership (%d)", path, sb.st_uid); - unlockmbox(); + warn("%s: linked file", path); return; } else { mbfd = open(path, O_APPEND|O_WRONLY, 0); if (mbfd != -1 && (fstat(mbfd, &fsb) || fsb.st_nlink != 1 || - !S_ISREG(fsb.st_mode) || sb.st_dev != fsb.st_dev || - sb.st_ino != fsb.st_ino || sb.st_uid != fsb.st_uid)) { + S_ISLNK(fsb.st_mode) || sb.st_dev != fsb.st_dev || + sb.st_ino != fsb.st_ino)) { warn("%s: file changed after open", path); - goto err1; + (void)close(mbfd); + return; } } if (mbfd == -1) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); - goto err0; + return; } /* Wait until we can get a lock on the file. */ @@ -368,11 +279,12 @@ tryagain: goto err1; } - /* Get the starting offset of the new message for biff. */ - curoff = lseek(mbfd, (off_t)0, SEEK_END); - (void)snprintf(biffmsg, sizeof(biffmsg), - sizeof curoff > sizeof(long) ? "%s@%qd\n" : "%s@%ld\n", - name, curoff); + if (!nobiff) { + /* Get the starting offset of the new message for biff. */ + curoff = lseek(mbfd, (off_t)0, SEEK_END); + (void)snprintf(biffmsg, sizeof(biffmsg), "%s@%qd\n", + name, curoff); + } /* Copy the message into the file. */ if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) { @@ -380,113 +292,39 @@ tryagain: warn("temporary file: %s", strerror(errno)); goto err1; } - if (setreuid(0, pw->pw_uid) < 0) { - e_to_sys(errno); - warn("setreuid(0, %d): %s (r=%d, e=%d)", - pw->pw_uid, strerror(errno), getuid(), geteuid()); - goto err1; - } -#ifdef DEBUG - printf("new euid = %d\n", geteuid()); -#endif while ((nr = read(fd, buf, sizeof(buf))) > 0) - for (off = 0; off < nr; off += nw) - if ((nw = write(mbfd, buf + off, nr - off)) < 0) { + for (off = 0; off < nr; nr -= nw, off += nw) + if ((nw = write(mbfd, buf + off, nr)) < 0) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); - goto err3; + goto err2; } if (nr < 0) { e_to_sys(errno); warn("temporary file: %s", strerror(errno)); - goto err3; +err2: (void)ftruncate(mbfd, curoff); +err1: (void)close(mbfd); + return; } +#ifndef DONT_FSYNC /* Flush to disk, don't wait for update. */ if (fsync(mbfd)) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); -err3: - if (setreuid(0, 0) < 0) { - e_to_sys(errno); - warn("setreuid(0, 0): %s", strerror(errno)); - } -#ifdef DEBUG - printf("reset euid = %d\n", geteuid()); -#endif -err2: (void)ftruncate(mbfd, curoff); -err1: (void)close(mbfd); -err0: unlockmbox(); - return; + goto err2; } - +#endif + /* Close and check -- NFS doesn't write until the close. */ if (close(mbfd)) { e_to_sys(errno); warn("%s: %s", path, strerror(errno)); - unlockmbox(); return; } - if (setreuid(0, 0) < 0) { - e_to_sys(errno); - warn("setreuid(0, 0): %s", strerror(errno)); - } -#ifdef DEBUG - printf("reset euid = %d\n", geteuid()); -#endif - unlockmbox(); - notifybiff(biffmsg); -} - -/* - * user.lock files are necessary for compatibility with other - * systems, e.g., when the mail spool file is NFS exported. - * Alas, mailbox locking is more than just a local matter. - * EPA 11/94. - */ - -char lockname[MAXPATHLEN]; -int locked = 0; - -lockmbox(path) - char *path; -{ - int statfailed = 0; - - if (locked) - return; - sprintf(lockname, "%s.lock", path); - for (;; sleep(5)) { - int fd; - struct stat st; - time_t now; - - fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT, 0); - if (fd >= 0) { - locked = 1; - close(fd); - return; - } - if (stat(lockname, &st) < 0) { - if (statfailed++ > 5) - return; - continue; - } - statfailed = 0; - time(&now); - if (now < st.st_ctime + 300) - continue; - unlink(lockname); - } -} - -unlockmbox() -{ - if (!locked) - return; - unlink(lockname); - locked = 0; + if (!nobiff) + notifybiff(biffmsg); } void @@ -508,7 +346,7 @@ notifybiff(msg) return; } addr.sin_family = hp->h_addrtype; - memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); + memmove(&addr.sin_addr, hp->h_addr, hp->h_length); addr.sin_port = sp->s_port; } if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { @@ -525,7 +363,7 @@ void usage() { eval = EX_USAGE; - err("usage: mail.local [-f from] user ..."); + err("usage: mail.local [-b] [-f from] user ..."); } #if __STDC__ @@ -587,17 +425,8 @@ vwarn(fmt, ap) (void)vfprintf(stderr, fmt, ap); (void)fprintf(stderr, "\n"); -#if !defined(ultrix) && !defined(__osf__) /* Log the message to syslog. */ vsyslog(LOG_ERR, fmt, ap); -#else - { - char fmtbuf[10240]; - - (void) sprintf(fmtbuf, fmt, ap); - syslog(LOG_ERR, "%s", fmtbuf); - } -#endif } /* @@ -682,7 +511,7 @@ e_to_sys(num) #ifdef ETIMEDOUT case ETIMEDOUT: /* Connection timed out */ #endif -#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK +#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: /* Operation would block. */ #endif eval = EX_TEMPFAIL; @@ -692,177 +521,3 @@ e_to_sys(num) break; } } - -#ifndef BSD4_4 - -# ifndef __osf__ -char * -strerror(eno) - int eno; -{ - extern int sys_nerr; - extern char *sys_errlist[]; - static char ebuf[60]; - - if (eno >= 0 && eno <= sys_nerr) - return sys_errlist[eno]; - (void) sprintf(ebuf, "Error %d", eno); - return ebuf; -} -# endif - -# if __STDC__ -snprintf(char *buf, int bufsiz, const char *fmt, ...) -# else -snprintf(buf, bufsiz, fmt, va_alist) - char *buf; - int bufsiz; - const char *fmt; - va_dcl -# endif -{ - va_list ap; - -# if __STDC__ - va_start(ap, fmt); -# else - va_start(ap); -# endif - vsprintf(buf, fmt, ap); - va_end(ap); -} - -#endif - -#ifdef ultrix - -/* - * Copyright (c) 1987, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; -#endif /* LIBC_SCCS and not lint */ - -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <errno.h> -#include <stdio.h> -#include <ctype.h> - -static int _gettemp(); - -mkstemp(path) - char *path; -{ - int fd; - - return (_gettemp(path, &fd) ? fd : -1); -} - -/* -char * -mktemp(path) - char *path; -{ - return(_gettemp(path, (int *)NULL) ? path : (char *)NULL); -} -*/ - -static -_gettemp(path, doopen) - char *path; - register int *doopen; -{ - extern int errno; - register char *start, *trv; - struct stat sbuf; - u_int pid; - - pid = getpid(); - for (trv = path; *trv; ++trv); /* extra X's get set to 0's */ - while (*--trv == 'X') { - *trv = (pid % 10) + '0'; - pid /= 10; - } - - /* - * check the target directory; if you have six X's and it - * doesn't exist this runs for a *very* long time. - */ - for (start = trv + 1;; --trv) { - if (trv <= path) - break; - if (*trv == '/') { - *trv = '\0'; - if (stat(path, &sbuf)) - return(0); - if (!S_ISDIR(sbuf.st_mode)) { - errno = ENOTDIR; - return(0); - } - *trv = '/'; - break; - } - } - - for (;;) { - if (doopen) { - if ((*doopen = - open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0) - return(1); - if (errno != EEXIST) - return(0); - } - else if (stat(path, &sbuf)) - return(errno == ENOENT ? 1 : 0); - - /* tricky little algorithm for backward compatibility */ - for (trv = start;;) { - if (!*trv) - return(0); - if (*trv == 'z') - *trv++ = 'a'; - else { - if (isdigit(*trv)) - *trv = 'a'; - else - ++*trv; - break; - } - } - } - /*NOTREACHED*/ -} - -#endif diff --git a/libexec/makekey/Makefile b/libexec/makekey/Makefile new file mode 100644 index 0000000..2ec0edd --- /dev/null +++ b/libexec/makekey/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 +# $Id$ + +PROG= makekey +MAN8= makekey.8 + +DPADD+= ${LIBCRYPT} +LDADD+= -lcrypt + +.include <bsd.prog.mk> diff --git a/libexec/kpasswdd/kpasswdd.8 b/libexec/makekey/makekey.8 index f6a401f..dc367bf 100644 --- a/libexec/kpasswdd/kpasswdd.8 +++ b/libexec/makekey/makekey.8 @@ -1,4 +1,4 @@ -.\" Copyright (c) 1990, 1993 +.\" Copyright (c) 1990, 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -29,32 +29,32 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)kpasswdd.8 8.1 (Berkeley) 6/9/93 +.\" @(#)makekey.8 8.2 (Berkeley) 12/11/93 +.\" $Id$ .\" -.Dd June 9, 1993 -.Dt KPASSWDD 8 +.Dd December 11, 1993 +.Dt MAKEKEY 8 .Os .Sh NAME -.Nm kpasswdd -.Nd Kerberos password changing daemon +.Nm makekey +.Nd make encrypted keys or passwords .Sh SYNOPSIS -.Nm kpasswdd +.Nm makekey .Sh DESCRIPTION -.Nm Kpasswdd -is the server for the -.Xr passwd 1 -program. -The server provides a remote password changing facility -with Kerberos authentication. -A user must provide the old Kerberos password, encrypted -in a random session key, to the server. -.Nm Kpasswdd -runs only on the Kerberos server, as it directly updates the -Kerberos database. +.Nm Makekey +encrypts a key and salt which it reads from the standard input +and writes the result to the standard output. +The key is expected to be +ten bytes; the salt is expected to be two bytes. +See +.Xr crypt 3 +for more information on what characters the key and salt can contain +and how the encrypted value is calculated. .Sh SEE ALSO -.Xr kerberos 1 , -.Xr passwd 1 +.Xr login 1 , +.Xr crypt 3 .Sh HISTORY -The -.Nm kpasswdd -utility first appeared in 4.4BSD. +A +.Nm +command appeared in +.At v7 . diff --git a/libexec/getty/ttydefaults.c b/libexec/makekey/makekey.c index 518f41b..482908d 100644 --- a/libexec/getty/ttydefaults.c +++ b/libexec/makekey/makekey.c @@ -29,26 +29,56 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id$ */ #ifndef lint -static char sccsid[] = "@(#)ttydefaults.c 8.1 (Berkeley) 6/4/93"; +static char copyright[] = +"@(#) Copyright (c) 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ -#include <sys/termios.h> +#ifndef lint +static char sccsid[] = "@(#)makekey.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> -#include "extern.h" +static void get __P((char *, int)); -void -set_ttydefaults(fd) - int fd; +int +main() { - struct termios term; - - tcgetattr(fd, &term); - term.c_iflag = TTYDEF_IFLAG; - term.c_oflag = TTYDEF_OFLAG; - term.c_lflag = TTYDEF_LFLAG; - term.c_cflag = TTYDEF_CFLAG; - tcsetattr(fd, TCSAFLUSH, &term); + int len; + char *r, key[9], salt[3]; + + get(key, sizeof(key) - 1); + get(salt, sizeof(salt) - 1); + len = strlen(r = crypt(key, salt)); + if (write(STDOUT_FILENO, r, len) != len) + err(1, "stdout"); + exit(0); +} + +static void +get(bp, len) + char *bp; + register int len; +{ + register int nr; + + bp[len] = '\0'; + if ((nr = read(STDIN_FILENO, bp, len)) == len) + return; + if (nr >= 0) + errno = EFTYPE; + err(1, "stdin"); } diff --git a/libexec/mknetid/Makefile b/libexec/mknetid/Makefile new file mode 100644 index 0000000..db5999b --- /dev/null +++ b/libexec/mknetid/Makefile @@ -0,0 +1,8 @@ +# $Id$ + +PROG= mknetid +SRCS= mknetid.c hash.c parse_group.c + +MAN8= mknetid.8 + +.include <bsd.prog.mk> diff --git a/libexec/mknetid/hash.c b/libexec/mknetid/hash.c new file mode 100644 index 0000000..f875ba1 --- /dev/null +++ b/libexec/mknetid/hash.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. + * + * $Id$ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include "hash.h" + +#ifndef lint +static const char rcsid[] = "$Id$"; +#endif + + +/* + * This hash function is stolen directly from the + * Berkeley DB package. It already exists inside libc, but + * it's declared static which prevents us from calling it + * from here. + */ +/* + * OZ's original sdbm hash + */ +u_int32_t +hash(keyarg, len) + const void *keyarg; + register size_t len; +{ + register const u_char *key; + register size_t loop; + register u_int32_t h; + +#define HASHC h = *key++ + 65599 * h + + h = 0; + key = keyarg; + if (len > 0) { + loop = (len + 8 - 1) >> 3; + + switch (len & (8 - 1)) { + case 0: + do { + HASHC; + /* FALLTHROUGH */ + case 7: + HASHC; + /* FALLTHROUGH */ + case 6: + HASHC; + /* FALLTHROUGH */ + case 5: + HASHC; + /* FALLTHROUGH */ + case 4: + HASHC; + /* FALLTHROUGH */ + case 3: + HASHC; + /* FALLTHROUGH */ + case 2: + HASHC; + /* FALLTHROUGH */ + case 1: + HASHC; + } while (--loop); + } + } + return (h); +} + +/* + * Generate a hash value for a given key (character string). + * We mask off all but the lower 8 bits since our table array + * can only hole 256 elements. + */ +u_int32_t hashkey(key) + char *key; +{ + + if (key == NULL) + return (-1); + return(hash((void *)key, strlen(key)) & HASH_MASK); +} + +/* Find an entry in the hash table (may be hanging off a linked list). */ +struct grouplist *lookup(table, key) + 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 an group member entry and/or update its grouplist. + */ +void mstore (table, key, gid, dup) + 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..5f6cd2e --- /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. + * + * $Id$ + */ + +/* 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 __P(( struct member_entry ** , char *, int, int )); +extern struct grouplist *lookup __P(( struct member_entry **, char * )); + diff --git a/libexec/mknetid/mknetid.8 b/libexec/mknetid/mknetid.8 new file mode 100644 index 0000000..5b7ff6d --- /dev/null +++ b/libexec/mknetid/mknetid.8 @@ -0,0 +1,141 @@ +.\" 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. +.\" +.\" $Id$ +.\" +.Dd June 23, 1996 +.Dt MKNETID 8 +.Os +.Sh NAME +.Nm mknetid +.Nd "generate netid map data" +.Sh SYNOPSIS +.Nm mknetid +.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 +.Nm Mknetid +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 +NIS map. This map is used to hold credential information for both users +and hosts in a operating system independent format. +.Pp +The +.Nm mknetid +command checks for duplicate occurances of netids and filters +them out. +.Pp +The +.Nm mknetid +command prints its results on the standard output. It is usually called +only by +.Nm /var/yp/Makefile +when rebuilding the NIS maps. +.Pp +.Sh OPTIONS +The +.Nm mknetid +command supports the following options: +.Bl -tag -width flag +.It Fl q +Normally, +.Nm mknetid +prints a warning message when it encounters a duplicate netid. +This flag turns on 'quiet' mode, allowing the warnings to be +surpressed. Other error messages may still be generated. +.It Fl g Ar group-file +The +.Fl g +flag can be used to specify the location of the group information +file. The compiled-in default is +.Pa /etc/group . +.It Fl p Ar passwd-file +The +.Fl p +flag can be used to specify the location of the passwd information +file. The compiled-in default is +.Pa /etc/passwd . +.It Fl h Ar group-file +The +.Fl h +flag can be used to specify the location of the hosts database +file. The compiled-in default is +.Pa /etc/hosts . +.It Fl n Ar netid-file +The +.Fl n +flag can be used to 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 can't be +found. The netid database is not likely to be present on most systems +until Secure RPC support is added to FreeBSD. +.It Fl d Ar domain +By default, the +.Nm mknetid +command uses the system domainname when generating netid records. If +the system domainnameis 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 Pa -compact +.It Pa /var/yp/Makefile +The Makefile that calls +.Nm yp_mkdb +and +.Nm mknetid +to build the 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 4 , +.Xr yp_mkdb 8 +.Sh AUTHOR +Bill Paul <wpaul@ctr.columbia.edu> diff --git a/libexec/mknetid/mknetid.c b/libexec/mknetid/mknetid.c new file mode 100644 index 0000000..36bbfc4 --- /dev/null +++ b/libexec/mknetid/mknetid.c @@ -0,0 +1,291 @@ +/* + * 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 + * + * $Id: mknetid.c,v 1.6 1997/02/22 14:21:54 peter Exp $ + */ + +#include <sys/types.h> + +#include <rpc/rpc.h> +#include <rpcsvc/yp_prot.h> +#include <rpcsvc/ypclnt.h> + +#include <err.h> +#include <grp.h> +#include <pwd.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "hash.h" + +#ifndef lint +static const char rcsid[] = "$Id: mknetid.c,v 1.6 1997/02/22 14:21:54 peter Exp $"; +#endif + +#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 __P(( void )); +extern int _setgrent __P(( void )); +extern void _endgrent __P(( void )); +void usage(prog) +char *prog; +{ + fprintf (stderr,"usage: %s [-q] [-g group file] [-p passwd file] \ +[-h hosts file]\n\t\t\t[-d netid file] [-d domain]\n",prog); + exit(1); +} + +extern char *optarg; +extern FILE *_gr_fp; + +main(argc, argv) + 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; + + 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(argv[0]); + 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)) { + if ((ptr = strchr(readbuf, ':')) == NULL) + warnx("bad passwd file entry: %s", readbuf); + *ptr = '\0'; + ptr++; + if ((ptr = strchr(ptr, ':')) == NULL) + warnx("bad passwd file entry: %s", readbuf); + *ptr = '\0'; + ptr++; + pidptr = ptr; + if ((ptr = strchr(ptr, ':')) == NULL) + warnx("bad passwd file entry: %s", readbuf); + *ptr = '\0'; + ptr++; + gidptr = ptr; + if ((ptr = strchr(ptr, ':')) == NULL) + warnx("bad passwd file entry: %s", readbuf); + *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", 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/parse_group.c b/libexec/mknetid/parse_group.c new file mode 100644 index 0000000..e5fd718 --- /dev/null +++ b/libexec/mknetid/parse_group.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)getgrent.c 8.2 (Berkeley) 3/21/94"; +#endif /* LIBC_SCCS and not lint */ + +#ifndef lint +static const char rcsid[] = "$Id: parse_group.c,v 1.3 1997/02/22 14:21:54 peter Exp $"; +#endif + +/* + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <grp.h> + +FILE *_gr_fp; +static struct group _gr_group; +static int _gr_stayopen; +static int grscan(), start_gr(); + +#define MAXGRP 200 +static char *members[MAXGRP]; +#define MAXLINELENGTH 1024 +static char line[MAXLINELENGTH]; + +struct group * +_getgrent() +{ + if (!_gr_fp && !start_gr()) { + return NULL; + } + + + if (!grscan(0, 0, NULL)) + return(NULL); + return(&_gr_group); +} + +static int +start_gr() +{ + return 1; +} + +int +_setgroupent(stayopen) + int stayopen; +{ + if (!start_gr()) + return(0); + _gr_stayopen = stayopen; + return(1); +} + +int +_setgrent() +{ + return(_setgroupent(0)); +} + +void +_endgrent() +{ + if (_gr_fp) { + (void)fclose(_gr_fp); + _gr_fp = NULL; + } +} + +static int +grscan(search, gid, name) + register int search, gid; + register char *name; +{ + register char *cp, **m; + char *bp; + for (;;) { + if (!fgets(line, sizeof(line), _gr_fp)) + return(0); + bp = line; + /* skip lines that are too big */ + if (!index(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 (search && name) { + if(strcmp(_gr_group.gr_name, name)) { + 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 && name == NULL && _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/named-xfer/Makefile b/libexec/named-xfer/Makefile new file mode 100644 index 0000000..a02cb03 --- /dev/null +++ b/libexec/named-xfer/Makefile @@ -0,0 +1,20 @@ +# $Id$ + +.include "${.CURDIR}/../../usr.sbin/named/Makefile.inc" + +.PATH: ${BIND_DIR}/named +.PATH: ${BIND_DIR}/man + +PROG= named-xfer +SRCS= named-xfer.c db_glue.c storage.c version.c +MAN8= named-xfer.8 + +CLEANFILES+= version.c + +version.c: Version.c ${BIND_DIR}/Makefile + (u=$${USER-root} d=`pwd` h=`hostname` t=`LC_TIME=C date`; \ + sed -e "s|%WHEN%|$${t}|" -e "s|%VERSION%|"${VER}"|" \ + -e "s|%WHOANDWHERE%|$${u}@$${h}:$${d}|" \ + < ${BIND_DIR}/named/Version.c > version.c) + +.include <bsd.prog.mk> diff --git a/libexec/rbootd/Makefile b/libexec/rbootd/Makefile new file mode 100644 index 0000000..0cc5ac2 --- /dev/null +++ b/libexec/rbootd/Makefile @@ -0,0 +1,8 @@ +# from: @(#)Makefile 8.1 (Berkeley) 6/4/93 +# $Id: Makefile,v 1.7 1997/02/22 14:21:57 peter Exp $ + +PROG= rbootd +SRCS= bpf.c conf.c parseconf.c rbootd.c rmpproto.c utils.c +MAN8= rbootd.8 + +.include <bsd.prog.mk> diff --git a/libexec/rbootd/bpf.c b/libexec/rbootd/bpf.c new file mode 100644 index 0000000..ddeea4f --- /dev/null +++ b/libexec/rbootd/bpf.c @@ -0,0 +1,419 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)bpf.c 8.1 (Berkeley) 6/4/93 + * $Id: bpf.c,v 1.7 1997/06/29 19:00:01 steve Exp $ + * + * From: Utah Hdr: bpf.c 3.1 92/07/06 + * Author: Jeff Forys, University of Utah CSS + */ + +#ifndef lint +static const char sccsid[] = "@(#)bpf.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <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() +{ + 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(errmsg) + char **errmsg; +{ + struct ifreq ibuf[8], *ifrp, *ifend, *mp; + struct ifconf ifc; + int fd; + int minunit, n; + char *cp; + static char device[sizeof(ifrp->ifr_name)]; + static char errbuf[128] = "No Error!"; + + if (errmsg != NULL) + *errmsg = errbuf; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + (void) strcpy(errbuf, "bpf: socket: %m"); + return(NULL); + } + ifc.ifc_len = sizeof ibuf; + ifc.ifc_buf = (caddr_t)ibuf; + +#ifdef OSIOCGIFCONF + if (ioctl(fd, OSIOCGIFCONF, (char *)&ifc) < 0 || + ifc.ifc_len < sizeof(struct ifreq)) { + (void) strcpy(errbuf, "bpf: ioctl(OSIOCGIFCONF): %m"); + return(NULL); + } +#else + if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 || + ifc.ifc_len < sizeof(struct ifreq)) { + (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFCONF): %m"); + return(NULL); + } +#endif + ifrp = ibuf; + ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len); + + mp = 0; + minunit = 666; + for (; ifrp < ifend; ++ifrp) { + if (ioctl(fd, SIOCGIFFLAGS, (char *)ifrp) < 0) { + (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFFLAGS): %m"); + return(NULL); + } + + /* + * If interface is down or this is the loopback interface, + * ignore it. + */ + if ((ifrp->ifr_flags & IFF_UP) == 0 || +#ifdef IFF_LOOPBACK + (ifrp->ifr_flags & IFF_LOOPBACK)) +#else + (strcmp(ifrp->ifr_name, "lo0") == 0)) +#endif + continue; + + for (cp = ifrp->ifr_name; !isdigit(*cp); ++cp) + ; + n = atoi(cp); + if (n < minunit) { + minunit = n; + mp = ifrp; + } + } + + (void) close(fd); + if (mp == 0) { + (void) strcpy(errbuf, "bpf: no interfaces found"); + return(NULL); + } + + (void) strcpy(device, mp->ifr_name); + return(device); +} + +/* +** BpfRead -- Read packets from a BPF device and fill in `rconn'. +** +** Parameters: +** rconn - filled in with next packet. +** doread - is True if we can issue a read() syscall. +** +** Returns: +** True if `rconn' contains a new packet, False otherwise. +** +** Side Effects: +** None. +*/ +int +BpfRead(rconn, doread) + RMPCONN *rconn; + int doread; +{ + 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(rconn) + RMPCONN *rconn; +{ + if (write(BpfFd, (char *)&rconn->rmp, rconn->rmplen) < 0) { + syslog(LOG_ERR, "write: %s: %m", EnetStr(rconn)); + return(0); + } + + return(1); +} + +/* +** BpfClose -- Close a BPF device. +** +** Parameters: +** None. +** +** Returns: +** Nothing. +** +** Side Effects: +** None. +*/ +void +BpfClose() +{ + struct ifreq ifr; + + if (BpfPkt != NULL) { + free((char *)BpfPkt); + BpfPkt = NULL; + } + + if (BpfFd == -1) + return; + +#ifdef MSG_EOR + ifr.ifr_addr.sa_len = RMP_ADDRLEN + 2; +#endif + ifr.ifr_addr.sa_family = AF_UNSPEC; + 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..58da9f0 --- /dev/null +++ b/libexec/rbootd/conf.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)conf.c 8.1 (Berkeley) 6/4/93 + * $Id$ + * + * From: Utah Hdr: conf.c 3.1 92/07/06 + * Author: Jeff Forys, University of Utah CSS + */ + +#ifndef lint +static const char sccsid[] = "@(#)conf.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> + +#include <stdio.h> +#include "defs.h" +#include "pathnames.h" + +/* +** Define (and possibly initialize) global variables here. +** +** Caveat: +** The maximum number of bootable files (`char *BootFiles[]') is +** limited to C_MAXFILE (i.e. the maximum number of files that +** can be spec'd in the configuration file). This was done to +** simplify the boot file search code. +*/ + +char MyHost[MAXHOSTNAMELEN+1]; /* 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..6edde9b --- /dev/null +++ b/libexec/rbootd/defs.h @@ -0,0 +1,184 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * 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 + */ + +#include "rmp.h" +#include "rmp_var.h" + +/* +** Common #define's and external variables. All other files should +** include this. +*/ + +/* + * This may be defined in <sys/param.h>, if not, it's defined here. + */ +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 +#endif + +/* + * SIGUSR1 and SIGUSR2 are defined in <signal.h> for 4.3BSD systems. + */ +#ifndef SIGUSR1 +#define SIGUSR1 SIGEMT +#endif +#ifndef SIGUSR2 +#define SIGUSR2 SIGFPE +#endif + +/* + * These can be faster & more efficient than strcmp()/strncmp()... + */ +#define STREQN(s1,s2) ((*s1 == *s2) && (strcmp(s1,s2) == 0)) +#define STRNEQN(s1,s2,n) ((*s1 == *s2) && (strncmp(s1,s2,n) == 0)) + +/* + * Configuration file limitations. + */ +#define C_MAXFILE 10 /* max number of boot-able files */ +#define C_LINELEN 1024 /* max length of line */ + +/* + * Direction of packet (used as argument to DispPkt). + */ +#define DIR_RCVD 0 +#define DIR_SENT 1 +#define DIR_NONE 2 + +/* + * These need not be functions, so... + */ +#define FreeStr(str) free(str) +#define FreeClient(cli) free(cli) +#define GenSessID() (++SessionID ? SessionID: ++SessionID) + +/* + * Converting an Ethernet address to a string is done in many routines. + * Using `rmp.hp_hdr.saddr' works because this field is *never* changed; + * it will *always* contain the source address of the packet. + */ +#define EnetStr(rptr) GetEtherAddr(&(rptr)->rmp.hp_hdr.saddr[0]) + +/* + * Every machine we can boot will have one of these allocated for it + * (unless there are no restrictions on who we can boot). + */ +typedef struct client_s { + u_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 __P((RMPCONN *)); +int BootDone __P((RMPCONN *)); +void BpfClose __P((void)); +char *BpfGetIntfName __P((char **)); +int BpfOpen __P((void)); +int BpfRead __P((RMPCONN *, int)); +int BpfWrite __P((RMPCONN *)); +void DebugOff __P((int)); +void DebugOn __P((int)); +void DispPkt __P((RMPCONN *, int)); +void DoTimeout __P((void)); +void DspFlnm __P((u_int, char *)); +void Exit __P((int)); +CLIENT *FindClient __P((RMPCONN *)); +RMPCONN *FindConn __P((RMPCONN *)); +void FreeClients __P((void)); +void FreeConn __P((RMPCONN *)); +void FreeConns __P((void)); +int GetBootFiles __P((void)); +char *GetEtherAddr __P((u_int8_t *)); +CLIENT *NewClient __P((u_int8_t *)); +RMPCONN *NewConn __P((RMPCONN *)); +char *NewStr __P((char *)); +u_int8_t *ParseAddr __P((char *)); +int ParseConfig __P((void)); +void ProcessPacket __P((RMPCONN *, CLIENT *)); +void ReConfig __P((int)); +void RemoveConn __P((RMPCONN *)); +int SendBootRepl __P((struct rmp_packet *, RMPCONN *, char *[])); +int SendFileNo __P((struct rmp_packet *, RMPCONN *, char *[])); +int SendPacket __P((RMPCONN *)); +int SendReadRepl __P((RMPCONN *)); +int SendServerID __P((RMPCONN *)); diff --git a/libexec/rbootd/parseconf.c b/libexec/rbootd/parseconf.c new file mode 100644 index 0000000..4950e4f --- /dev/null +++ b/libexec/rbootd/parseconf.c @@ -0,0 +1,359 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)parseconf.c 8.1 (Berkeley) 6/4/93 + * $Id: parseconf.c,v 1.6 1997/06/29 19:00:08 steve Exp $ + * + * From: Utah Hdr: parseconf.c 3.1 92/07/06 + * Author: Jeff Forys, University of Utah CSS + */ + +#ifndef lint +static const char sccsid[] = "@(#)parseconf.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <dirent.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include "defs.h" + +/* +** ParseConfig -- parse the config file into linked list of clients. +** +** Parameters: +** None. +** +** Returns: +** 1 on success, 0 otherwise. +** +** Side Effects: +** - Linked list of clients will be (re)allocated. +** +** Warnings: +** - GetBootFiles() must be called before this routine +** to create a linked list of default boot files. +*/ +int +ParseConfig() +{ + FILE *fp; + CLIENT *client; + u_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: cant parse <%s>", + linecnt, bcp); + continue; + } + + if ((client = NewClient(addr)) == NULL) /* alloc new client */ + continue; + + GETSTR; /* get first file */ + + /* + * If no boot files are spec'd, use the default list. + * Otherwise, validate each file (`bcp') against the + * list of boot-able files. + */ + i = 0; + if (bcp == cp) /* no files spec'd */ + for (; i < C_MAXFILE && BootFiles[i] != NULL; i++) + client->files[i] = BootFiles[i]; + else { + do { + /* + * For each boot file spec'd, make sure it's + * in our list. If so, include a pointer to + * it in the CLIENT's list of boot files. + */ + for (j = 0; ; j++) { + if (j==C_MAXFILE||BootFiles[j]==NULL) { + syslog(LOG_ERR, "ParseConfig: line %d: no boot file (%s)", + linecnt, bcp); + break; + } + if (STREQN(BootFiles[j], bcp)) { + if (i < C_MAXFILE) + client->files[i++] = + BootFiles[j]; + else + syslog(LOG_ERR, "ParseConfig: line %d: too many boot files (%s)", + linecnt, bcp); + break; + } + } + GETSTR; /* get next file */ + } while (bcp != cp); + + /* + * Restricted list of boot files were spec'd, + * however, none of them were found. Since we + * apparently cant let them boot "just anything", + * the entire record is invalidated. + */ + if (i == 0) { + FreeClient(client); + continue; + } + } + + /* + * Link this client into the linked list of clients. + * SIGHUP has already been blocked. + */ + if (Clients) + client->next = Clients; + Clients = client; + } + + (void) fclose(fp); /* close config file */ + + (void) sigsetmask(omask); /* reset signal mask */ + + return(1); /* return success */ +} + +/* +** ParseAddr -- Parse a string containing an RMP address. +** +** This routine is fairly liberal at parsing an RMP address. The +** address must contain 6 octets consisting of between 0 and 2 hex +** chars (upper/lower case) separated by colons. If two colons are +** together (e.g. "::", the octet between them is recorded as being +** zero. Hence, the following addrs are all valid and parse to the +** same thing: +** +** 08:00:09:00:66:ad 8::9:0:66:AD 8::9::66:aD +** +** For clarity, an RMP address is really an Ethernet address, but +** since the HP boot code uses IEEE 802.3, it's really an IEEE +** 802.3 address. Of course, all of these are identical. +** +** Parameters: +** str - string representation of an RMP address. +** +** Returns: +** pointer to a static array of RMP_ADDRLEN bytes. +** +** Side Effects: +** None. +** +** Warnings: +** - The return value points to a static buffer; it must +** be copied if it's to be saved. +*/ +u_int8_t * +ParseAddr(str) + 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() +{ + 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) /* cant find any boot files */ + syslog(LOG_ERR, "GetBootFiles: no boot files (%s)\n", BootDir); + + return(i); +} diff --git a/libexec/bugfiler/extern.h b/libexec/rbootd/pathnames.h index b096f3d..56b9deb 100644 --- a/libexec/bugfiler/extern.h +++ b/libexec/rbootd/pathnames.h @@ -1,7 +1,15 @@ -/*- - * Copyright (c) 1993 +/* + * 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: @@ -30,12 +38,14 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)extern.h 8.1 (Berkeley) 6/4/93 + * 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 */ -void error __P((char *, char *)); -void gethead __P((int)); -int process __P((void)); -void redist __P((void)); -void reply __P((void)); -void seterr __P((void)); +#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..23c37c5 --- /dev/null +++ b/libexec/rbootd/rbootd.8 @@ -0,0 +1,156 @@ +.\" Copyright (c) 1988, 1992 The University of Utah and the Center +.\" for Software Science (CSS). +.\" Copyright (c) 1992, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Center for Software Science of the University of Utah Computer +.\" Science Department. CSS requests users of this software to return +.\" to css-dist@cs.utah.edu any improvements that they make and grant +.\" CSS redistribution rights. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)rbootd.8 8.2 (Berkeley) 12/11/93 +.\" $Id: rbootd.8,v 1.6 1997/06/23 04:02:13 steve Exp $ +.\" +.\" Utah Hdr: rbootd.man 3.1 92/07/06 +.\" Author: Jeff Forys, University of Utah CSS +.\" +.Dd December 11, 1993 +.Dt RBOOTD 8 +.Os +.Sh NAME +.Nm rbootd +.Nd HP remote boot server +.Sh SYNOPSIS +.Nm rbootd +.Op Fl ad +.Op Fl i Ar interface +.Op config_file +.Sh DESCRIPTION +The +.Nm rbootd +utility services boot requests from Hewlett-Packard workstations over a +local area network. +All boot files must reside in the boot file directory; further, if a +client supplies path information in its boot request, it will be silently +stripped away before processing. +By default, +.Nm rbootd +only responds to requests from machines listed in its configuration file. +.Pp +The options are as follows: +.Bl -tag -width Fl +.It Fl a +Respond to boot requests from any machine. +The configuration file is ignored if this option is specified. +.It Fl d +Run +.Nm rbootd +in debug mode. +Packets sent and received are displayed to the terminal. +.It Fl i Ar interface +Service boot requests on specified interface. +If unspecified, +.Nm rbootd +searches the system interface list for the lowest numbered, configured +``up'' interface (excluding loopback). +Ties are broken by choosing the earliest match. +.El +.Pp +Specifying +.Ar config_file +on the command line causes +.Nm rbootd +to use a different configuration file from the default. +.Pp +The configuration file is a text file where each line describes a particular +machine. +A line must start with a machine's Ethernet address followed by an optional +list of boot file names. +An Ethernet address is specified in hexadecimal with each of its six octets +separated by a colon. +The boot file names come from the boot file directory. +The ethernet address and boot file(s) must be separated by white-space +and/or comma characters. +A pound sign causes the remainder of a line to be ignored. +.Pp +Here is a sample configuration file: +.Bl -column 08:00:09:0:66:ad SYSHPBSD,SYSHPUX "# vandy (anything)" +.It # +.It # ethernet addr boot file(s) comments +.It # +.It 08:00:09:0:66:ad SYSHPBSD # snake (4.3BSD) +.It 08:00:09:0:59:5b # vandy (anything) +.It 8::9:1:C6:75 SYSHPBSD,SYSHPUX # jaguar (either) +.El +.Pp +.Nm Rbootd +logs status and error messages via +.Xr syslog 3 . +A startup message is always logged, and in the case of fatal errors (or +deadly signals) a message is logged announcing the server's termination. +In general, a non-fatal error is handled by ignoring the event that caused +it (e.g. an invalid Ethernet address in the config file causes that line +to be invalidated). +.Pp +The following signals have the specified effect when sent to the server +process using the +.Xr kill 1 +command: +.Bl -tag -width SIGUSR1 -offset -compact +.It SIGHUP +Drop all active connections and reconfigure. +.It SIGUSR1 +Turn on debugging, do nothing if already on. +.It SIGUSR2 +Turn off debugging, do nothing if already off. +.El +.Sh "FILES" +.Bl -tag -width /usr/libexec/rbootd -compact +.It /dev/bpf# +packet-filter device +.It /etc/rbootd.conf +configuration file +.It /tmp/rbootd.dbg +debug output +.It /usr/mdec/rbootd +directory containing boot files +.It /var/run/rbootd.pid +process id +.El +.Sh SEE ALSO +.Xr kill 1 , +.Xr socket 2 , +.Xr signal 3 , +.Xr syslog 3 +.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..0129c6d --- /dev/null +++ b/libexec/rbootd/rbootd.c @@ -0,0 +1,447 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)rbootd.c 8.1 (Berkeley) 6/4/93 + * $Id: rbootd.c,v 1.7 1997/06/29 19:00:15 steve Exp $ + * + * 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 +static const char sccsid[] = "@(#)rbootd.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#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" + +extern char *__progname; /* from crt0.o */ + +int +main(argc, argv) + 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; + } + for (; optind < argc; optind++) { + if (ConfigFile == NULL) + ConfigFile = argv[optind]; + else { + warnx("too many config files (`%s' ignored)\n", + 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(__progname, 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) { + syslog(LOG_NOTICE, "restarted (??)"); + syslog(LOG_ERR, errmsg); + Exit(0); + } + } + + syslog(LOG_NOTICE, "restarted (%s)", IntfName); + + (void) signal(SIGHUP, ReConfig); + (void) signal(SIGINT, Exit); + (void) signal(SIGTERM, Exit); + + /* + * Grab our host name and pid. + */ + if (gethostname(MyHost, MAXHOSTNAMELEN) < 0) { + syslog(LOG_ERR, "gethostname: %m"); + Exit(0); + } + MyHost[MAXHOSTNAMELEN] = '\0'; + + MyPid = getpid(); + + /* + * Write proc's pid to a file. + */ + { + FILE *fp; + + if ((fp = fopen(PidFile, "w")) != NULL) { + (void) fprintf(fp, "%d\n", (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 isnt 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); + } + } + } +} + +/* +** DoTimeout -- Free any connections that have timed out. +** +** Parameters: +** None. +** +** Returns: +** Nothing. +** +** Side Effects: +** - Timed out connections in `RmpConns' will be freed. +*/ +void +DoTimeout() +{ + RMPCONN *rtmp; + struct timeval now; + + (void) gettimeofday(&now, (struct timezone *)0); + + /* + * For each active connection, if RMP_TIMEOUT seconds have passed + * since the last packet was sent, delete the connection. + */ + for (rtmp = RmpConns; rtmp != NULL; rtmp = rtmp->next) + if ((rtmp->tstamp.tv_sec + RMP_TIMEOUT) < now.tv_sec) { + syslog(LOG_WARNING, "%s: connection timed out (%u)", + EnetStr(rtmp), rtmp->rmp.r_type); + RemoveConn(rtmp); + } +} + +/* +** FindClient -- Find client associated with a packet. +** +** Parameters: +** rconn - the new packet. +** +** Returns: +** Pointer to client info if found, NULL otherwise. +** +** Side Effects: +** None. +** +** Warnings: +** - This routine must be called with SIGHUP blocked since +** a reconfigure can invalidate the information returned. +*/ + +CLIENT * +FindClient(rconn) + 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(sig) + int sig; +{ + if (sig > 0) + syslog(LOG_ERR, "going down on signal %d", sig); + else + syslog(LOG_ERR, "going down with fatal error"); + BpfClose(); + exit(1); +} + +/* +** ReConfig -- Get new list of boot files and reread config files. +** +** Parameters: +** None. +** +** Returns: +** Nothing. +** +** Side Effects: +** - All active connections are dropped. +** - List of boot-able files is changed. +** - List of clients is changed. +** +** Warnings: +** - This routine must be called with SIGHUP blocked. +*/ +void +ReConfig(signo) + int signo; +{ + syslog(LOG_NOTICE, "reconfiguring boot server"); + + FreeConns(); + + if (GetBootFiles() == 0) + Exit(0); + + if (ParseConfig() == 0) + Exit(0); +} + +/* +** DebugOff -- Turn off debugging. +** +** Parameters: +** None. +** +** Returns: +** Nothing. +** +** Side Effects: +** - Debug file is closed. +*/ +void +DebugOff(signo) + int signo; +{ + if (DbgFp != NULL) + (void) fclose(DbgFp); + + DbgFp = NULL; +} + +/* +** DebugOn -- Turn on debugging. +** +** Parameters: +** None. +** +** Returns: +** Nothing. +** +** Side Effects: +** - Debug file is opened/truncated if not already opened, +** otherwise do nothing. +*/ +void +DebugOn(signo) + int signo; +{ + if (DbgFp == NULL) { + if ((DbgFp = fopen(DbgFile, "w")) == NULL) + syslog(LOG_ERR, "can't open debug file (%s)", DbgFile); + } +} diff --git a/libexec/rbootd/rmp.h b/libexec/rbootd/rmp.h new file mode 100644 index 0000000..c4285c9 --- /dev/null +++ b/libexec/rbootd/rmp.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * 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 + */ + +/* + * 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..3a9c7f4 --- /dev/null +++ b/libexec/rbootd/rmp_var.h @@ -0,0 +1,244 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * 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 + */ + +/* + * 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..31102e8 --- /dev/null +++ b/libexec/rbootd/rmpproto.c @@ -0,0 +1,600 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)rmpproto.c 8.1 (Berkeley) 6/4/93 + * $Id: rmpproto.c,v 1.2 1997/06/29 19:00:24 steve Exp $ + * + * From: Utah Hdr: rmpproto.c 3.1 92/07/06 + * Author: Jeff Forys, University of Utah CSS + */ + +#ifndef lint +static const char sccsid[] = "@(#)rmpproto.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include "defs.h" + +/* +** ProcessPacket -- determine packet type and do what's required. +** +** An RMP BOOT packet has been received. Look at the type field +** and process Boot Requests, Read Requests, and Boot Complete +** packets. Any other type will be dropped with a warning msg. +** +** Parameters: +** rconn - the new connection +** client - list of files available to this host +** +** Returns: +** Nothing. +** +** Side Effects: +** - If this is a valid boot request, it will be added to +** the linked list of outstanding requests (RmpConns). +** - If this is a valid boot complete, its associated +** entry in RmpConns will be deleted. +** - Also, unless we run out of memory, a reply will be +** sent to the host that sent the packet. +*/ +void +ProcessPacket(rconn, client) + RMPCONN *rconn; + CLIENT *client; +{ + struct rmp_packet *rmp; + RMPCONN *rconnout; + + rmp = &rconn->rmp; /* cache pointer to RMP packet */ + + switch(rmp->r_type) { /* do what we came here to do */ + case RMP_BOOT_REQ: /* boot request */ + if ((rconnout = NewConn(rconn)) == NULL) + return; + + /* + * If the Session ID is 0xffff, this is a "probe" + * packet and we do not want to add the connection + * to the linked list of active connections. There + * are two types of probe packets, if the Sequence + * Number is 0 they want to know our host name, o/w + * they want the name of the file associated with + * the number spec'd by the Sequence Number. + * + * If this is an actual boot request, open the file + * and send a reply. If SendBootRepl() does not + * return 0, add the connection to the linked list + * of active connections, otherwise delete it since + * an error was encountered. + */ + if (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(rconn) + 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(req, rconn, filelist) + 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(req, rconn, filelist) + 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(rconn) + RMPCONN *rconn; +{ + int retval = 0; + RMPCONN *oldconn; + struct rmp_packet *rpl, *req; + int size = 0; + int madeconn = 0; + + /* + * Find the old connection. If one doesnt exist, create one only + * to return the error code. + */ + if ((oldconn = FindConn(rconn)) == NULL) { + if ((oldconn = NewConn(rconn)) == NULL) + return(0); + syslog(LOG_ERR, "SendReadRepl: no active connection (%s)", + EnetStr(rconn)); + madeconn++; + } + + req = &rconn->rmp; /* cache ptr to request packet */ + rpl = &oldconn->rmp; /* cache ptr to reply packet */ + + if (madeconn) { /* no active connection above; abort */ + rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; + retval = 1; + goto sendpkt; + } + + /* + * Make sure Session ID's match. + */ + if (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, L_SET) < 0) { + syslog(LOG_ERR, "SendReadRepl: lseek: %m (%s)", + EnetStr(rconn)); + rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; + retval = 1; + goto sendpkt; + } + + /* + * Read data directly into reply packet. + */ + if ((size = read(oldconn->bootfd, &rpl->r_rrpl.rmp_data, + (int) 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(rconn) + RMPCONN *rconn; +{ + RMPCONN *oldconn; + struct rmp_packet *rpl; + + /* + * If we cant find the connection, ignore the request. + */ + if ((oldconn = FindConn(rconn)) == NULL) { + syslog(LOG_ERR, "BootDone: no existing connection (%s)", + EnetStr(rconn)); + return(0); + } + + rpl = &oldconn->rmp; /* cache ptr to RMP packet */ + + /* + * Make sure Session ID's match. + */ + if (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(rconn) + 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); +#ifdef __FreeBSD__ + /* BPF (incorrectly) wants this in host order. */ + rconn->rmp.hp_hdr.len = rconn->rmplen - sizeof(struct hp_hdr); +#else + rconn->rmp.hp_hdr.len = htons(rconn->rmplen - sizeof(struct hp_hdr)); +#endif + + /* + * 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, (struct timezone *)0); + + if (DbgFp != NULL) /* display packet */ + DispPkt(rconn,DIR_SENT); + + /* + * Send RMP packet to remote host. + */ + return(BpfWrite(rconn)); +} diff --git a/libexec/rbootd/utils.c b/libexec/rbootd/utils.c new file mode 100644 index 0000000..aadb5a9 --- /dev/null +++ b/libexec/rbootd/utils.c @@ -0,0 +1,556 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)utils.c 8.1 (Berkeley) 6/4/93 + * $Id: utils.c,v 1.2 1997/06/29 19:00:29 steve Exp $ + * + * From: Utah Hdr: utils.c 3.1 92/07/06 + * Author: Jeff Forys, University of Utah CSS + */ + +#ifndef lint +static const char sccsid[] = "@(#)utils.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> + +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> +#include "defs.h" + +/* +** DispPkt -- Display the contents of an RMPCONN packet. +** +** Parameters: +** rconn - packet to be displayed. +** direct - direction packet is going (DIR_*). +** +** Returns: +** Nothing. +** +** Side Effects: +** None. +*/ +void +DispPkt(rconn, direct) + RMPCONN *rconn; + int direct; +{ + static char BootFmt[] = "\t\tRetCode:%u SeqNo:%lx SessID:%x Vers:%u"; + static char ReadFmt[] = "\t\tRetCode:%u Offset:%lx SessID:%x\n"; + + struct tm *tmp; + 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: %d\n", + rconn->rmplen - RMPREADSIZE(0)); + break; + case RMP_BOOT_DONE: /* boot complete */ + (void) fprintf(DbgFp, "\tBoot Complete:\n"); + (void) fprintf(DbgFp, "\t\tRetCode:%u SessID:%x\n", + rmp->r_done.rmp_retcode, + 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(addr) + 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(size, flnm) + 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(addr) + 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() +{ + CLIENT *ctmp; + + while (Clients != NULL) { + ctmp = Clients; + Clients = Clients->next; + FreeClient(ctmp); + } +} + +/* +** NewStr -- allocate memory for a character array. +** +** Parameters: +** str - null terminated character array. +** +** Returns: +** Ptr to new character array or NULL if we ran out of memory. +** +** Side Effects: +** - Memory will be malloc'd for the new character array. +** - If malloc() fails, a log message will be generated. +*/ +char * +NewStr(str) + char *str; +{ + char *stmp; + + if ((stmp = (char *)malloc((unsigned) (strlen(str)+1))) == NULL) { + syslog(LOG_ERR, "NewStr: out of memory (%s)", str); + return(NULL); + } + + (void) strcpy(stmp, str); + return(stmp); +} + +/* +** To save time, NewConn and FreeConn maintain a cache of one RMPCONN +** in `LastFree' (defined below). +*/ + +static RMPCONN *LastFree = NULL; + +/* +** NewConn -- allocate memory for a new RMPCONN connection. +** +** Parameters: +** rconn - initialization template for new connection. +** +** Returns: +** Ptr to new RMPCONN or NULL if we ran out of memory. +** +** Side Effects: +** - Memory may be malloc'd for the new RMPCONN (if not cached). +** - If malloc() fails, a log message will be generated. +*/ +RMPCONN * +NewConn(rconn) + RMPCONN *rconn; +{ + RMPCONN *rtmp; + + if (LastFree == NULL) { /* nothing cached; make a new one */ + if ((rtmp = (RMPCONN *) malloc(sizeof(RMPCONN))) == NULL) { + syslog(LOG_ERR, "NewConn: out of memory (%s)", + EnetStr(rconn)); + return(NULL); + } + } else { /* use the cached RMPCONN */ + rtmp = LastFree; + LastFree = NULL; + } + + /* + * Copy template into `rtmp', init file descriptor to `-1' and + * set ptr to next elem NULL. + */ + 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(rtmp) + 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() +{ + RMPCONN *rtmp; + + while (RmpConns != NULL) { + rtmp = RmpConns; + RmpConns = RmpConns->next; + FreeConn(rtmp); + } + + if (LastFree != NULL) { + free((char *)LastFree); + LastFree = NULL; + } +} + +/* +** AddConn -- Add a connection to the linked list of connections. +** +** Parameters: +** rconn - connection to be added. +** +** Returns: +** Nothing. +** +** Side Effects: +** - RmpConn will point to new connection. +** +** Warnings: +** - This routine must be called with SIGHUP blocked. +*/ +void +AddConn(rconn) + RMPCONN *rconn; +{ + if (RmpConns != NULL) + rconn->next = RmpConns; + RmpConns = rconn; +} + +/* +** FindConn -- Find a connection in the linked list of connections. +** +** We use the RMP (Ethernet) address as the basis for determining +** if this is the same connection. According to the Remote Maint +** Protocol, we can only have one connection with any machine. +** +** Parameters: +** rconn - connection to be found. +** +** Returns: +** Matching connection from linked list or NULL if not found. +** +** Side Effects: +** None. +** +** Warnings: +** - This routine must be called with SIGHUP blocked. +*/ +RMPCONN * +FindConn(rconn) + 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(rconn) + 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..dec0e7b --- /dev/null +++ b/libexec/revnetgroup/Makefile @@ -0,0 +1,8 @@ +# $Id$ + +PROG= revnetgroup +SRCS= revnetgroup.c hash.c parse_netgroup.c + +MAN8= revnetgroup.8 + +.include <bsd.prog.mk> diff --git a/libexec/revnetgroup/hash.c b/libexec/revnetgroup/hash.c new file mode 100644 index 0000000..2c1cb16 --- /dev/null +++ b/libexec/revnetgroup/hash.c @@ -0,0 +1,214 @@ +/* + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include "hash.h" + +#ifndef lint +static const char rcsid[] = "$Id$"; +#endif + +/* + * This hash function is stolen directly from the + * Berkeley DB package. It already exists inside libc, but + * it's declared static which prevents us from calling it + * from here. + */ +/* + * OZ's original sdbm hash + */ +u_int32_t +hash(keyarg, len) + const void *keyarg; + register size_t len; +{ + register const u_char *key; + register size_t loop; + register u_int32_t h; + +#define HASHC h = *key++ + 65599 * h + + h = 0; + key = keyarg; + if (len > 0) { + loop = (len + 8 - 1) >> 3; + + switch (len & (8 - 1)) { + case 0: + do { + HASHC; + /* FALLTHROUGH */ + case 7: + HASHC; + /* FALLTHROUGH */ + case 6: + HASHC; + /* FALLTHROUGH */ + case 5: + HASHC; + /* FALLTHROUGH */ + case 4: + HASHC; + /* FALLTHROUGH */ + case 3: + HASHC; + /* FALLTHROUGH */ + case 2: + HASHC; + /* FALLTHROUGH */ + case 1: + HASHC; + } while (--loop); + } + } + return (h); +} + +/* + * Generate a hash value for a given key (character string). + * We mask off all but the lower 8 bits since our table array + * can only hold 256 elements. + */ +u_int32_t hashkey(key) + char *key; +{ + + if (key == NULL) + return (-1); + return(hash((void *)key, strlen(key)) & HASH_MASK); +} + +/* Find an entry in the hash table (may be hanging off a linked list). */ +char *lookup(table, key) + struct group_entry *table[]; + char *key; +{ + struct group_entry *cur; + + cur = table[hashkey(key)]; + + while (cur) { + if (!strcmp(cur->key, key)) + return(cur->data); + cur = cur->next; + } + + return(NULL); +} + +/* + * Store an entry in the main netgroup hash table. Here's how this + * works: the table can only be so big when we initialize it (TABLESIZE) + * but the number of netgroups in the /etc/netgroup file could easily be + * much larger than the table. Since our hash values are adjusted to + * never be greater than TABLESIZE too, this means it won't be long before + * we find ourselves with two keys that hash to the same value. + * + * One way to deal with this is to malloc(2) a second table and start + * doing indirection, but this is a pain in the butt and it's not worth + * going to all that trouble for a dinky 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 (table, key, data) + struct group_entry *table[]; + char *key, *data; +{ + struct group_entry *new; + u_int32_t i; + + i = hashkey(key); + + new = (struct group_entry *)malloc(sizeof(struct group_entry)); + new->key = strdup(key); + new->data = strdup(data); + new->next = table[i]; + table[i] = new; + + return; +} + +/* + * Store 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 (table, key, data, domain) + struct member_entry *table[]; + char *key, *data, *domain; +{ + struct member_entry *cur, *new; + struct grouplist *tmp; + u_int32_t i; + + i = hashkey(key); + cur = table[i]; + + tmp = (struct grouplist *)malloc(sizeof(struct grouplist)); + tmp->groupname = strdup(data); + tmp->next = NULL; + + /* Check if all we have to do is insert a new groupname. */ + while (cur) { + if (!strcmp(cur->key, key)) { + tmp->next = cur->groups; + cur->groups = tmp; + return; + } + cur = cur->next; + } + + /* Didn't find a match -- add the whole mess to the table. */ + new = (struct member_entry *)malloc(sizeof(struct member_entry)); + new->key = strdup(key); + new->domain = domain ? strdup(domain) : "*"; + new->groups = tmp; + new->next = table[i]; + table[i] = new; + + return; +} diff --git a/libexec/revnetgroup/hash.h b/libexec/revnetgroup/hash.h new file mode 100644 index 0000000..7106967 --- /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. + * + * $Id$ + */ + +/* Groupname entry hung off a member_entry node. */ +struct grouplist { + char *groupname; + struct grouplist *next; +}; + +/* Entry in the cooked member list hash table. */ +struct member_entry { + char *key; + char *domain; + struct grouplist *groups; + struct member_entry *next; +}; + +/* Entry in the raw netgroup table. */ +struct group_entry { + char *key; + char *data; + struct 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 __P(( struct group_entry ** , char *, char * )); +extern void mstore __P(( struct member_entry ** , char *, char *, char * )); +extern char *lookup __P(( struct group_entry **, char * )); +extern void __endnetgrent __P(( void )); +extern void __setnetgrent __P(( char * )); +extern int __getnetgrent __P(( char **, char **, char ** )); diff --git a/libexec/revnetgroup/parse_netgroup.c b/libexec/revnetgroup/parse_netgroup.c new file mode 100644 index 0000000..14e7e23 --- /dev/null +++ b/libexec/revnetgroup/parse_netgroup.c @@ -0,0 +1,372 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Rick Macklem at The University of Guelph. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "$Id$"; +#endif /* LIBC_SCCS and not lint */ + +/* + * This is a specially hacked-up version of getnetgrent.c used to parse + * data from the stored hash table of netgroup info rather than from a + * file. It's used mainly for the parse_netgroup() function. All the YP + * stuff and file support has been stripped out since it isn't needed. + */ + +#include <stdio.h> +#include <strings.h> +#include <stdlib.h> +#include <unistd.h> +#include "hash.h" + +#ifndef lint +static const char rcsid[] = "$Id$"; +#endif + +/* + * Static Variables and functions used by setnetgrent(), getnetgrent() and + * __endnetgrent(). + * There are two linked lists: + * - linelist is just used by setnetgrent() to parse the net group file via. + * parse_netgrp() + * - netgrp is the list of entries for the current netgroup + */ +struct linelist { + struct linelist *l_next; /* Chain ptr. */ + int l_parsed; /* Flag for cycles */ + char *l_groupname; /* Name of netgroup */ + char *l_line; /* Netgroup entrie(s) to be parsed */ +}; + +struct netgrp { + struct netgrp *ng_next; /* Chain ptr */ + char *ng_str[3]; /* Field pointers, see below */ +}; +#define NG_HOST 0 /* Host name */ +#define NG_USER 1 /* User name */ +#define NG_DOM 2 /* and Domain name */ + +static struct linelist *linehead = (struct linelist *)0; +static struct netgrp *nextgrp = (struct netgrp *)0; +static struct { + struct netgrp *gr; + char *grname; +} grouphead = { + (struct netgrp *)0, + (char *)0, +}; +static int parse_netgrp(); +static struct linelist *read_for_group(); +void __setnetgrent(), __endnetgrent(); +int __getnetgrent(); +extern struct group_entry *gtable[]; + +/* + * setnetgrent() + * Parse the netgroup file looking for the netgroup and build the list + * of netgrp structures. Let parse_netgrp() and read_for_group() do + * most of the work. + */ +void +__setnetgrent(group) + char *group; +{ + /* Sanity check */ + + if (group == NULL || !strlen(group)) + return; + + if (grouphead.gr == (struct netgrp *)0 || + strcmp(group, grouphead.grname)) { + __endnetgrent(); + if (parse_netgrp(group)) + __endnetgrent(); + else { + grouphead.grname = (char *) + malloc(strlen(group) + 1); + strcpy(grouphead.grname, group); + } + } + nextgrp = grouphead.gr; +} + +/* + * Get the next netgroup off the list. + */ +int +__getnetgrent(hostp, userp, domp) + char **hostp, **userp, **domp; +{ + if (nextgrp) { + *hostp = nextgrp->ng_str[NG_HOST]; + *userp = nextgrp->ng_str[NG_USER]; + *domp = nextgrp->ng_str[NG_DOM]; + nextgrp = nextgrp->ng_next; + return (1); + } + return (0); +} + +/* + * __endnetgrent() - cleanup + */ +void +__endnetgrent() +{ + register struct linelist *lp, *olp; + register struct netgrp *gp, *ogp; + + lp = linehead; + while (lp) { + olp = lp; + lp = lp->l_next; + free(olp->l_groupname); + free(olp->l_line); + free((char *)olp); + } + linehead = (struct linelist *)0; + if (grouphead.grname) { + free(grouphead.grname); + grouphead.grname = (char *)0; + } + gp = grouphead.gr; + while (gp) { + ogp = gp; + gp = gp->ng_next; + if (ogp->ng_str[NG_HOST]) + free(ogp->ng_str[NG_HOST]); + if (ogp->ng_str[NG_USER]) + free(ogp->ng_str[NG_USER]); + if (ogp->ng_str[NG_DOM]) + free(ogp->ng_str[NG_DOM]); + free((char *)ogp); + } + grouphead.gr = (struct netgrp *)0; +} + +/* + * Parse the netgroup file setting up the linked lists. + */ +static int +parse_netgrp(group) + char *group; +{ + register char *spos, *epos; + register int len, strpos; +#ifdef DEBUG + register int fields; +#endif + char *pos, *gpos; + struct netgrp *grp; + struct linelist *lp = linehead; + + /* + * First, see if the line has already been read in. + */ + while (lp) { + if (!strcmp(group, lp->l_groupname)) + break; + lp = lp->l_next; + } + if (lp == (struct linelist *)0 && + (lp = read_for_group(group)) == (struct linelist *)0) + return (1); + if (lp->l_parsed) { +#ifdef DEBUG + /* + * This error message is largely superflous since the + * code handles the error condition sucessfully, and + * spewing it out from inside libc can actually hose + * certain programs. + */ + fprintf(stderr, "Cycle in netgroup %s\n", lp->l_groupname); +#endif + return (1); + } else + lp->l_parsed = 1; + pos = lp->l_line; + /* Watch for null pointer dereferences, dammit! */ + while (pos != NULL && *pos != '\0') { + if (*pos == '(') { + grp = (struct netgrp *)malloc(sizeof (struct netgrp)); + bzero((char *)grp, sizeof (struct netgrp)); + grp->ng_next = grouphead.gr; + grouphead.gr = grp; + pos++; + gpos = strsep(&pos, ")"); +#ifdef DEBUG + fields = 0; +#endif + for (strpos = 0; strpos < 3; strpos++) { + if ((spos = strsep(&gpos, ","))) { +#ifdef DEBUG + fields++; +#endif + while (*spos == ' ' || *spos == '\t') + spos++; + if ((epos = strpbrk(spos, " \t"))) { + *epos = '\0'; + len = epos - spos; + } else + len = strlen(spos); + if (len > 0) { + grp->ng_str[strpos] = (char *) + malloc(len + 1); + bcopy(spos, grp->ng_str[strpos], + len + 1); + } + } else { + /* + * All other systems I've tested + * return NULL for empty netgroup + * fields. It's up to user programs + * to handle the NULLs appropriately. + */ + grp->ng_str[strpos] = NULL; + } + } +#ifdef DEBUG + /* + * Note: on other platforms, malformed netgroup + * entries are not normally flagged. While we + * can catch bad entries and report them, we should + * stay silent by default for compatibility's sake. + */ + if (fields < 3) + fprintf(stderr, "Bad entry (%s%s%s%s%s) in netgroup \"%s\"\n", + grp->ng_str[NG_HOST] == NULL ? "" : grp->ng_str[NG_HOST], + grp->ng_str[NG_USER] == NULL ? "" : ",", + grp->ng_str[NG_USER] == NULL ? "" : grp->ng_str[NG_USER], + grp->ng_str[NG_DOM] == NULL ? "" : ",", + grp->ng_str[NG_DOM] == NULL ? "" : grp->ng_str[NG_DOM], + lp->l_groupname); +#endif + } else { + spos = strsep(&pos, ", \t"); + if (parse_netgrp(spos)) + continue; + } + /* Watch for null pointer dereferences, dammit! */ + if (pos != NULL) + while (*pos == ' ' || *pos == ',' || *pos == '\t') + pos++; + } + return (0); +} + +/* + * Read the netgroup file and save lines until the line for the netgroup + * is found. Return 1 if eof is encountered. + */ +static struct linelist * +read_for_group(group) + char *group; +{ + register char *pos, *spos, *linep = NULL, *olinep = NULL; + register 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..2d03f81 --- /dev/null +++ b/libexec/revnetgroup/revnetgroup.8 @@ -0,0 +1,137 @@ +.\" Copyright (c) 1995 +.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Bill Paul. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id$ +.\" +.Dd October 24, 1995 +.Dt REVNETGROUP 8 +.Os +.Sh NAME +.Nm revnetgroup +.Nd "generate reverse netgroup data" +.Sh SYNOPSIS +.Nm revnetgroup +.Fl u +.Fl h +.Op Fl f Ar netgroup_file +.Sh DESCRIPTION +.Nm revnetgroup +processes the contents of a file in +.Xr netgroup 5 +format into what is called +.Pa reverse netgroup +form. That is, where the original file shows +netgroup memberships in terms of which members reside in a particular +group, the reverse netgroup format specifies what groups are associated +with a particular member. This information is used to generate the +.Nm netgroup.byuser +and +.Nm netgroup.byhosts +NIS maps. These reverse netgroup maps are used to help speed up +netgroup lookups, particularly for the +.Fn innetgr +library function. +.Pp +For example, the standard +.Nm /etc/netgroup +file may list a netgroup and a list of its members. Here, the +netgroup is considered the +.Pa key +and the member names are the +.Pa data . +By contrast, the reverse +.Nm netgroup.byusers +database lists each unique +member as the key and the netgroups to which the members belong become +the data. Seperate databases are created to hold information pertaining +to users and hosts; this allows netgroup username lookups +and netgroup hostname lookups to be performed using independent keyspaces. +.Pp +By constructing these reverse netgroup databases (and the corresponding +NIS maps) in advance, the +.Xr getnetgrent 3 +library functions are spared from having to work out the dependencies +themselves on the fly. This is important on networks with large numbers +of users and hosts, since it can take a considerable amount of time +to process very large netgroup databases. +.Pp +The +.Nm revnetgroup +command prints its results on the standard output. It is usually called +only by +.Nm /var/yp/Makefile +when rebuilding the NIS netgroup maps. +.Pp +.Sh OPTIONS +The +.Nm revnetgroup +command supports the following options: +.Bl -tag -width flag +.It Fl u +Generate netgroup.byuser output; only username information in the +original netgroup file is processed. +.It Fl h +Generate netgroup.byhost output; only hostname information in the +original netgroup file is processed. (Note at least one of the +.Fl u +or +.Fl h +flags must be specified.) +.It Op Fl f Ar netgroup_file +The +.Nm revnetgroup +command uses +.Nm /etc/netgroup +as its default input file. The +.Fl f +flag allows the user to specify an alternate input file. Specifying ``-'' +as the input file causes +.Nm revnetgroup +to read from the standard input. +.El +.Sh FILES +.Bl -tag -width Pa -compact +.It Pa /var/yp/Makefile +The Makefile that calls +.Nm yp_mkdb +and +.Nm revnetgroup +to build the NIS databases. +.It Pa /etc/netgroup +The default netgroup database file. This file is most often found +only on the NIS master server. +.El +.Sh SEE ALSO +.Xr getnetgrent 3 , +.Xr yp 4 , +.Xr netgroup 5 , +.Xr yp_mkdb 8 +.Sh AUTHOR +Bill Paul <wpaul@ctr.columbia.edu> diff --git a/libexec/revnetgroup/revnetgroup.c b/libexec/revnetgroup/revnetgroup.c new file mode 100644 index 0000000..7655480 --- /dev/null +++ b/libexec/revnetgroup/revnetgroup.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * reverse netgroup map generator program + * + * Written by Bill Paul <wpaul@ctr.columbia.edu> + * Center for Telecommunications Research + * Columbia University, New York City + * + * $Id: revnetgroup.c,v 1.6 1997/02/22 14:22:03 peter Exp $ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <err.h> +#include "hash.h" + +#ifndef lint +static const char rcsid[] = "$Id: revnetgroup.c,v 1.6 1997/02/22 14:22:03 peter Exp $"; +#endif + +/* 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]; + +void usage(prog) +char *prog; +{ + fprintf (stderr,"usage: %s -u|-h [-f netgroup file]\n",prog); + exit(1); +} + +extern char *optarg; + +int +main(argc, argv) + 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(argv[0]); + + while ((ch = getopt(argc, argv, "uhf:")) != -1) { + switch(ch) { + case 'u': + if (hosts != -1) { + warnx("please use only one of -u or -h"); + usage(argv[0]); + } + hosts = 0; + break; + case 'h': + if (hosts != -1) { + warnx("please use only one of -u or -h"); + usage(argv[0]); + } + hosts = 1; + break; + case 'f': + netgroup = optarg; + break; + default: + usage(argv[0]); + break; + } + } + + if (hosts == -1) + usage(argv[0]); + + if (strcmp(netgroup, "-")) { + if ((fp = fopen(netgroup, "r")) == NULL) { + err(1,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) != NULL) { + if (hosts ? host && strcmp(host,"-") : user && strcmp(user, "-")) + mstore(mtable, hosts ? host : user, gcur->key, domain); + } + gcur = gcur->next; + } + } + + /* Release resources used by the netgroup parser code. */ + __endnetgrent(); + + /* Spew out the results. */ + for (i = 0; i < TABLESIZE; i++) { + mcur = mtable[i]; + while(mcur) { + struct grouplist *tmp; + printf ("%s.%s\t", mcur->key, mcur->domain); + tmp = mcur->groups; + while(tmp) { + printf ("%s", tmp->groupname); + tmp = tmp->next; + if (tmp) + printf(","); + } + mcur = mcur->next; + printf ("\n"); + } + } + + /* Let the OS free all our resources. */ + exit(0); +} diff --git a/libexec/rexecd/Makefile b/libexec/rexecd/Makefile index aaedac5..ebd3d15 100644 --- a/libexec/rexecd/Makefile +++ b/libexec/rexecd/Makefile @@ -1,6 +1,11 @@ # @(#)Makefile 8.1 (Berkeley) 6/4/93 +# $Id$ PROG= rexecd -MAN8= rexecd.0 +MAN8= rexecd.8 +CFLAGS+= -DSKEY + +DPADD= ${LIBSKEY} ${LIBMD} ${LIBCRYPT} +LDADD= -lskey -lmd -lcrypt .include <bsd.prog.mk> diff --git a/libexec/rexecd/rexecd.8 b/libexec/rexecd/rexecd.8 index 3035900..df89504 100644 --- a/libexec/rexecd/rexecd.8 +++ b/libexec/rexecd/rexecd.8 @@ -29,9 +29,10 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)rexecd.8 8.3 (Berkeley) 6/1/94 +.\" @(#)rexecd.8 8.2 (Berkeley) 12/11/93 +.\" $Id$ .\" -.Dd June 1, 1994 +.Dd September 23, 1994 .Dt REXECD 8 .Os BSD 4.2 .Sh NAME @@ -96,6 +97,14 @@ shell inherits the network connections established by .Nm rexecd . .El +.Sh CAVEATS +.Nm Rexecd +will no longer allow root logins, access for users listed in /etc/ftpusers, +or access for users with no passwords, which were all serious security holes. +The entire concept of rexec/rexecd is a major security hole and an example +of how not to do things. +.Nm Rexecd +is disabled by default in /etc/inetd.conf. .Sh DIAGNOSTICS Except for the last one listed below, all diagnostic messages are returned on the initial socket, @@ -117,7 +126,6 @@ list (as configured into the system). No password file entry for the user name existed. .It Sy Password incorrect. The wrong password was supplied. -.ne 1i .It Sy \&No remote directory. The .Xr chdir @@ -136,10 +144,6 @@ and is not preceded by a flag byte. .Sh SEE ALSO .Xr rexec 3 .Sh BUGS -Indicating ``Login incorrect'' as opposed to ``Password incorrect'' -is a security breach which allows people to probe a system for users -with null passwords. -.Pp A facility to allow all data and password exchanges to be encrypted should be present. .Sh HISTORY diff --git a/libexec/rexecd/rexecd.c b/libexec/rexecd/rexecd.c index 796ad9c..119551e 100644 --- a/libexec/rexecd/rexecd.c +++ b/libexec/rexecd/rexecd.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id: rexecd.c,v 1.13 1997/02/22 14:22:06 peter Exp $ */ #ifndef lint @@ -47,6 +49,7 @@ static char sccsid[] = "@(#)rexecd.c 8.1 (Berkeley) 6/4/93"; #include <sys/time.h> #include <netinet/in.h> +#include <arpa/inet.h> #include <errno.h> #include <netdb.h> @@ -57,10 +60,23 @@ static char sccsid[] = "@(#)rexecd.c 8.1 (Berkeley) 6/4/93"; #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <syslog.h> +#include <netdb.h> /*VARARGS1*/ int error(); +char username[MAXLOGNAME + 5 + 1] = "USER="; +char homedir[MAXPATHLEN + 5 + 1] = "HOME="; +char shell[MAXPATHLEN + 6 + 1] = "SHELL="; +char path[sizeof(_PATH_DEFPATH) + sizeof("PATH=")] = "PATH="; +char *envinit[] = + {homedir, shell, path, username, 0}; +char **environ; +char *remote; + +struct sockaddr_in asin = { AF_INET }; + /* * remote execute server: * username\0 @@ -75,32 +91,36 @@ main(argc, argv) { struct sockaddr_in from; int fromlen; + struct hostent *hp; + openlog(argv[0], LOG_PID, LOG_AUTH); fromlen = sizeof (from); if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { (void)fprintf(stderr, "rexecd: getpeername: %s\n", strerror(errno)); exit(1); } - doit(0, &from); -} -char username[20] = "USER="; -char homedir[64] = "HOME="; -char shell[64] = "SHELL="; -char path[sizeof(_PATH_DEFPATH) + sizeof("PATH=")] = "PATH="; -char *envinit[] = - {homedir, shell, path, username, 0}; -char **environ; + hp = gethostbyaddr((char *) &from.sin_addr, sizeof(from.sin_addr), + from.sin_family); + remote = inet_ntoa(from.sin_addr); + remote = (hp != NULL) ? hp->h_name : inet_ntoa(from.sin_addr); -struct sockaddr_in asin = { AF_INET }; + doit(0, &from); +} doit(f, fromp) int f; struct sockaddr_in *fromp; { + FILE *fp; char cmdbuf[NCARGS+1], *cp, *namep; +#ifdef SKEY + char *skey_crypt(); + char user[16], pass[100]; +#else /* SKEY */ char user[16], pass[16]; +#endif /* SKEY */ struct passwd *pwd; int s; u_short port; @@ -156,16 +176,43 @@ doit(f, fromp) } endpwent(); if (*pwd->pw_passwd != '\0') { +#ifdef SKEY + namep = skey_crypt(pass, pwd->pw_passwd, pwd, + skeyaccess(user, NULL, remote)); +#else /* SKEY */ namep = crypt(pass, pwd->pw_passwd); +#endif /* SKEY */ if (strcmp(namep, pwd->pw_passwd)) { - error("Password incorrect.\n"); + syslog(LOG_ERR, "LOGIN FAILURE from %s, %s", + remote, user); + error("Login incorrect.\n"); exit(1); } } - if (chdir(pwd->pw_dir) < 0) { - error("No remote directory.\n"); + + if (pwd->pw_uid == 0 || *pwd->pw_passwd == '\0' || + (pwd->pw_expire && time(NULL) >= pwd->pw_expire)) { + syslog(LOG_ERR, "%s LOGIN REFUSED from %s", user, remote); + error("Login incorrect.\n"); exit(1); } + + if ((fp = fopen(_PATH_FTPUSERS, "r")) != NULL) { + while (fgets(buf, sizeof(buf), fp) != NULL) { + if ((cp = index(buf, '\n')) != NULL) + *cp = '\0'; + if (strcmp(buf, pwd->pw_name) == 0) { + syslog(LOG_ERR, "%s LOGIN REFUSED from %s", + user, remote); + error("Login incorrect.\n"); + exit(1); + } + } + } + (void) fclose(fp); + + syslog(LOG_INFO, "login from %s as %s", remote, user); + (void) write(2, "\0", 1); if (port) { (void) pipe(pv); @@ -210,6 +257,8 @@ doit(f, fromp) pwd->pw_shell = _PATH_BSHELL; if (f > 2) (void) close(f); + if (setlogin(pwd->pw_name) < 0) + syslog(LOG_ERR, "setlogin() failed: %m"); (void) setgid((gid_t)pwd->pw_gid); initgroups(pwd->pw_name, pwd->pw_gid); (void) setuid((uid_t)pwd->pw_uid); @@ -223,6 +272,10 @@ doit(f, fromp) cp++; else cp = pwd->pw_shell; + if (chdir(pwd->pw_dir) < 0) { + error("No remote directory.\n"); + exit(1); + } execl(pwd->pw_shell, cp, "-c", cmdbuf, 0); perror(pwd->pw_shell); exit(1); @@ -236,7 +289,7 @@ error(fmt, a1, a2, a3) char buf[BUFSIZ]; buf[0] = 1; - (void) sprintf(buf+1, fmt, a1, a2, a3); + (void) snprintf(buf+1, sizeof(buf) - 1, fmt, a1, a2, a3); (void) write(2, buf, strlen(buf)); } diff --git a/libexec/rlogind/Makefile b/libexec/rlogind/Makefile index 639d3f6..41136e2 100644 --- a/libexec/rlogind/Makefile +++ b/libexec/rlogind/Makefile @@ -1,11 +1,18 @@ # @(#)Makefile 8.1 (Berkeley) 6/4/93 +# $Id$ PROG= rlogind +SRCS= rlogind.c +MAN8= rlogind.8 +DPADD= ${LIBUTIL} +LDADD= -lutil + +.if exists(${DESTDIR}/usr/lib/libkrb.a) && (defined(MAKE_KERBEROS) \ + || defined(MAKE_EBONES)) CFLAGS+=-DKERBEROS -DCRYPT -SRCS= rlogind.c des_rw.c -MAN8= rlogind.0 -DPADD= ${LIBUTIL} ${LIBKRB} ${LIBDES} -LDADD= -lutil -lkrb -ldes -.PATH: ${.CURDIR}/../../usr.bin/rlogin +DPADD= ${LIBKRB} ${LIBDES} +LDADD+= -lkrb -ldes +DISTRIBUTION= krb +.endif .include <bsd.prog.mk> diff --git a/libexec/rlogind/rlogind.8 b/libexec/rlogind/rlogind.8 index 9c19933..7c565e3 100644 --- a/libexec/rlogind/rlogind.8 +++ b/libexec/rlogind/rlogind.8 @@ -30,6 +30,7 @@ .\" SUCH DAMAGE. .\" .\" @(#)rlogind.8 8.1 (Berkeley) 6/4/93 +.\" $Id$ .\" .Dd June 4, 1993 .Dt RLOGIND 8 @@ -39,7 +40,7 @@ .Nd remote login server .Sh SYNOPSIS .Nm rlogind -.Op Fl aln +.Op Fl Daln .Sh DESCRIPTION .Nm Rlogind is the server for the @@ -50,6 +51,9 @@ with authentication based on privileged port numbers from trusted hosts. Options supported by .Nm rlogind : .Bl -tag -width Ds +.It Fl D +Set TCP_NODELAY socket option. This improves responsiveness at the expense of +some additional network traffic. .It Fl a Ask hostname for verification. .It Fl l @@ -60,6 +64,20 @@ file, unless the user is logging in as the superuser. Disable keep-alive messages. .El .Pp +The following options are valid only if Kerberos is in use: +.Bl -tag -width Ds +.It Fl k +Enable Kerberos authentication. +.It Fl v +Enable vacuous mode. +.It Fl x +Enable +.Tn DES +encryption for all data passed via the rlogin +session. This may impact response time +and CPU utilization, but provides increased security. +.El +.Pp .Nm Rlogind listens for service requests at the port indicated in the ``login'' service specification; see @@ -151,7 +169,17 @@ by the server failed. .Sh SEE ALSO .Xr login 1 , .Xr ruserok 3 , +.Xr hosts 5 , +.Xr nologin 5 , .Xr rshd 8 +.Sh FILES +.Bl -tag -width /etc/hostsxxxxxxxx -compact +.It Pa /etc/hosts +.It Pa /etc/hosts.equiv +.It Pa $HOME/.rhosts +.It Pa /etc/nologin +.El + .Sh BUGS The authentication procedure used here assumes the integrity of each client machine and the connecting medium. This is diff --git a/libexec/rlogind/rlogind.c b/libexec/rlogind/rlogind.c index 80d53d5..97ba074 100644 --- a/libexec/rlogind/rlogind.c +++ b/libexec/rlogind/rlogind.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id: rlogind.c,v 1.16 1997/03/24 06:01:39 imp Exp $ */ #ifndef lint @@ -38,7 +40,7 @@ static char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)rlogind.c 8.2 (Berkeley) 4/28/95"; +static char sccsid[] = "@(#)rlogind.c 8.1 (Berkeley) 6/4/93"; #endif /* not lint */ /* @@ -61,6 +63,7 @@ static char sccsid[] = "@(#)rlogind.c 8.2 (Berkeley) 4/28/95"; #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> +#include <netinet/tcp.h> #include <arpa/inet.h> #include <netdb.h> @@ -78,7 +81,7 @@ static char sccsid[] = "@(#)rlogind.c 8.2 (Berkeley) 4/28/95"; #endif #ifdef KERBEROS -#include <kerberosIV/des.h> +#include <des.h> #include <kerberosIV/krb.h> #define SECURE_MESSAGE "This rlogin session is using DES encryption for all transmissions.\r\n" @@ -89,9 +92,9 @@ u_char tick_buf[sizeof(KTEXT_ST)]; Key_schedule schedule; int doencrypt, retval, use_kerberos, vacuous; -#define ARGSTR "alnkvx" +#define ARGSTR "Dalnkvx" #else -#define ARGSTR "aln" +#define ARGSTR "Daln" #endif /* KERBEROS */ char *env[2]; @@ -101,6 +104,7 @@ 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; @@ -129,8 +133,11 @@ main(argc, argv) openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH); opterr = 0; - while ((ch = getopt(argc, argv, ARGSTR)) != EOF) + while ((ch = getopt(argc, argv, ARGSTR)) != -1) switch (ch) { + case 'D': + no_delay = 1; + break; case 'a': check_all = 1; break; @@ -176,9 +183,13 @@ main(argc, argv) if (keepalive && setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); + if (no_delay && + setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) + syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m"); on = IPTOS_LOWDELAY; if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + doit(0, &from); } @@ -215,10 +226,12 @@ doit(f, fromp) fromp->sin_port = ntohs((u_short)fromp->sin_port); hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof(struct in_addr), fromp->sin_family); - if (hp) - (void)strcpy(hostname, hp->h_name); - else - (void)strcpy(hostname, inet_ntoa(fromp->sin_addr)); + if (hp) { + (void)strncpy(hostname, hp->h_name, sizeof(hostname)); + } else { + (void)strncpy(hostname, inet_ntoa(fromp->sin_addr), sizeof(hostname)); + } + hostname[sizeof(hostname) - 1] = '\0'; #ifdef KERBEROS if (use_kerberos) { @@ -241,9 +254,8 @@ doit(f, fromp) } #ifdef IP_OPTIONS { - u_char optbuf[BUFSIZ/3], *cp; - char lbuf[BUFSIZ], *lp; - int optsize = sizeof(optbuf), ipproto; + u_char optbuf[BUFSIZ/3]; + int optsize = sizeof(optbuf), ipproto, i; struct protoent *ip; if ((ip = getprotobyname("ip")) != NULL) @@ -252,17 +264,18 @@ doit(f, fromp) ipproto = IPPROTO_IP; if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) == 0 && optsize != 0) { - lp = lbuf; - for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) - sprintf(lp, " %2.2x", *cp); - syslog(LOG_NOTICE, - "Connection received using IP options (ignored):%s", - lbuf); - if (setsockopt(0, ipproto, IP_OPTIONS, - (char *)NULL, optsize) != 0) { - syslog(LOG_ERR, - "setsockopt IP_OPTIONS NULL: %m"); - exit(1); + 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->sin_addr), + c == IPOPT_LSRR ? "LSRR" : "SSRR"); + exit(1); + } + if (c == IPOPT_EOL) + break; + i += (c == IPOPT_NOP) ? 1 : optbuf[i+1]; } } } @@ -293,6 +306,11 @@ doit(f, fromp) if (f > 2) /* f should always be 0, but... */ (void) close(f); setup_term(0); + if (*lusername=='-') { + syslog(LOG_ERR, "tried to pass user \"%s\" to login", + lusername); + fatal(STDERR_FILENO, "invalid user", 0); + } if (authenticated) { #ifdef KERBEROS if (use_kerberos && (pwd->pw_uid == 0)) @@ -302,11 +320,11 @@ doit(f, fromp) hostname); #endif - execle(_PATH_LOGIN, "login", "-p", - "-h", hostname, "-f", "--", lusername, NULL, env); + execl(_PATH_LOGIN, "login", "-p", + "-h", hostname, "-f", lusername, (char *)NULL); } else - execle(_PATH_LOGIN, "login", "-p", - "-h", hostname, "--", lusername, NULL, env); + execl(_PATH_LOGIN, "login", "-p", + "-h", hostname, lusername, (char *)NULL); fatal(STDERR_FILENO, _PATH_LOGIN, 1); /*NOTREACHED*/ } @@ -348,7 +366,7 @@ control(pty, cp, n) if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's') return (0); oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */ - memmove(&w, cp+4, sizeof(w)); + 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); @@ -573,10 +591,9 @@ do_rlogin(dest) pwd = getpwnam(lusername); if (pwd == NULL) return (-1); - if (pwd->pw_uid == 0) - return (-1); /* XXX why don't we syslog() failure? */ - return (iruserok(dest->sin_addr.s_addr, 0, rusername, lusername)); + return (iruserok(dest->sin_addr.s_addr, pwd->pw_uid == 0, + rusername, lusername)); } void @@ -596,6 +613,8 @@ getstr(buf, cnt, errmsg) } while (c != 0); } +extern char **environ; + void setup_term(fd) int fd; @@ -634,6 +653,7 @@ setup_term(fd) env[0] = term; env[1] = 0; + environ = env; } #ifdef KERBEROS @@ -673,7 +693,7 @@ do_krb_login(dest) ticket, "rcmd", instance, dest, &faddr, kdata, "", schedule, version); - des_set_key(kdata->session, schedule); + des_set_key_krb(&kdata->session, schedule); } else #endif @@ -681,7 +701,7 @@ do_krb_login(dest) authopts, 0, ticket, "rcmd", instance, dest, (struct sockaddr_in *) 0, - kdata, "", (bit_64 *) 0, version); + kdata, "", NULL, version); if (rc != KSUCCESS) return (rc); @@ -697,7 +717,7 @@ do_krb_login(dest) /* returns nonzero for no access */ if (kuserok(kdata, lusername) != 0) return (-1); - + return (0); } @@ -707,9 +727,9 @@ void usage() { #ifdef KERBEROS - syslog(LOG_ERR, "usage: rlogind [-aln] [-k | -v]"); + syslog(LOG_ERR, "usage: rlogind [-Daln] [-k | -v]"); #else - syslog(LOG_ERR, "usage: rlogind [-aln]"); + syslog(LOG_ERR, "usage: rlogind [-Daln]"); #endif } diff --git a/libexec/rpc.rquotad/Makefile b/libexec/rpc.rquotad/Makefile new file mode 100644 index 0000000..53ca412 --- /dev/null +++ b/libexec/rpc.rquotad/Makefile @@ -0,0 +1,10 @@ +# $Id$ + +PROG = rpc.rquotad +SRCS = rquotad.c +MAN8 = rpc.rquotad.8 + +DPADD= ${LIBRPCSVC} +LDADD= -lrpcsvc + +.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..dd8f3aa --- /dev/null +++ b/libexec/rpc.rquotad/rpc.rquotad.8 @@ -0,0 +1,61 @@ +.\" +.\" 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. +.\" +.\" $Id: rpc.rquotad.8,v 1.3 1997/02/22 14:22:10 peter Exp $ +.\" +.Dd June 22, 1994 +.Dt RPC.RQUOTAD 8 +.Os BSD 4.3 +.Sh NAME +.Nm rpc.rquotad +.Nd remote quota server +.Sh SYNOPSIS +.Nm /usr/libexec/rpc.rquotad +.Sh DESCRIPTION +.Nm rpc.rquotad +is a +.Xr rpc 3 +server which returns quotas for a user of a local filesystem +which is NFS-mounted onto a remote machine. +.Xr quota 1 +uses the results to display user quotas for remote filesystems. +.Nm rpc.rquotad +is normally invoked by +.Xr inetd 8 . +.Pp +.Nm rpc.rquotad +uses an RPC protocol defined in +.Pa /usr/include/rpcsvc/rquota.x . +.Sh BUGS +.Bx 4.4 +and +.Tn FreeBSD +support group quotas but the rquota protocol does not. +.Sh SEE ALSO +.Xr quota 1 diff --git a/libexec/rpc.rquotad/rquotad.c b/libexec/rpc.rquotad/rquotad.c new file mode 100644 index 0000000..33fa420 --- /dev/null +++ b/libexec/rpc.rquotad/rquotad.c @@ -0,0 +1,331 @@ +/* + * by Manuel Bouyer (bouyer@ensta.fr) + * + * There is no copyright, you can use it as you want. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/mount.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <signal.h> + +#include <stdio.h> +#include <fstab.h> +#include <ctype.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <pwd.h> +#include <grp.h> +#include <errno.h> + +#include <syslog.h> +#include <varargs.h> + +#include <ufs/ufs/quota.h> +#include <rpc/rpc.h> +#include <rpc/pmap_clnt.h> +#include <rpcsvc/rquota.h> +#include <arpa/inet.h> + +void rquota_service __P((struct svc_req *request, SVCXPRT *transp)); +void sendquota __P((struct svc_req *request, SVCXPRT *transp)); +void printerr_reply __P((SVCXPRT *transp)); +void initfs __P((void)); +int getfsquota __P((long id, char *path, struct dqblk *dqblk)); +int hasquota __P((struct fstab *fs, char **qfnamep)); + +/* + * structure containing informations about ufs filesystems + * initialised by initfs() + */ +struct fs_stat { + struct fs_stat *fs_next; /* next element */ + char *fs_file; /* mount point of the filesystem */ + char *qfpathname; /* pathname of the quota file */ + dev_t st_dev; /* device of the filesystem */ +} fs_stat; +struct fs_stat *fs_begin = NULL; + +int from_inetd = 1; + +void +cleanup() +{ + (void) pmap_unset(RQUOTAPROG, RQUOTAVERS); + exit(0); +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + SVCXPRT *transp; + int sock = 0; + int proto = 0; + struct sockaddr_in from; + int fromlen; + + fromlen = sizeof(from); + if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { + from_inetd = 0; + sock = RPC_ANYSOCK; + proto = IPPROTO_UDP; + } + + if (!from_inetd) { + daemon(0, 0); + + (void) pmap_unset(RQUOTAPROG, RQUOTAVERS); + + (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 */ + transp = svcudp_create(sock); + if (transp == NULL) { + syslog(LOG_ERR, "couldn't create udp service."); + exit(1); + } + if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquota_service, proto)) { + syslog(LOG_ERR, "unable to register (RQUOTAPROG, RQUOTAVERS, %s).", proto?"udp":"(inetd)"); + exit(1); + } + + initfs(); /* init the fs_stat list */ + svc_run(); + syslog(LOG_ERR, "svc_run returned"); + exit(1); +} + +void +rquota_service(request, transp) + struct svc_req *request; + SVCXPRT *transp; +{ + switch (request->rq_proc) { + case NULLPROC: + (void)svc_sendreply(transp, 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 */ +void +sendquota(request, transp) + struct svc_req *request; + SVCXPRT *transp; +{ + struct getquota_args getq_args; + struct getquota_rslt getq_rslt; + struct dqblk dqblk; + struct timeval timev; + + bzero((char *)&getq_args, sizeof(getq_args)); + if (!svc_getargs(transp, xdr_getquota_args, (caddr_t)&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; + getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = DEV_BSIZE; + getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit = + dqblk.dqb_bhardlimit; + getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit = + dqblk.dqb_bsoftlimit; + getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks = + dqblk.dqb_curblocks; + 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, xdr_getquota_rslt, (char *)&getq_rslt)) { + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_getquota_args, (caddr_t)&getq_args)) { + syslog(LOG_ERR, "unable to free arguments"); + exit(1); + } +} + +void +printerr_reply(transp) /* when a reply to a request failed */ + SVCXPRT *transp; +{ + char *name; + struct sockaddr_in *caller; + int save_errno; + + save_errno = errno; + + caller = svc_getcaller(transp); + name = (char *)inet_ntoa(caller->sin_addr); + errno = save_errno; + if (errno == 0) + syslog(LOG_ERR, "couldn't send reply to %s", name); + else + syslog(LOG_ERR, "couldn't send reply to %s: %m", name); +} + +/* initialise the fs_tab list from entries in /etc/fstab */ +void +initfs() +{ + struct fs_stat *fs_current = NULL; + struct fs_stat *fs_next = NULL; + char *qfpathname; + struct fstab *fs; + struct stat st; + + setfsent(); + while ((fs = getfsent())) { + if (strcmp(fs->fs_vfstype, "ufs")) + continue; + if (!hasquota(fs, &qfpathname)) + continue; + + fs_current = (struct fs_stat *) malloc(sizeof(struct fs_stat)); + fs_current->fs_next = fs_next; /* next element */ + + fs_current->fs_file = malloc(sizeof(char) * (strlen(fs->fs_file) + 1)); + strcpy(fs_current->fs_file, fs->fs_file); + + fs_current->qfpathname = malloc(sizeof(char) * (strlen(qfpathname) + 1)); + strcpy(fs_current->qfpathname, qfpathname); + + stat(qfpathname, &st); + fs_current->st_dev = st.st_dev; + + fs_next = fs_current; + } + endfsent(); + fs_begin = fs_current; +} + +/* + * gets the quotas for id, filesystem path. + * Return 0 if fail, 1 otherwise + */ +int +getfsquota(id, path, dqblk) + long id; + char *path; + struct dqblk *dqblk; +{ + struct stat st_path; + struct fs_stat *fs; + int qcmd, fd, ret = 0; + + if (stat(path, &st_path) < 0) + return (0); + + qcmd = QCMD(Q_GETQUOTA, USRQUOTA); + + for (fs = fs_begin; fs != NULL; fs = fs->fs_next) { + /* where the devise is the same as path */ + if (fs->st_dev != st_path.st_dev) + continue; + + /* find the specified filesystem. get and return quota */ + if (quotactl(fs->fs_file, qcmd, id, dqblk) == 0) + return (1); + + if ((fd = open(fs->qfpathname, O_RDONLY)) < 0) { + syslog(LOG_ERR, "open error: %s: %m", fs->qfpathname); + return (0); + } + if (lseek(fd, (off_t)(id * sizeof(struct dqblk)), L_SET) == (off_t)-1) { + close(fd); + return (1); + } + switch (read(fd, dqblk, sizeof(struct dqblk))) { + case 0: + /* + * Convert implicit 0 quota (EOF) + * into an explicit one (zero'ed dqblk) + */ + bzero((caddr_t) dqblk, sizeof(struct dqblk)); + ret = 1; + break; + case sizeof(struct dqblk): /* OK */ + ret = 1; + break; + default: /* ERROR */ + syslog(LOG_ERR, "read error: %s: %m", fs->qfpathname); + close(fd); + return (0); + } + close(fd); + } + return (ret); +} + +/* + * Check to see if a particular quota is to be enabled. + * Comes from quota.c, NetBSD 0.9 + */ +int +hasquota(fs, qfnamep) + struct fstab *fs; + char **qfnamep; +{ + static char initname, usrname[100]; + static char buf[BUFSIZ]; + char *opt, *cp; + char *qfextension[] = INITQFNAMES; + + if (!initname) { + sprintf(usrname, "%s%s", qfextension[USRQUOTA], QUOTAFILENAME); + initname = 1; + } + strcpy(buf, fs->fs_mntops); + for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { + if ((cp = index(opt, '='))) + *cp++ = '\0'; + if (strcmp(opt, usrname) == 0) + break; + } + if (!opt) + return (0); + if (cp) { + *qfnamep = cp; + return (1); + } + sprintf(buf, "%s/%s.%s", fs->fs_file, QUOTAFILENAME, qfextension[USRQUOTA]); + *qfnamep = buf; + return (1); +} diff --git a/libexec/rpc.rstatd/Makefile b/libexec/rpc.rstatd/Makefile new file mode 100644 index 0000000..026c8e0 --- /dev/null +++ b/libexec/rpc.rstatd/Makefile @@ -0,0 +1,10 @@ +# $Id$ + +PROG = rpc.rstatd +SRCS = rstatd.c rstat_proc.c +MAN8 = rpc.rstatd.8 + +DPADD= ${LIBRPCSVC} ${LIBUTIL} ${LIBKVM} +LDADD= -lrpcsvc -lutil -lkvm + +.include <bsd.prog.mk> diff --git a/libexec/rpc.rstatd/rpc.rstatd.8 b/libexec/rpc.rstatd/rpc.rstatd.8 new file mode 100644 index 0000000..5a54b81 --- /dev/null +++ b/libexec/rpc.rstatd/rpc.rstatd.8 @@ -0,0 +1,61 @@ +.\" -*- nroff -*- +.\" +.\" Copyright (c) 1985, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id$ +.\" +.Dd June 7, 1993 +.Dt RPC.RSTATD 8 +.Os BSD 4.3 +.Sh NAME +.Nm rpc.rstatd +.Nd kernel statistics server +.Sh SYNOPSIS +.Nm /usr/libexec/rpc.rstatd +.Sh DESCRIPTION +.Nm rpc.rstatd +is a server which returns performance statistics obtained from the kernel. +These statistics are read using the +.Xr rup 1 +command. +The +.Nm rpc.rstatd +daemon is normally invoked by +.Xr inetd 8 . +.Pp +.Nm rpc.rstatd +uses an RPC protocol defined in +.Pa /usr/include/rpcsvc/rstat.x . +.Sh SEE ALSO +.Xr rup 1 , +.Xr inetd 8 + diff --git a/libexec/rpc.rstatd/rstat_proc.c b/libexec/rpc.rstatd/rstat_proc.c new file mode 100644 index 0000000..bb67bed --- /dev/null +++ b/libexec/rpc.rstatd/rstat_proc.c @@ -0,0 +1,416 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#ifndef lint +/*static char sccsid[] = "from: @(#)rpc.rstatd.c 1.1 86/09/25 Copyr 1984 Sun Micro";*/ +/*static char sccsid[] = "from: @(#)rstat_proc.c 2.2 88/08/01 4.0 RPCSRC";*/ +static char rcsid[] = "$Id$"; +#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/param.h> +#include <sys/dkstat.h> +#include <sys/errno.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/time.h> +#include <sys/vmmeter.h> + +#include <fcntl.h> +#include <kvm.h> +#include <limits.h> +#include <nlist.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include <net/if.h> +#include <net/if_mib.h> + +#include <rpc/rpc.h> + +#undef FSHIFT /* Use protocol's shift and scale values */ +#undef FSCALE +#undef if_ipackets +#undef if_ierrors +#undef if_opackets +#undef if_oerrors +#undef if_collisions +#include <rpcsvc/rstat.h> + +struct nlist nl[] = { +#define X_CPTIME 0 + { "_cp_time" }, +#define X_CNT 1 + { "_cnt" }, +#define X_DKXFER 2 + { "_dk_xfer" }, +#define X_DKNDRIVE 3 + { "_dk_ndrive" }, + { "" }, +}; +int stats_service(); + +extern int from_inetd; +int sincelastreq = 0; /* number of alarms since last request */ +extern int closedown; + +union { + struct stats s1; + struct statsswtch s2; + struct statstime s3; +} stats_all; + +void updatestat(); +static stat_is_init = 0; +static kvm_t *kd; +extern int errno; + +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 + +stat_init() +{ + stat_is_init = 1; + setup(); + updatestat(); + (void) signal(SIGALRM, updatestat); + alarm(1); +} + +statstime * +rstatproc_stats_3() +{ + if (! stat_is_init) + stat_init(); + sincelastreq = 0; + return(&stats_all.s3); +} + +statsswtch * +rstatproc_stats_2() +{ + if (! stat_is_init) + stat_init(); + sincelastreq = 0; + return(&stats_all.s2); +} + +stats * +rstatproc_stats_1() +{ + if (! stat_is_init) + stat_init(); + sincelastreq = 0; + return(&stats_all.s1); +} + +u_int * +rstatproc_havedisk_3() +{ + static u_int have; + + if (! stat_is_init) + stat_init(); + sincelastreq = 0; + have = havedisk(); + return(&have); +} + +u_int * +rstatproc_havedisk_2() +{ + return(rstatproc_havedisk_3()); +} + +u_int * +rstatproc_havedisk_1() +{ + return(rstatproc_havedisk_3()); +} + +void +updatestat() +{ + int off, i, hz; + struct 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; + + if (kvm_read(kd, (long)nl[X_CPTIME].n_value, (char *)bsd_cp_time, sizeof(bsd_cp_time)) + != sizeof(bsd_cp_time)) { + syslog(LOG_ERR, "rstat: can't read cp_time from kmem\n"); + exit(1); + } + for(i = 0; i < RSTAT_CPUSTATES ; i++) + stats_all.s1.cp_time[i] = bsd_cp_time[cp_time_xlat[i]]; + + (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 + + /* XXX - should use sysctl */ + if (kvm_read(kd, (long)nl[X_CNT].n_value, (char *)&cnt, sizeof cnt) != sizeof cnt) { + syslog(LOG_ERR, "rstat: can't read cnt from kmem\n"); + exit(1); + } + stats_all.s1.v_pgpgin = cnt.v_vnodepgsin; + stats_all.s1.v_pgpgout = cnt.v_vnodepgsout; + stats_all.s1.v_pswpin = cnt.v_swappgsin; + stats_all.s1.v_pswpout = cnt.v_swappgsout; + stats_all.s1.v_intr = cnt.v_intr; + gettimeofday(&tm, (struct timezone *) 0); + stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) + + hz*(tm.tv_usec - btm.tv_usec)/1000000; + stats_all.s2.v_swtch = cnt.v_swtch; + + /* XXX - should use sysctl */ + if (kvm_read(kd, (long)nl[X_DKXFER].n_value, (char *)stats_all.s1.dk_xfer, sizeof (stats_all.s1.dk_xfer)) + != sizeof (stats_all.s1.dk_xfer)) { + syslog(LOG_ERR, "rstat: can't read dk_xfer from kmem\n"); + exit(1); + } + + 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) { + 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; + } + gettimeofday((struct timeval *)&stats_all.s3.curtime, + (struct timezone *) 0); + alarm(1); +} + +setup() +{ + int off; + char errbuf[_POSIX2_LINE_MAX]; + + int en; + + if ((kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf)) == NULL) { + syslog(LOG_ERR, "rpc.rstatd, %s", errbuf); + exit(1); + } + + if ((en = kvm_nlist(kd, nl)) != 0) { + syslog(LOG_ERR, "rstatd: Can't get namelist. %d", en); + exit (1); + } +} + +/* + * returns true if have a disk + */ +havedisk() +{ + int i, cnt; + int dk_ndrive; + + if (kvm_nlist(kd, nl) != 0) { + syslog(LOG_ERR, "rstatd: Can't get namelist.(d)"); + exit (1); + } + + if (kvm_read(kd, (long)nl[X_DKNDRIVE].n_value, (char *)&dk_ndrive, + sizeof dk_ndrive)!= sizeof dk_ndrive) { + syslog(LOG_ERR, "rstat: can't read kmem\n"); + exit(1); + } + return (dk_ndrive != 0); +} + +void +rstat_service(rqstp, transp) + struct svc_req *rqstp; + SVCXPRT *transp; +{ + union { + int fill; + } argument; + char *result; + bool_t (*xdr_argument)(), (*xdr_result)(); + char *(*local)(); + + switch (rqstp->rq_proc) { + case NULLPROC: + (void)svc_sendreply(transp, xdr_void, (char *)NULL); + goto leave; + + case RSTATPROC_STATS: + xdr_argument = xdr_void; + xdr_result = xdr_statstime; + switch (rqstp->rq_vers) { + case RSTATVERS_ORIG: + local = (char *(*)()) rstatproc_stats_1; + break; + case RSTATVERS_SWTCH: + local = (char *(*)()) rstatproc_stats_2; + break; + case RSTATVERS_TIME: + local = (char *(*)()) rstatproc_stats_3; + break; + default: + svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); + goto leave; + /*NOTREACHED*/ + } + break; + + case RSTATPROC_HAVEDISK: + xdr_argument = xdr_void; + xdr_result = xdr_u_int; + switch (rqstp->rq_vers) { + case RSTATVERS_ORIG: + local = (char *(*)()) rstatproc_havedisk_1; + break; + case RSTATVERS_SWTCH: + local = (char *(*)()) rstatproc_havedisk_2; + break; + case RSTATVERS_TIME: + local = (char *(*)()) rstatproc_havedisk_3; + break; + default: + svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); + goto leave; + /*NOTREACHED*/ + } + break; + + default: + svcerr_noproc(transp); + goto leave; + } + bzero((char *)&argument, sizeof(argument)); + if (!svc_getargs(transp, xdr_argument, &argument)) { + svcerr_decode(transp); + goto leave; + } + result = (*local)(&argument, rqstp); + if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_argument, &argument)) { + (void)fprintf(stderr, "unable to free arguments\n"); + exit(1); + } +leave: + if (from_inetd) + exit(0); +} diff --git a/libexec/rpc.rstatd/rstatd.c b/libexec/rpc.rstatd/rstatd.c new file mode 100644 index 0000000..72c449f --- /dev/null +++ b/libexec/rpc.rstatd/rstatd.c @@ -0,0 +1,118 @@ +/*- + * Copyright (c) 1993, John Brezak + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$Id$"; +#endif /* not lint */ + +#include <stdio.h> +#include <rpc/rpc.h> +#include <signal.h> +#include <syslog.h> +#include <rpcsvc/rstat.h> + +extern void rstat_service(); + +int from_inetd = 1; /* started from inetd ? */ +int closedown = 20; /* how long to wait before going dormant */ + +void +cleanup() +{ + (void) pmap_unset(RSTATPROG, RSTATVERS_TIME); + (void) pmap_unset(RSTATPROG, RSTATVERS_SWTCH); + (void) pmap_unset(RSTATPROG, RSTATVERS_ORIG); + exit(0); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + SVCXPRT *transp; + int sock = 0; + int proto = 0; + struct sockaddr_in from; + int fromlen; + + if (argc == 2) + closedown = atoi(argv[1]); + if (closedown <= 0) + closedown = 20; + + /* + * See if inetd started us + */ + fromlen = sizeof(from); + if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { + from_inetd = 0; + sock = RPC_ANYSOCK; + proto = IPPROTO_UDP; + } + + if (!from_inetd) { + daemon(0, 0); + + (void)pmap_unset(RSTATPROG, RSTATVERS_TIME); + (void)pmap_unset(RSTATPROG, RSTATVERS_SWTCH); + (void)pmap_unset(RSTATPROG, RSTATVERS_ORIG); + + (void) signal(SIGINT, cleanup); + (void) signal(SIGTERM, cleanup); + (void) signal(SIGHUP, cleanup); + } + + openlog("rpc.rstatd", LOG_CONS|LOG_PID, LOG_DAEMON); + + transp = svcudp_create(sock); + if (transp == NULL) { + syslog(LOG_ERR, "cannot create udp service."); + exit(1); + } + if (!svc_register(transp, RSTATPROG, RSTATVERS_TIME, rstat_service, proto)) { + syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_TIME, udp)."); + exit(1); + } + if (!svc_register(transp, RSTATPROG, RSTATVERS_SWTCH, rstat_service, proto)) { + syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_SWTCH, udp)."); + exit(1); + } + if (!svc_register(transp, RSTATPROG, RSTATVERS_ORIG, rstat_service, proto)) { + syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_ORIG, udp)."); + exit(1); + } + + svc_run(); + syslog(LOG_ERR, "svc_run returned"); + exit(1); +} diff --git a/libexec/rpc.rusersd/Makefile b/libexec/rpc.rusersd/Makefile new file mode 100644 index 0000000..48f3853 --- /dev/null +++ b/libexec/rpc.rusersd/Makefile @@ -0,0 +1,16 @@ +# $Id$ + +PROG = rpc.rusersd +SRCS = rusersd.c rusers_proc.c +MAN8 = 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/rpc.rusersd.8 b/libexec/rpc.rusersd/rpc.rusersd.8 new file mode 100644 index 0000000..81345f8 --- /dev/null +++ b/libexec/rpc.rusersd/rpc.rusersd.8 @@ -0,0 +1,64 @@ +.\" -*- nroff -*- +.\" +.\" Copyright (c) 1985, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id$ +.\" +.Dd June 7, 1993 +.Dt RPC.RUSERSD 8 +.Os BSD 4.3 +.Sh NAME +.Nm rpc.rusersd +.Nd logged in users server +.Sh SYNOPSIS +.Nm /usr/libexec/rpc.rusersd +.Sh DESCRIPTION +.Nm rpc.rusersd +is a server which returns information about users +currently logged in to the system. +.Pp +The currently logged in users are queried using the +.Xr rusers 1 +command. +The +.Nm rpc.rusersd +daemon is normally invoked by +.Xr inetd 8 . +.Pp +.Nm rpc.rusersd +uses an RPC protocol defined in +.Pa /usr/include/rpcsvc/rnusers.x . +.Sh SEE ALSO +.Xr rusers 1 , +.Xr 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..db6288b --- /dev/null +++ b/libexec/rpc.rusersd/rusers_proc.c @@ -0,0 +1,390 @@ +/*- + * Copyright (c) 1993, John Brezak + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$Id$"; +#endif /* not lint */ + +#include <signal.h> +#include <sys/types.h> +#include <sys/time.h> +#include <utmp.h> +#include <stdio.h> +#include <syslog.h> +#include <rpc/rpc.h> +#include <sys/socket.h> +#include <sys/param.h> +#include <sys/stat.h> +#ifdef XIDLE +#include <setjmp.h> +#include <X11/Xlib.h> +#include <X11/extensions/xidle.h> +#endif +#define utmp rutmp +#include <rpcsvc/rnusers.h> +#undef utmp + +#define IGNOREUSER "sleeper" + +#ifdef OSF +#define _PATH_UTMP UTMP_FILE +#endif + +#ifndef _PATH_UTMP +#define _PATH_UTMP "/etc/utmp" +#endif + +#ifndef _PATH_DEV +#define _PATH_DEV "/dev" +#endif + +#ifndef UT_LINESIZE +#define UT_LINESIZE sizeof(((struct utmp *)0)->ut_line) +#endif +#ifndef UT_NAMESIZE +#define UT_NAMESIZE sizeof(((struct utmp *)0)->ut_name) +#endif +#ifndef UT_HOSTSIZE +#define UT_HOSTSIZE sizeof(((struct utmp *)0)->ut_host) +#endif + +typedef char ut_line_t[UT_LINESIZE+1]; +typedef char ut_name_t[UT_NAMESIZE+1]; +typedef char ut_host_t[UT_HOSTSIZE+1]; + +utmpidle utmp_idle[MAXUSERS]; +rutmp old_utmp[MAXUSERS]; +ut_line_t line[MAXUSERS]; +ut_name_t name[MAXUSERS]; +ut_host_t host[MAXUSERS]; + +extern int from_inetd; + +FILE *ufp; + +#ifdef XIDLE +Display *dpy; + +static jmp_buf openAbort; + +static void +abortOpen () +{ + longjmp (openAbort, 1); +} + +XqueryIdle(char *display) +{ + int first_event, first_error; + Time IdleTime; + + (void) signal (SIGALRM, abortOpen); + (void) alarm ((unsigned) 10); + if (!setjmp (openAbort)) { + if (!(dpy= XOpenDisplay(display))) { + syslog(LOG_ERR, "Cannot open display %s", display); + return(-1); + } + if (XidleQueryExtension(dpy, &first_event, &first_error)) { + if (!XGetIdleTime(dpy, &IdleTime)) { + syslog(LOG_ERR, "%s: Unable to get idle time.", display); + return(-1); + } + } + else { + syslog(LOG_ERR, "%s: Xidle extension not loaded.", display); + return(-1); + } + XCloseDisplay(dpy); + } + else { + syslog(LOG_ERR, "%s: Server grabbed for over 10 seconds.", display); + return(-1); + } + (void) signal (SIGALRM, SIG_DFL); + (void) alarm ((unsigned) 0); + + IdleTime /= 1000; + return((IdleTime + 30) / 60); +} +#endif + +static u_int +getidle(char *tty, char *display) +{ + struct stat st; + char devname[PATH_MAX]; + time_t now; + u_long idle; + + /* + * If this is an X terminal or console, then try the + * XIdle extension + */ +#ifdef XIDLE + if (display && *display && (idle = XqueryIdle(display)) >= 0) + return(idle); +#endif + idle = 0; + if (*tty == 'X') { + u_long kbd_idle, mouse_idle; +#if !defined(__FreeBSD__) + kbd_idle = getidle("kbd", NULL); +#else + kbd_idle = getidle("vga", NULL); +#endif + mouse_idle = getidle("mouse", NULL); + idle = (kbd_idle < mouse_idle)?kbd_idle:mouse_idle; + } + else { + sprintf(devname, "%s/%s", _PATH_DEV, tty); + if (stat(devname, &st) < 0) { +#ifdef DEBUG + printf("%s: %s\n", devname, strerror(errno)); +#endif + return(-1); + } + time(&now); +#ifdef DEBUG + printf("%s: now=%d atime=%d\n", devname, now, + st.st_atime); +#endif + idle = now - st.st_atime; + idle = (idle + 30) / 60; /* secs->mins */ + } + if (idle < 0) idle = 0; + + return(idle); +} + +static utmpidlearr * +do_names_2(int all) +{ + static utmpidlearr ut; + struct utmp usr; + int nusers = 0; + + bzero((char *)&ut, sizeof(ut)); + ut.utmpidlearr_val = &utmp_idle[0]; + + ufp = fopen(_PATH_UTMP, "r"); + if (!ufp) { + syslog(LOG_ERR, "%m"); + return(&ut); + } + + /* only entries with both name and line fields */ + while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1 && + nusers < MAXUSERS) + if (*usr.ut_name && *usr.ut_line && + strncmp(usr.ut_name, IGNOREUSER, + sizeof(usr.ut_name)) +#ifdef OSF + && usr.ut_type == USER_PROCESS +#endif + ) { + utmp_idle[nusers].ui_utmp.ut_time = + usr.ut_time; + utmp_idle[nusers].ui_idle = + getidle(usr.ut_line, usr.ut_host); + utmp_idle[nusers].ui_utmp.ut_line = line[nusers]; + strncpy(line[nusers], usr.ut_line, UT_LINESIZE); + utmp_idle[nusers].ui_utmp.ut_name = name[nusers]; + strncpy(name[nusers], usr.ut_name, UT_NAMESIZE); + utmp_idle[nusers].ui_utmp.ut_host = host[nusers]; + strncpy(host[nusers], usr.ut_host, UT_HOSTSIZE); + + /* Make sure entries are NUL terminated */ + line[nusers][UT_LINESIZE] = + name[nusers][UT_NAMESIZE] = + host[nusers][UT_HOSTSIZE] = '\0'; + nusers++; + } + + ut.utmpidlearr_len = nusers; + fclose(ufp); + return(&ut); +} + +int * +rusers_num() +{ + static int num_users = 0; + struct utmp usr; + + ufp = fopen(_PATH_UTMP, "r"); + if (!ufp) { + syslog(LOG_ERR, "%m"); + return(0); + } + + /* only entries with both name and line fields */ + while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1) + if (*usr.ut_name && *usr.ut_line && + strncmp(usr.ut_name, IGNOREUSER, + sizeof(usr.ut_name)) +#ifdef OSF + && usr.ut_type == USER_PROCESS +#endif + ) { + num_users++; + } + + fclose(ufp); + return(&num_users); +} + +static utmparr * +do_names_1(int all) +{ + utmpidlearr *utidle; + static utmparr ut; + int i; + + bzero((char *)&ut, sizeof(ut)); + + utidle = do_names_2(all); + if (utidle) { + ut.utmparr_len = utidle->utmpidlearr_len; + ut.utmparr_val = &old_utmp[0]; + for (i = 0; i < ut.utmparr_len; i++) + bcopy(&utmp_idle[i].ui_utmp, &old_utmp[i], + sizeof(old_utmp[0])); + + } + + return(&ut); +} + +utmpidlearr * +rusersproc_names_2() +{ + return(do_names_2(0)); +} + +utmpidlearr * +rusersproc_allnames_2() +{ + return(do_names_2(1)); +} + +utmparr * +rusersproc_names_1() +{ + return(do_names_1(0)); +} + +utmparr * +rusersproc_allnames_1() +{ + return(do_names_1(1)); +} + +void +rusers_service(rqstp, transp) + struct svc_req *rqstp; + SVCXPRT *transp; +{ + union { + int fill; + } argument; + char *result; + bool_t (*xdr_argument)(), (*xdr_result)(); + char *(*local)(); + + switch (rqstp->rq_proc) { + case NULLPROC: + (void)svc_sendreply(transp, xdr_void, (char *)NULL); + goto leave; + + case RUSERSPROC_NUM: + xdr_argument = xdr_void; + xdr_result = xdr_int; + local = (char *(*)()) rusers_num; + break; + + case RUSERSPROC_NAMES: + xdr_argument = xdr_void; + xdr_result = xdr_utmpidlearr; + switch (rqstp->rq_vers) { + case RUSERSVERS_ORIG: + local = (char *(*)()) rusersproc_names_1; + break; + case RUSERSVERS_IDLE: + local = (char *(*)()) rusersproc_names_2; + break; + default: + svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE); + goto leave; + /*NOTREACHED*/ + } + break; + + case RUSERSPROC_ALLNAMES: + xdr_argument = xdr_void; + xdr_result = xdr_utmpidlearr; + switch (rqstp->rq_vers) { + case RUSERSVERS_ORIG: + local = (char *(*)()) rusersproc_allnames_1; + break; + case RUSERSVERS_IDLE: + local = (char *(*)()) rusersproc_allnames_2; + break; + default: + svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE); + goto leave; + /*NOTREACHED*/ + } + break; + + default: + svcerr_noproc(transp); + goto leave; + } + bzero((char *)&argument, sizeof(argument)); + if (!svc_getargs(transp, xdr_argument, &argument)) { + svcerr_decode(transp); + goto leave; + } + result = (*local)(&argument, rqstp); + if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_argument, &argument)) { + (void)fprintf(stderr, "unable to free arguments\n"); + exit(1); + } +leave: + if (from_inetd) + exit(0); +} diff --git a/libexec/bugfiler/error.c b/libexec/rpc.rusersd/rusersd.c index bd00b2b..8538797 100644 --- a/libexec/bugfiler/error.c +++ b/libexec/rpc.rusersd/rusersd.c @@ -1,6 +1,6 @@ -/* - * Copyright (c) 1986, 1987, 1993 - * The Regents of the University of California. All rights reserved. +/*- + * 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 @@ -32,58 +32,78 @@ */ #ifndef lint -static char sccsid[] = "@(#)error.c 8.1 (Berkeley) 6/4/93"; +static char rcsid[] = "$Id$"; #endif /* not lint */ -#include <sys/param.h> - -#include <dirent.h> #include <stdio.h> -#include <stdlib.h> -#include <string.h> +#include <rpc/rpc.h> +#include <signal.h> #include <syslog.h> +#define utmp rutmp +#include <rpcsvc/rnusers.h> +#undef utmp -#include "bug.h" -#include "extern.h" +extern void rusers_service(); -static short err_redir; /* stderr redirected */ +int from_inetd = 1; -/* - * seterr -- - * redirect stderr for error processing - */ void -seterr() +cleanup() { - if (!freopen(ERROR_FILE, "a", stderr)) - error("can't open error file %s.", ERROR_FILE); - err_redir = YES; + (void) pmap_unset(RUSERSPROG, RUSERSVERS_IDLE); + (void) pmap_unset(RUSERSPROG, RUSERSVERS_ORIG); + exit(0); } -/* - * error -- - * write errors to log file and die - */ -void -error(fmt, arg) - register char *fmt, - *arg; +main(argc, argv) + int argc; + char *argv[]; { - static char logmsg[MAXLINELEN]; /* syslog message */ + SVCXPRT *transp; + int sock = 0; + int proto = 0; + struct sockaddr_in from; + int fromlen; + + /* + * See if inetd started us + */ + fromlen = sizeof(from); + if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { + from_inetd = 0; + sock = RPC_ANYSOCK; + proto = IPPROTO_UDP; + } + + if (!from_inetd) { + daemon(0, 0); - if (err_redir) { - /* don't combine these, "fmt" may not require "arg" */ - fprintf(stderr, "\t%s\n\t", tmpname); - fprintf(stderr, fmt, arg); - fputc('\n', stderr); + (void) pmap_unset(RUSERSPROG, RUSERSVERS_IDLE); + (void) pmap_unset(RUSERSPROG, RUSERSVERS_ORIG); + + (void) signal(SIGINT, cleanup); + (void) signal(SIGTERM, cleanup); + (void) signal(SIGHUP, cleanup); + } + + openlog("rpc.rusersd", LOG_CONS|LOG_PID, LOG_DAEMON); + + transp = svcudp_create(sock); + if (transp == NULL) { + syslog(LOG_ERR, "cannot create udp service."); + exit(1); } - else { - sprintf(logmsg, "bugfiler: %s", fmt); - syslog(LOG_ERR, logmsg, arg); + if (!svc_register(transp, RUSERSPROG, RUSERSVERS_IDLE, rusers_service, proto)) { + syslog(LOG_ERR, "unable to register (RUSERSPROG, RUSERSVERS_IDLE, %s).", proto?"udp":"(inetd)"); + exit(1); } -#ifdef METOO - exit(ERR); -#else - exit(OK); -#endif + + if (!svc_register(transp, RUSERSPROG, RUSERSVERS_ORIG, rusers_service, proto)) { + syslog(LOG_ERR, "unable to register (RUSERSPROG, RUSERSVERS_ORIG, %s).", proto?"udp":"(inetd)"); + exit(1); + } + + svc_run(); + syslog(LOG_ERR, "svc_run returned"); + exit(1); } diff --git a/libexec/rpc.rwalld/Makefile b/libexec/rpc.rwalld/Makefile new file mode 100644 index 0000000..f9bf277 --- /dev/null +++ b/libexec/rpc.rwalld/Makefile @@ -0,0 +1,10 @@ +# $Id$ + +PROG = rpc.rwalld +SRCS = rwalld.c +MAN8 = rpc.rwalld.8 + +DPADD= ${LIBRPCSVC} ${LIBUTIL} +LDADD= -lrpcsvc -lutil + +.include <bsd.prog.mk> diff --git a/libexec/rpc.rwalld/rpc.rwalld.8 b/libexec/rpc.rwalld/rpc.rwalld.8 new file mode 100644 index 0000000..cbd64a9 --- /dev/null +++ b/libexec/rpc.rwalld/rpc.rwalld.8 @@ -0,0 +1,67 @@ +.\" -*- nroff -*- +.\" +.\" Copyright (c) 1985, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id$ +.\" +.Dd June 7, 1993 +.Dt RPC.RWALLD 8 +.Os BSD 4.3 +.Sh NAME +.Nm rpc.rwalld +.Nd write messages to users currently logged in server +.Sh SYNOPSIS +.Nm /usr/libexec/rpc.rwalld +.Sh DESCRIPTION +.Nm rpc.rwalld +is a server which will send a message to users +currently logged in to the system. This server +invokes the +.Xr wall 1 +command to actually write the messages to the +system. +.Pp +Messages are sent to this server by the +.Xr rwall 1 +command. +The +.Nm rpc.rwalld +daemon is normally invoked by +.Xr inetd 8 . +.Pp +.Nm rpc.rwalld +uses an RPC protocol defined in +.Pa /usr/include/rpcsvc/rwall.x . +.Sh SEE ALSO +.Xr rwall 1 , +.Xr wall 1 , +.Xr inetd 8 diff --git a/libexec/rpc.rwalld/rwalld.c b/libexec/rpc.rwalld/rwalld.c new file mode 100644 index 0000000..90b0ffe --- /dev/null +++ b/libexec/rpc.rwalld/rwalld.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 1993 Christopher G. Demetriou + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$Id$"; +#endif /* not lint */ + +#include <unistd.h> +#include <sys/types.h> +#include <pwd.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/socket.h> +#include <signal.h> +#include <sys/wait.h> +#include <rpc/rpc.h> +#include <rpcsvc/rwall.h> + +#ifdef OSF +#define WALL_CMD "/usr/sbin/wall" +#else +#define WALL_CMD "/usr/bin/wall -n" +#endif + +void wallprog_1(); +void possess(); +void killkids(); + +int nodaemon = 0; +int from_inetd = 1; + +main(argc, argv) + int argc; + char *argv[]; +{ + SVCXPRT *transp; + int s, salen; + struct sockaddr_in sa; + int sock = 0; + int proto = 0; + + if (argc == 2 && !strcmp(argv[1], "-n")) + nodaemon = 1; + if (argc != 1 && !nodaemon) { + printf("usage: %s [-n]\n", argv[0]); + exit(1); + } + + if (geteuid() == 0) { + struct passwd *pep = getpwnam("nobody"); + if (pep) + setuid(pep->pw_uid); + else + setuid(getuid()); + } + + /* + * See if inetd started us + */ + salen = sizeof(sa); + if (getsockname(0, (struct sockaddr *)&sa, &salen) < 0) { + from_inetd = 0; + sock = RPC_ANYSOCK; + proto = IPPROTO_UDP; + } + + if (!from_inetd) { + if (!nodaemon) + possess(); + + (void)pmap_unset(WALLPROG, WALLVERS); + if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + perror("socket"); + exit(1); + } + bzero((char *)&sa, sizeof sa); + if (bind(s, (struct sockaddr *)&sa, sizeof sa) < 0) { + perror("bind"); + exit(1); + } + + salen = sizeof sa; + if (getsockname(s, (struct sockaddr *)&sa, &salen)) { + perror("getsockname"); + exit(1); + } + + pmap_set(WALLPROG, WALLVERS, IPPROTO_UDP, ntohs(sa.sin_port)); + if (dup2(s, 0) < 0) { + perror("dup2"); + exit(1); + } + (void)pmap_unset(WALLPROG, WALLVERS); + } + + (void)signal(SIGCHLD, killkids); + + transp = svcudp_create(sock); + if (transp == NULL) { + (void)fprintf(stderr, "cannot create udp service.\n"); + exit(1); + } + if (!svc_register(transp, WALLPROG, WALLVERS, wallprog_1, proto)) { + (void)fprintf(stderr, "unable to register (WALLPROG, WALLVERS, udp).\n"); + exit(1); + } + svc_run(); + (void)fprintf(stderr, "svc_run returned\n"); + exit(1); + +} + +void possess() +{ + daemon(0, 0); +} + +void killkids() +{ + while(wait4(-1, NULL, WNOHANG, NULL) > 0) + ; +} + +void *wallproc_wall_1(s) + char **s; +{ + /* fork, popen wall with special option, and send the message */ + if (fork() == 0) { + FILE *pfp; + + pfp = popen(WALL_CMD, "w"); + if (pfp != NULL) { + fprintf(pfp, "\007\007%s", *s); + pclose(pfp); + exit(0); + } + } +} + +void +wallprog_1(rqstp, transp) + struct svc_req *rqstp; + SVCXPRT *transp; +{ + union { + char *wallproc_wall_1_arg; + } argument; + char *result; + bool_t (*xdr_argument)(), (*xdr_result)(); + char *(*local)(); + + switch (rqstp->rq_proc) { + case NULLPROC: + (void)svc_sendreply(transp, xdr_void, (char *)NULL); + goto leave; + + case WALLPROC_WALL: + xdr_argument = xdr_wrapstring; + xdr_result = xdr_void; + local = (char *(*)()) wallproc_wall_1; + break; + + default: + svcerr_noproc(transp); + goto leave; + } + bzero((char *)&argument, sizeof(argument)); + if (!svc_getargs(transp, xdr_argument, &argument)) { + svcerr_decode(transp); + goto leave; + } + result = (*local)(&argument, rqstp); + if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_argument, &argument)) { + (void)fprintf(stderr, "unable to free arguments\n"); + exit(1); + } +leave: + if (from_inetd) + exit(0); +} diff --git a/libexec/rpc.sprayd/Makefile b/libexec/rpc.sprayd/Makefile new file mode 100644 index 0000000..233c4d8 --- /dev/null +++ b/libexec/rpc.sprayd/Makefile @@ -0,0 +1,11 @@ +# $Id$ + +PROG = rpc.sprayd +SRCS = sprayd.c +MAN8 = rpc.sprayd.8 + +DPADD= ${LIBRPCSVC} +LDADD= -lrpcsvc + +.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..1b7b823 --- /dev/null +++ b/libexec/rpc.sprayd/rpc.sprayd.8 @@ -0,0 +1,54 @@ +.\" +.\" 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. +.\" +.\" $Id$ +.\" +.Dd June 22, 1994 +.Dt RPC.SPRAYD 8 +.Os BSD 4.3 +.Sh NAME +.Nm rpc.sprayd +.Nd spray server +.Sh SYNOPSIS +.Nm /usr/libexec/rpc.sprayd +.Sh DESCRIPTION +.Nm rpc.sprayd +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 rpc.sprayd +daemon is normally invoked by +.Xr inetd 8 . +.Pp +.Nm rpc.sprayd +uses an 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..44883f4 --- /dev/null +++ b/libexec/rpc.sprayd/sprayd.c @@ -0,0 +1,169 @@ +/* + * 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. + * + * $Id$ + */ + +#ifndef lint +static char rcsid[] = "$Id$"; +#endif /* not lint */ + +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <rpc/rpc.h> +#include <rpc/pmap_clnt.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <syslog.h> +#include <rpcsvc/spray.h> + +static void spray_service __P((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() +{ + (void) pmap_unset(SPRAYPROG, SPRAYVERS); + exit(0); +} + +void +die() +{ + exit(0); +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + SVCXPRT *transp; + int sock = 0; + int proto = 0; + struct sockaddr_in from; + int fromlen; + + /* + * See if inetd started us + */ + fromlen = sizeof(from); + if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { + from_inetd = 0; + sock = RPC_ANYSOCK; + proto = IPPROTO_UDP; + } + + if (!from_inetd) { + daemon(0, 0); + + (void) pmap_unset(SPRAYPROG, SPRAYVERS); + + (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); + + transp = svcudp_create(sock); + if (transp == NULL) { + syslog(LOG_ERR, "cannot create udp service."); + return 1; + } + if (!svc_register(transp, SPRAYPROG, SPRAYVERS, spray_service, proto)) { + syslog(LOG_ERR, + "unable to register (SPRAYPROG, SPRAYVERS, %s).", + proto ? "udp" : "(inetd)"); + return 1; + } + + svc_run(); + syslog(LOG_ERR, "svc_run returned"); + return 1; +} + + +static void +spray_service(rqstp, transp) + 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, xdr_void, (char *)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, xdr_spraycumul, (caddr_t)&scum)) { + svcerr_systemerr(transp); + syslog(LOG_ERR, "bad svc_sendreply"); + } +} diff --git a/libexec/rshd/Makefile b/libexec/rshd/Makefile index 0b448aa..c94f453 100644 --- a/libexec/rshd/Makefile +++ b/libexec/rshd/Makefile @@ -1,11 +1,21 @@ -# @(#)Makefile 8.1 (Berkeley) 6/4/93 +# From: @(#)Makefile 8.1 (Berkeley) 6/4/93 +# $Id: Makefile,v 1.6 1997/02/22 14:22:22 peter Exp $ PROG= rshd +SRCS= rshd.c +MAN8= rshd.8 + +.if exists(${DESTDIR}/usr/lib/libkrb.a) && (defined(MAKE_KERBEROS) \ + || defined(MAKE_EBONES)) CFLAGS+=-DKERBEROS -DCRYPT -SRCS= rshd.c des_rw.c -MAN8= rshd.0 DPADD= ${LIBKRB} ${LIBDES} LDADD= -lkrb -ldes -.PATH: ${.CURDIR}/../../usr.bin/rlogin +DISTRIBUTION= krb +.endif + +# For login_cap handling +CFLAGS+=-DLOGIN_CAP +DPADD+= ${LIBUTIL} +LDADD+= -lutil .include <bsd.prog.mk> diff --git a/libexec/rshd/rshd.8 b/libexec/rshd/rshd.8 index 82e1991..1f2ae32 100644 --- a/libexec/rshd/rshd.8 +++ b/libexec/rshd/rshd.8 @@ -30,6 +30,7 @@ .\" SUCH DAMAGE. .\" .\" @(#)rshd.8 8.1 (Berkeley) 6/4/93 +.\" $Id: rshd.8,v 1.8 1997/04/23 03:06:47 davidn Exp $ .\" .Dd June 4, 1993 .Dt RSHD 8 @@ -106,7 +107,7 @@ machine. A null terminated user name of at most 16 characters is retrieved on the initial socket. This user name is interpreted as a user identity to use on the -.Sy server Ns 's +.Em server Ns 's machine. .It A null terminated command to be passed to a @@ -132,6 +133,12 @@ If the file .Pa /etc/nologin exists and the user is not the superuser, the connection is closed. +The name of the nologin file may be overridden +using the nologin= capability in login.conf +according to the local user's login class, +which may also be used to restrict rsh access by +login time (times.allow and times.deny capabilities) +and remote host (hosts.allow and hosts.deny capabilities). .It A null byte is returned on the initial socket and the command line is passed to the normal login @@ -173,13 +180,15 @@ longer than 16 characters. 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. +No password file entry for the user name existed +or the authentication procedure described above failed. .It Sy Remote directory. The .Xr chdir command to the home directory failed. -.It Sy Permission denied. -The authentication procedure described above failed. +.It Sy Logins not available right now. +Rsh was attempted outside the allowed hours defined in +login.conf for the local user's login class. .It Sy Can't make pipe. The pipe needed for the .Em stderr , @@ -195,9 +204,25 @@ on the connection associated with the 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 ruserok 3 , +.Xr hosts 5 , +.Xr login.conf 5 , +.Xr nologin 5 , +.Xr services 5 , +.Xr named 8 , +.Xr rlogind 8 , +.Xr syslogd 8 . +.Sh FILES +.Bl -tag -width /etc/hosts -compact +.It Pa /etc/hosts +.It Pa /etc/hosts.equiv +.It Pa $HOME/.rhosts +.It Pa /etc/nologin +.El .Sh BUGS The authentication procedure used here assumes the integrity of each client machine and the connecting medium. This is diff --git a/libexec/rshd/rshd.c b/libexec/rshd/rshd.c index 1b9eea9..dbb1351 100644 --- a/libexec/rshd/rshd.c +++ b/libexec/rshd/rshd.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id: rshd.c,v 1.17 1997/05/10 19:02:03 davidn Exp $ */ #ifndef lint @@ -54,7 +56,9 @@ static char sccsid[] = "@(#)rshd.c 8.2 (Berkeley) 4/6/94"; #include <sys/time.h> #include <sys/socket.h> +#include <netinet/in_systm.h> #include <netinet/in.h> +#include <netinet/ip.h> #include <arpa/inet.h> #include <netdb.h> @@ -68,6 +72,9 @@ static char sccsid[] = "@(#)rshd.c 8.2 (Berkeley) 4/6/94"; #include <string.h> #include <syslog.h> #include <unistd.h> +#ifdef LOGIN_CAP +#include <login_cap.h> +#endif int keepalive = 1; int check_all; @@ -82,7 +89,7 @@ char *topdomain __P((char *)); void usage __P((void)); #ifdef KERBEROS -#include <kerberosIV/des.h> +#include <des.h> #include <kerberosIV/krb.h> #define VERSION_SIZE 9 #define SECURE_MESSAGE "This rsh session is using DES encryption for all transmissions.\r\n" @@ -108,7 +115,7 @@ main(argc, argv) openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON); opterr = 0; - while ((ch = getopt(argc, argv, OPTIONS)) != EOF) + while ((ch = getopt(argc, argv, OPTIONS)) != -1) switch (ch) { case 'a': check_all = 1; @@ -200,6 +207,10 @@ doit(fromp) char *cp, sig, buf[BUFSIZ]; char cmdbuf[NCARGS+1], locuser[16], remuser[16]; char remotehost[2 * MAXHOSTNAMELEN + 1]; + char fromhost[2 * MAXHOSTNAMELEN + 1]; +#ifdef LOGIN_CAP + login_cap_t *lc; +#endif #ifdef KERBEROS AUTH_DAT *kdata = (AUTH_DAT *) NULL; @@ -233,9 +244,8 @@ doit(fromp) } #ifdef IP_OPTIONS { - u_char optbuf[BUFSIZ/3], *cp; - char lbuf[BUFSIZ], *lp; - int optsize = sizeof(optbuf), ipproto; + u_char optbuf[BUFSIZ/3]; + int optsize = sizeof(optbuf), ipproto, i; struct protoent *ip; if ((ip = getprotobyname("ip")) != NULL) @@ -244,16 +254,18 @@ doit(fromp) ipproto = IPPROTO_IP; if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) && optsize != 0) { - lp = lbuf; - for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) - sprintf(lp, " %2.2x", *cp); - syslog(LOG_NOTICE, - "Connection received from %s using IP options (ignored):%s", - inet_ntoa(fromp->sin_addr), lbuf); - if (setsockopt(0, ipproto, IP_OPTIONS, - (char *)NULL, optsize) != 0) { - syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m"); - exit(1); + 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->sin_addr), + c == IPOPT_LSRR ? "LSRR" : "SSRR"); + exit(1); + } + if (c == IPOPT_EOL) + break; + i += (c == IPOPT_NOP) ? 1 : optbuf[i+1]; } } } @@ -297,8 +309,12 @@ doit(fromp) #ifdef KERBEROS if (!use_kerberos) #endif - if (port >= IPPORT_RESERVED) { - syslog(LOG_ERR, "2nd port not reserved\n"); + if (port >= IPPORT_RESERVED || + port < IPPORT_RESERVED/2) { + syslog(LOG_NOTICE|LOG_AUTH, + "2nd socket from %s on unreserved port %u", + inet_ntoa(fromp->sin_addr), + port); exit(1); } fromp->sin_port = htons(port); @@ -331,7 +347,9 @@ doit(fromp) * in a remote net; look up the name and check that this * address corresponds to the name. */ - hostname = hp->h_name; + strncpy(fromhost, hp->h_name, sizeof(fromhost) - 1); + fromhost[sizeof(fromhost) - 1] = 0; + hostname = fromhost; #ifdef KERBEROS if (!use_kerberos) #endif @@ -346,7 +364,10 @@ doit(fromp) remotehost); errorstr = "Couldn't look up address for your host (%s)\n"; - hostname = inet_ntoa(fromp->sin_addr); + strncpy(fromhost, inet_ntoa(fromp->sin_addr), + sizeof(fromhost) - 1); + fromhost[sizeof(fromhost) - 1] = 0; + hostname = fromhost; } else for (; ; hp->h_addr_list++) { if (hp->h_addr_list[0] == NULL) { syslog(LOG_NOTICE, @@ -355,19 +376,26 @@ doit(fromp) hp->h_name); errorstr = "Host address mismatch for %s\n"; - hostname = inet_ntoa(fromp->sin_addr); + strncpy(fromhost, inet_ntoa(fromp->sin_addr), + sizeof(fromhost) - 1); + fromhost[sizeof(fromhost) - 1] = 0; + hostname = fromhost; break; } if (!bcmp(hp->h_addr_list[0], (caddr_t)&fromp->sin_addr, sizeof(fromp->sin_addr))) { - hostname = hp->h_name; + hostname = remotehost; break; } } } - } else - errorhost = hostname = inet_ntoa(fromp->sin_addr); + } else { + strncpy(fromhost, inet_ntoa(fromp->sin_addr), + sizeof(fromhost) - 1); + fromhost[sizeof(fromhost) - 1] = 0; + errorhost = hostname = fromhost; + } #ifdef KERBEROS if (use_kerberos) { @@ -391,13 +419,13 @@ doit(fromp) "rcmd", instance, &fromaddr, &local_addr, kdata, "", schedule, version); - des_set_key(kdata->session, schedule); + des_set_key_krb(&kdata->session, schedule); } else #endif rc = krb_recvauth(authopts, 0, ticket, "rcmd", instance, &fromaddr, (struct sockaddr_in *) 0, - kdata, "", (bit_64 *) 0, version); + kdata, "", NULL, version); if (rc != KSUCCESS) { error("Kerberos authentication failure: %s\n", krb_err_txt[rc]); @@ -419,7 +447,20 @@ doit(fromp) errorstr = "Login incorrect.\n"; goto fail; } +#ifdef LOGIN_CAP + lc = login_getpwclass(pwd); +#endif if (chdir(pwd->pw_dir) < 0) { +#ifdef LOGIN_CAP + if (chdir("/") < 0 || + login_getcapbool(lc, "requirehome", !!pwd->pw_uid)) { + syslog(LOG_INFO|LOG_AUTH, + "%s@%s as %s: no home directory. cmd='%.80s'", + remuser, hostname, locuser, cmdbuf); + error("No remote home directory.\n"); + exit(0); + } +#else (void) chdir("/"); #ifdef notdef syslog(LOG_INFO|LOG_AUTH, @@ -428,6 +469,8 @@ doit(fromp) error("No remote directory.\n"); exit(1); #endif +#endif + pwd->pw_dir = "/"; } #ifdef KERBEROS @@ -437,7 +480,7 @@ doit(fromp) syslog(LOG_INFO|LOG_AUTH, "Kerberos rsh denied to %s.%s@%s", kdata->pname, kdata->pinst, kdata->prealm); - error("Permission denied.\n"); + error("Login incorrect.\n"); exit(1); } } @@ -445,9 +488,10 @@ doit(fromp) #endif if (errorstr || - pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' && + (pwd->pw_expire && time(NULL) >= pwd->pw_expire) || + (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' && iruserok(fromp->sin_addr.s_addr, pwd->pw_uid == 0, - remuser, locuser) < 0) { + remuser, locuser) < 0)) { if (__rcmd_errstr) syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: permission denied (%s). cmd='%.80s'", @@ -459,7 +503,7 @@ doit(fromp) remuser, hostname, locuser, cmdbuf); fail: if (errorstr == NULL) - errorstr = "Permission denied.\n"; + errorstr = "Login incorrect.\n"; error(errorstr, errorhost); exit(1); } @@ -468,6 +512,32 @@ fail: error("Logins currently disabled.\n"); exit(1); } +#ifdef LOGIN_CAP + if (lc != NULL) { + char remote_ip[MAXHOSTNAMELEN]; + + strncpy(remote_ip, inet_ntoa(fromp->sin_addr), + sizeof(remote_ip) - 1); + remote_ip[sizeof(remote_ip) - 1] = 0; + if (!auth_hostok(lc, fromhost, remote_ip)) { + syslog(LOG_INFO|LOG_AUTH, + "%s@%s as %s: permission denied (%s). cmd='%.80s'", + remuser, hostname, locuser, __rcmd_errstr, + cmdbuf); + error("Login incorrect.\n"); + exit(1); + } + if (!auth_timeok(lc, time(NULL))) { + error("Logins not available right now\n"); + exit(1); + } + } +#endif /* !LOGIN_CAP */ +#if BSD > 43 + /* before fork, while we're session leader */ + if (setlogin(pwd->pw_name) < 0) + syslog(LOG_ERR, "setlogin() failed: %m"); +#endif (void) write(STDERR_FILENO, "\0", 1); sent_null = 1; @@ -642,13 +712,6 @@ fail: } if (*pwd->pw_shell == '\0') pwd->pw_shell = _PATH_BSHELL; -#if BSD > 43 - if (setlogin(pwd->pw_name) < 0) - syslog(LOG_ERR, "setlogin() failed: %m"); -#endif - (void) setgid((gid_t)pwd->pw_gid); - initgroups(pwd->pw_name, pwd->pw_gid); - (void) setuid((uid_t)pwd->pw_uid); environ = envinit; strncat(homedir, pwd->pw_dir, sizeof(homedir)-6); strcat(path, _PATH_DEFPATH); @@ -659,6 +722,17 @@ fail: cp++; else cp = pwd->pw_shell; +#ifdef LOGIN_CAP + if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL) != 0) { + syslog(LOG_ERR, "setusercontext: %m"); + exit(1); + } + login_close(lc); +#else + (void) setgid((gid_t)pwd->pw_gid); + initgroups(pwd->pw_name, pwd->pw_gid); + (void) setuid((uid_t)pwd->pw_uid); +#endif endpwent(); if (log_success || pwd->pw_uid == 0) { #ifdef KERBEROS diff --git a/libexec/rtld-aout/Makefile b/libexec/rtld-aout/Makefile new file mode 100644 index 0000000..4177f6b --- /dev/null +++ b/libexec/rtld-aout/Makefile @@ -0,0 +1,20 @@ +# $Id: Makefile,v 1.23 1997/02/22 15:46:46 peter Exp $ + +PROG= ld.so +SRCS= mdprologue.S rtld.c malloc.c shlib.c md.c support.c sbrk.c +MAN1= rtld.1 +LDDIR?= $(.CURDIR)/.. +# As there is relocation going on behind GCC's back, don't cache function addresses. +PICFLAG=-fpic -fno-function-cse +CFLAGS+=-I$(LDDIR) -I$(.CURDIR) -I$(LDDIR)/$(MACHINE) $(PICFLAG) -DRTLD +LDFLAGS+=-nostdlib -Wl,-Bshareable -Wl,-Bsymbolic -Wl,-assert -Wl,nosymbolic +ASFLAGS+=-k +DPADD+= ${LIBC:S/c.a/c_pic.a/} ${LIBC:S/c.a/gcc_pic.a/} +LDADD+= -lc_pic -lgcc_pic +BINDIR= /usr/libexec +INSTALLFLAGS+= -fschg -C # -C to install as atomically as possible +MLINKS= rtld.1 ld.so.1 + +.PATH: $(LDDIR) $(LDDIR)/$(MACHINE) + +.include <bsd.prog.mk> diff --git a/libexec/rtld-aout/dynamic.h b/libexec/rtld-aout/dynamic.h new file mode 100644 index 0000000..c1890db --- /dev/null +++ b/libexec/rtld-aout/dynamic.h @@ -0,0 +1,380 @@ +/* + * Copyright (c) 1993 Paul Kranenburg + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Paul Kranenburg. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: dynamic.h,v 1.3 1997/02/22 15:46:18 peter Exp $ + */ + +#ifndef __DYNAMIC_H__ +#define __DYNAMIC_H__ + +#define SUN_COMPAT + +#include "md.h" +#include "link.h" + +#ifndef RELOC_JMPTAB_P + +#define RELOC_JMPTAB_P(r) ((r)->r_jmptable) +#define RELOC_BASEREL_P(r) ((r)->r_baserel) +#define RELOC_RELATIVE_P(r) ((r)->r_relative) +#define RELOC_COPY_P(r) ((r)->r_copy) +#define RELOC_LAZY_P(r) ((r)->r_jmptable) + +#define CHECK_GOT_RELOC(r) ((r)->r_pcrel) +#define RELOC_PIC_TYPE(r) ((r)->r_baserel? \ + PIC_TYPE_LARGE:PIC_TYPE_NONE) +#endif + +#ifndef RELOC_INIT_SEGMENT_RELOC +#define RELOC_INIT_SEGMENT_RELOC(r) +#endif + +#ifndef MAX_GOTOFF +#define MAX_GOTOFF(x) (LONG_MAX) +#endif + +#ifndef MIN_GOTOFF +#define MIN_GOTOFF(x) (LONG_MIN) +#endif + +/* + * Internal representation of relocation types + */ +#define RELTYPE_EXTERN 1 +#define RELTYPE_JMPSLOT 2 +#define RELTYPE_BASEREL 4 +#define RELTYPE_RELATIVE 8 +#define RELTYPE_COPY 16 + +#define N_ISWEAK(p) (N_BIND(p) & BIND_WEAK) + +typedef struct localsymbol { + struct nzlist nzlist; /* n[z]list from file */ + struct glosym *symbol; /* Corresponding global symbol, + if any */ + struct localsymbol *next; /* List of definitions */ + struct file_entry *entry; /* Backpointer to file */ + long gotslot_offset; /* Position in GOT, if any */ + int symbolnum; /* Position in output nlist */ + int flags; +#define LS_L_SYMBOL 1 /* Local symbol starts with an `L' */ +#define LS_WRITE 2 /* Symbol goes in output symtable */ +#define LS_RENAME 4 /* xlat name to `<file>.<name>' */ +#define LS_HASGOTSLOT 8 /* This symbol has a GOT entry */ +#define LS_WARNING 16 /* Second part of a N_WARNING duo */ +} localsymbol_t; + +/* + * Global symbol data is recorded in these structures, one for each global + * symbol. They are found via hashing in 'symtab', which points to a vector + * of buckets. Each bucket is a chain of these structures through the link + * field. + * + * Rewritten version to support extra info for dynamic linking. + */ + +struct glosym { + struct glosym *link; /* Next symbol hash bucket. */ + char *name; /* Name of this symbol. */ + long value; /* Value of this symbol */ + localsymbol_t *refs; /* Chain of local symbols from object + files pertaining to this global + symbol */ + localsymbol_t *sorefs;/* Same for local symbols from shared + object files. */ + + char *warning; /* message, from N_WARNING nlists */ + int common_size; /* Common size */ + int symbolnum; /* Symbol index in output symbol table */ + int rrs_symbolnum; /* Symbol index in RRS symbol table */ + + localsymbol_t *def_lsp; /* The local symbol that gave this + global symbol its definition */ + + char defined; /* Definition of this symbol */ + char so_defined; /* Definition of this symbol in a shared + object. These go into the RRS symbol table */ + u_char undef_refs; /* Count of number of "undefined" + messages printed for this symbol */ + u_char mult_defs; /* Same for "multiply defined" symbols */ + struct glosym *alias; /* For symbols of type N_INDR, this + points at the real symbol. */ + int setv_count; /* Number of elements in N_SETV symbols */ + int size; /* Size of this symbol (either from N_SIZE + symbols or a from shared object's RRS */ + int aux; /* Auxiliary type information conveyed in + the `n_other' field of nlists */ + + /* The offset into one of the RRS tables, -1 if not used */ + long jmpslot_offset; + long gotslot_offset; + + long flags; + +#define GS_DEFINED 0x1 /* Symbol has definition (notyetused)*/ +#define GS_REFERENCED 0x2 /* Symbol is referred to by something + interesting */ +#define GS_TRACE 0x4 /* Symbol will be traced */ +#define GS_HASJMPSLOT 0x8 /* */ +#define GS_HASGOTSLOT 0x10 /* Some state bits concerning */ +#define GS_CPYRELOCRESERVED 0x20 /* entries in GOT and PLT tables */ +#define GS_CPYRELOCCLAIMED 0x40 /* */ +#define GS_WEAK 0x80 /* Symbol is weakly defined */ + +}; +#ifndef __symbol_defined__ +#define __symbol_defined__ +typedef struct glosym symbol; +#endif + +/* The symbol hash table: a vector of SYMTABSIZE pointers to struct glosym. */ +extern symbol *symtab[]; +#define FOR_EACH_SYMBOL(i,sp) { \ + int i; \ + for (i = 0; i < SYMTABSIZE; i++) { \ + register symbol *sp; \ + for (sp = symtab[i]; sp; sp = sp->link) + +#define END_EACH_SYMBOL }} + +extern symbol *got_symbol; /* the symbol __GLOBAL_OFFSET_TABLE_ */ +extern symbol *dynamic_symbol; /* the symbol __DYNAMIC */ + +/* + * Each input file, and each library member ("subfile") being loaded, has a + * `file_entry' structure for it. + * + * For files specified by command args, these are contained in the vector which + * `file_table' points to. + * + * For library members, they are dynamically allocated, and chained through the + * `chain' field. The chain is found in the `subfiles' field of the + * `file_entry'. The `file_entry' objects for the members have `superfile' + * fields pointing to the one for the library. + * + * Rewritten version to support extra info for dynamic linking. + */ + +struct file_entry { + char *filename; /* Name of this file. */ + /* + * Name to use for the symbol giving address of text start Usually + * the same as filename, but for a file spec'd with -l this is the -l + * switch itself rather than the filename. + */ + char *local_sym_name; + struct exec header; /* The file's a.out header. */ + localsymbol_t *symbols; /* Symbol table of the file. */ + int nsymbols; /* Number of symbols in above array. */ + int string_size; /* Size in bytes of string table. */ + char *strings; /* Pointer to the string table when + in core, NULL otherwise */ + int strings_offset; /* Offset of string table, + (normally N_STROFF() + 4) */ + /* + * Next two used only if `relocatable_output' or if needed for + * output of undefined reference line numbers. + */ + struct relocation_info *textrel; /* Text relocations */ + int ntextrel; /* # of text relocations */ + struct relocation_info *datarel; /* Data relocations */ + int ndatarel; /* # of data relocations */ + + /* + * Relation of this file's segments to the output file. + */ + int text_start_address; /* Start of this file's text segment + in the output file core image. */ + int data_start_address; /* Start of this file's data segment + in the output file core image. */ + int bss_start_address; /* Start of this file's bss segment + in the output file core image. */ + struct file_entry *subfiles; /* For a library, points to chain of + entries for the library members. */ + struct file_entry *superfile; /* For library member, points to the + library's own entry. */ + struct file_entry *chain; /* For library member, points to next + entry for next member. */ + int starting_offset; /* For a library member, offset of the + member within the archive. Zero for + files that are not library members.*/ + int total_size; /* Size of contents of this file, + if library member. */ +#ifdef SUN_COMPAT + struct file_entry *silly_archive;/* For shared libraries which have + a .sa companion */ +#endif + int lib_major, lib_minor; /* Version numbers of a shared object */ + + int flags; +#define E_IS_LIBRARY 1 /* File is a an archive */ +#define E_HEADER_VALID 2 /* File's header has been read */ +#define E_SEARCH_DIRS 4 /* Search directories for file */ +#define E_SEARCH_DYNAMIC 8 /* Search for shared libs allowed */ +#define E_JUST_SYMS 0x10 /* File is used for incremental load */ +#define E_DYNAMIC 0x20 /* File is a shared object */ +#define E_SCRAPPED 0x40 /* Ignore this file */ +#define E_SYMBOLS_USED 0x80 /* Symbols from this entry were used */ +#define E_SECONDCLASS 0x100 /* Shared object is a subsidiary */ +}; + +/* + * Runtime Relocation Section (RRS). + * This describes the data structures that go into the output text and data + * segments to support the run-time linker. The RRS can be empty (plain old + * static linking), or can just exist of GOT and PLT entries (in case of + * statically linked PIC code). + */ +extern int rrs_section_type; /* What's in the RRS section */ +#define RRS_NONE 0 +#define RRS_PARTIAL 1 +#define RRS_FULL 2 +extern int rrs_text_size; /* Size of RRS text additions */ +extern int rrs_text_start; /* Location of above */ +extern int rrs_data_size; /* Size of RRS data additions */ +extern int rrs_data_start; /* Location of above */ +extern char *rrs_search_paths; /* `-L' RT paths */ + +/* Version number to put in __DYNAMIC (set by -V) */ +extern int soversion; +#ifndef DEFAULT_SOVERSION +#define DEFAULT_SOVERSION LD_VERSION_BSD +#endif + +extern int pc_relocation; /* Current PC reloc value */ + +extern int number_of_shobjs; /* # of shared objects linked in */ + +/* Current link mode */ +extern int link_mode; +#define DYNAMIC 1 /* Consider shared libraries */ +#define SYMBOLIC 2 /* Force symbolic resolution */ +#define FORCEARCHIVE 4 /* Force inclusion of all members + of archives */ +#define SHAREABLE 8 /* Build a shared object */ +#define SILLYARCHIVE 16 /* Process .sa companions, if any */ +#define FORCEDYNAMIC 32 /* Force dynamic output even if no + shared libraries included */ +#define WARNRRSTEXT 64 /* Warn about rrs in text */ + +extern FILE *outstream; /* Output file. */ +extern struct exec outheader; /* Output file header. */ +extern int magic; /* Output file magic. */ +extern int oldmagic; +extern int relocatable_output; +extern int pic_type; +#define PIC_TYPE_NONE 0 +#define PIC_TYPE_SMALL 1 +#define PIC_TYPE_LARGE 2 + +void read_header __P((int, struct file_entry *)); +void read_entry_symbols __P((int, struct file_entry *)); +void read_entry_strings __P((int, struct file_entry *)); +void read_entry_relocation __P((int, struct file_entry *)); +void enter_file_symbols __P((struct file_entry *)); +void read_file_symbols __P((struct file_entry *)); +int set_element_prefixed_p __P((char *)); +int text_offset __P((struct file_entry *)); +int file_open __P((struct file_entry *)); +void each_file __P((void (*)(), void *)); +void each_full_file __P((void (*)(), void *)); +unsigned long check_each_file __P((unsigned long (*)(), void *)); +void mywrite __P((void *, int, int, FILE *)); +void padfile __P((int, FILE *)); + +/* In warnings.c: */ +void perror_name __P((char *)); +void perror_file __P((struct file_entry *)); +void print_symbols __P((FILE *)); +char *get_file_name __P((struct file_entry *)); +void print_file_name __P((struct file_entry *, FILE *)); +void prline_file_name __P((struct file_entry *, FILE *)); +int do_warnings __P((FILE *)); + +/* In etc.c: */ +#include "support.h" + +/* In symbol.c: */ +void symtab_init __P((int)); +symbol *getsym __P((char *)), *getsym_soft __P((char *)); + +/* In lib.c: */ +void search_library __P((int, struct file_entry *)); +void read_shared_object __P((int, struct file_entry *)); +int findlib __P((struct file_entry *)); + +/* In shlib.c: */ +#include "shlib.h" + +/* In rrs.c: */ +void init_rrs __P((void)); +int rrs_add_shobj __P((struct file_entry *)); +void alloc_rrs_reloc __P((struct file_entry *, symbol *)); +void alloc_rrs_segment_reloc __P((struct file_entry *, struct relocation_info *)); +void alloc_rrs_jmpslot __P((struct file_entry *, symbol *)); +void alloc_rrs_gotslot __P((struct file_entry *, struct relocation_info *, localsymbol_t *)); +void alloc_rrs_cpy_reloc __P((struct file_entry *, symbol *)); + +int claim_rrs_reloc __P((struct file_entry *, struct relocation_info *, symbol *, long *)); +long claim_rrs_jmpslot __P((struct file_entry *, struct relocation_info *, symbol *, long)); +long claim_rrs_gotslot __P((struct file_entry *, struct relocation_info *, struct localsymbol *, long)); +long claim_rrs_internal_gotslot __P((struct file_entry *, struct relocation_info *, struct localsymbol *, long)); +void claim_rrs_cpy_reloc __P((struct file_entry *, struct relocation_info *, symbol *)); +void claim_rrs_segment_reloc __P((struct file_entry *, struct relocation_info *)); +void consider_rrs_section_lengths __P((void)); +void relocate_rrs_addresses __P((void)); +void write_rrs __P((void)); + +/* In <md>.c */ +void md_init_header __P((struct exec *, int, int)); +long md_get_addend __P((struct relocation_info *, unsigned char *)); +void md_relocate __P((struct relocation_info *, long, unsigned char *, int)); +void md_make_jmpslot __P((jmpslot_t *, long, long)); +void md_fix_jmpslot __P((jmpslot_t *, long, u_long)); +int md_make_reloc __P((struct relocation_info *, struct relocation_info *, int)); +void md_make_jmpreloc __P((struct relocation_info *, struct relocation_info *, int)); +void md_make_gotreloc __P((struct relocation_info *, struct relocation_info *, int)); +void md_make_copyreloc __P((struct relocation_info *, struct relocation_info *)); +void md_set_breakpoint __P((long, long *)); + +#ifdef NEED_SWAP +/* In xbits.c: */ +void swap_longs __P((long *, int)); +void swap_symbols __P((struct nlist *, int)); +void swap_zsymbols __P((struct nzlist *, int)); +void swap_ranlib_hdr __P((struct ranlib *, int)); +void swap__dynamic __P((struct link_dynamic *)); +void swap_section_dispatch_table __P((struct section_dispatch_table *)); +void swap_so_debug __P((struct so_debug *)); +void swapin_sod __P((struct sod *, int)); +void swapout_sod __P((struct sod *, int)); +void swapout_fshash __P((struct fshash *, int)); +#endif + +#endif /* __DYNAMIC_H__ */ diff --git a/libexec/rtld-aout/i386/md-static-funcs.c b/libexec/rtld-aout/i386/md-static-funcs.c new file mode 100644 index 0000000..758a4b2 --- /dev/null +++ b/libexec/rtld-aout/i386/md-static-funcs.c @@ -0,0 +1,17 @@ +/* + * $Id$ + * + * Called by ld.so when onanating. + * This *must* be a static function, so it is not called through a jmpslot. + */ + +static void +md_relocate_simple(r, relocation, addr) +struct relocation_info *r; +long relocation; +char *addr; +{ + if (r->r_relative) + *(long *)addr += relocation; +} + diff --git a/libexec/rtld-aout/i386/md.c b/libexec/rtld-aout/i386/md.c new file mode 100644 index 0000000..8e945e8 --- /dev/null +++ b/libexec/rtld-aout/i386/md.c @@ -0,0 +1,384 @@ +/* + * Copyright (c) 1993 Paul Kranenburg + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Paul Kranenburg. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id$ + */ + +#include <sys/param.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <err.h> +#include <fcntl.h> +#include <a.out.h> +#include <stab.h> +#include <string.h> + +#include "dynamic.h" + +#if defined(RTLD) && defined(SUN_COMPAT) +#define REL_SIZE(r) (2) /* !!!!! Sun BUG compatible */ +#else +#define REL_SIZE(r) ((r)->r_length) +#endif + +/* + * Get relocation addend corresponding to relocation record RP + * from address ADDR + */ +long +md_get_addend(rp, addr) +struct relocation_info *rp; +unsigned char *addr; +{ + switch (REL_SIZE(rp)) { + case 0: + return get_byte(addr); + case 1: + return get_short(addr); + case 2: + return get_long(addr); + default: + errx(1, "Unsupported relocation size: %x", + REL_SIZE(rp)); + } +} + +/* + * Put RELOCATION at ADDR according to relocation record RP. + */ +void +md_relocate(rp, relocation, addr, relocatable_output) +struct relocation_info *rp; +long relocation; +unsigned char *addr; +int relocatable_output; +{ + switch (REL_SIZE(rp)) { + case 0: + put_byte(addr, relocation); + break; + case 1: + put_short(addr, relocation); + break; + case 2: + put_long(addr, relocation); + break; + default: + errx(1, "Unsupported relocation size: %x", + REL_SIZE(rp)); + } +} + +/* + * Machine dependent part of claim_rrs_reloc(). + * Set RRS relocation type. + */ +int +md_make_reloc(rp, r, type) +struct relocation_info *rp, *r; +int type; +{ + /* Relocation size */ + r->r_length = rp->r_length; + + if (rp->r_pcrel) + r->r_pcrel = 1; + + if (type & RELTYPE_RELATIVE) + r->r_relative = 1; + + if (type & RELTYPE_COPY) + r->r_copy = 1; + + return 0; +} + +/* + * Set up a transfer from jmpslot at OFFSET (relative to the PLT table) + * to the binder slot (which is at offset 0 of the PLT). + */ +void +md_make_jmpslot(sp, offset, index) +jmpslot_t *sp; +long offset; +long index; +{ + /* + * i386 PC-relative "fixed point" is located right after the + * instruction it pertains to. + */ + u_long fudge = - (sizeof(sp->opcode) + sizeof(sp->addr) + offset); + + sp->opcode = CALL; +#if 0 + sp->addr = fudge; +#else + sp->addr[0] = fudge & 0xffff; + sp->addr[1] = fudge >> 16; +#endif + sp->reloc_index = index; +} + +/* + * Set up a "direct" transfer (ie. not through the run-time binder) from + * jmpslot at OFFSET to ADDR. Used by `ld' when the SYMBOLIC flag is on, + * and by `ld.so' after resolving the symbol. + * On the i386, we use the JMP instruction which is PC relative, so no + * further RRS relocations will be necessary for such a jmpslot. + */ +void +md_fix_jmpslot(sp, offset, addr) +jmpslot_t *sp; +long offset; +u_long addr; +{ + u_long fudge = addr - (sizeof(sp->opcode) + sizeof(sp->addr) + offset); + + sp->opcode = JUMP; +#if 0 + sp->addr = fudge; +#else + sp->addr[0] = fudge & 0xffff; + sp->addr[1] = fudge >> 16; +#endif + sp->reloc_index = 0; +} + +/* + * Bind a jmpslot to its target address. TARGET is where the jmpslot + * should jump to, and WHERE is a pointer to the jmpslot's address field. + * This is called by the dynamic linker when LD_BIND_NOW is set in the + * environment. + */ +void +md_bind_jmpslot(target, where) +u_long target; +caddr_t where; +{ + jmpslot_t *sp = + (jmpslot_t *) (where - offsetof(jmpslot_t, addr[0])); + + md_fix_jmpslot(sp, (long) sp, target); +} + +/* + * Update the relocation record for a RRS jmpslot. + */ +void +md_make_jmpreloc(rp, r, type) +struct relocation_info *rp, *r; +int type; +{ + jmpslot_t *sp; + + /* + * Fix relocation address to point to the correct + * location within this jmpslot. + */ + r->r_address += sizeof(sp->opcode); + + /* Relocation size */ + r->r_length = 2; + + /* Set relocation type */ + r->r_jmptable = 1; + if (type & RELTYPE_RELATIVE) + r->r_relative = 1; + +} + +/* + * Set relocation type for a RRS GOT relocation. + */ +void +md_make_gotreloc(rp, r, type) +struct relocation_info *rp, *r; +int type; +{ + r->r_baserel = 1; + if (type & RELTYPE_RELATIVE) + r->r_relative = 1; + + /* Relocation size */ + r->r_length = 2; +} + +/* + * Set relocation type for a RRS copy operation. + */ +void +md_make_cpyreloc(rp, r) +struct relocation_info *rp, *r; +{ + /* Relocation size */ + r->r_length = 2; + + r->r_copy = 1; +} + +void +md_set_breakpoint(where, savep) +long where; +long *savep; +{ + *savep = *(long *)where; + *(char *)where = TRAP; +} + +#ifndef RTLD + +#ifdef __FreeBSD__ +int netzmagic; +#endif + +/* + * Initialize (output) exec header such that useful values are + * obtained from subsequent N_*() macro evaluations. + */ +void +md_init_header(hp, magic, flags) +struct exec *hp; +int magic, flags; +{ +#ifdef NetBSD + if (oldmagic || magic == QMAGIC) + hp->a_midmag = magic; + else + N_SETMAGIC((*hp), magic, MID_I386, flags); +#endif +#ifdef __FreeBSD__ + if (oldmagic) + hp->a_midmag = magic; + else if (netzmagic) + N_SETMAGIC_NET((*hp), magic, MID_I386, flags); + else + N_SETMAGIC((*hp), magic, MID_I386, flags); +#endif + + /* TEXT_START depends on the value of outheader.a_entry. */ + if (!(link_mode & SHAREABLE)) + hp->a_entry = PAGSIZ; +} +#endif /* RTLD */ + + +#ifdef NEED_SWAP +/* + * Byte swap routines for cross-linking. + */ + +void +md_swapin_exec_hdr(h) +struct exec *h; +{ + int skip = 0; + + if (!N_BADMAG(*h)) + skip = 1; + + swap_longs((long *)h + skip, sizeof(*h)/sizeof(long) - skip); +} + +void +md_swapout_exec_hdr(h) +struct exec *h; +{ + /* NetBSD: Always leave magic alone */ + int skip = 1; +#if 0 + if (N_GETMAGIC(*h) == OMAGIC) + skip = 0; +#endif + + swap_longs((long *)h + skip, sizeof(*h)/sizeof(long) - skip); +} + + +void +md_swapin_reloc(r, n) +struct relocation_info *r; +int n; +{ + int bits; + + for (; n; n--, r++) { + r->r_address = md_swap_long(r->r_address); + bits = ((int *)r)[1]; + r->r_symbolnum = md_swap_long(bits) & 0x00ffffff; + r->r_pcrel = (bits & 1); + r->r_length = (bits >> 1) & 3; + r->r_extern = (bits >> 3) & 1; + r->r_baserel = (bits >> 4) & 1; + r->r_jmptable = (bits >> 5) & 1; + r->r_relative = (bits >> 6) & 1; +#ifdef N_SIZE + r->r_copy = (bits >> 7) & 1; +#endif + } +} + +void +md_swapout_reloc(r, n) +struct relocation_info *r; +int n; +{ + int bits; + + for (; n; n--, r++) { + r->r_address = md_swap_long(r->r_address); + bits = md_swap_long(r->r_symbolnum) & 0xffffff00; + bits |= (r->r_pcrel & 1); + bits |= (r->r_length & 3) << 1; + bits |= (r->r_extern & 1) << 3; + bits |= (r->r_baserel & 1) << 4; + bits |= (r->r_jmptable & 1) << 5; + bits |= (r->r_relative & 1) << 6; +#ifdef N_SIZE + bits |= (r->r_copy & 1) << 7; +#endif + ((int *)r)[1] = bits; + } +} + +void +md_swapout_jmpslot(j, n) +jmpslot_t *j; +int n; +{ + for (; n; n--, j++) { + j->opcode = md_swap_short(j->opcode); + j->addr[0] = md_swap_short(j->addr[0]); + j->addr[1] = md_swap_short(j->addr[1]); + j->reloc_index = md_swap_short(j->reloc_index); + } +} + +#endif /* NEED_SWAP */ diff --git a/libexec/rtld-aout/i386/md.h b/libexec/rtld-aout/i386/md.h new file mode 100644 index 0000000..84785de --- /dev/null +++ b/libexec/rtld-aout/i386/md.h @@ -0,0 +1,245 @@ +/* + * Copyright (c) 1993 Paul Kranenburg + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Paul Kranenburg. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: md.h,v 1.16 1997/02/22 15:46:34 peter Exp $ + */ + +#ifndef __MD_H__ +#define __MD_H__ + +#if defined(CROSS_LINKER) && defined(XHOST) && XHOST==sparc +#define NEED_SWAP +#endif + +#define MAX_ALIGNMENT (sizeof (long)) + +#ifdef NetBSD +#define PAGSIZ __LDPGSZ +#else +#define PAGSIZ 4096 +#endif + +#if defined(NetBSD) || defined(CROSS_LINKER) + +#define N_SET_FLAG(ex,f) (oldmagic || N_GETMAGIC(ex)==QMAGIC ? (0) : \ + N_SETMAGIC(ex, \ + N_GETMAGIC(ex), \ + MID_MACHINE, \ + N_GETFLAG(ex)|(f))) + +#define N_IS_DYNAMIC(ex) ((N_GETFLAG(ex) & EX_DYNAMIC)) + +#define N_BADMID(ex) \ + (N_GETMID(ex) != 0 && N_GETMID(ex) != MID_MACHINE) + +#endif + +/* + * FreeBSD does it differently + */ +#ifdef __FreeBSD__ +#define N_SET_FLAG(ex,f) (oldmagic ? (0) : \ + (netzmagic == 0 ? \ + N_SETMAGIC(ex, \ + N_GETMAGIC(ex), \ + MID_MACHINE, \ + N_GETFLAG(ex)|(f)) : \ + N_SETMAGIC_NET(ex, \ + N_GETMAGIC_NET(ex), \ + MID_MACHINE, \ + N_GETFLAG_NET(ex)|(f)) )) + +#define N_IS_DYNAMIC(ex) ((N_GETMAGIC_NET(ex) == ZMAGIC) ? \ + ((N_GETFLAG_NET(ex) & EX_DYNAMIC)) : \ + ((N_GETFLAG(ex) & EX_DYNAMIC) )) +#define N_BADMID(ex) 0 +#endif + +/* + * Should be handled by a.out.h ? + */ +#define N_ADJUST(ex) (((ex).a_entry < PAGSIZ) ? -PAGSIZ : 0) +#define TEXT_START(ex) (N_TXTADDR(ex) + N_ADJUST(ex)) +#define DATA_START(ex) (N_DATADDR(ex) + N_ADJUST(ex)) + +#define RELOC_STATICS_THROUGH_GOT_P(r) (0) +#define JMPSLOT_NEEDS_RELOC (0) + +#define md_got_reloc(r) (0) + +#define md_get_rt_segment_addend(r,a) md_get_addend(r,a) + +#define RELOC_INIT_SEGMENT_RELOC(r) ((r)->r_length = 2) + +/* Width of a Global Offset Table entry */ +#define GOT_ENTRY_SIZE 4 +typedef long got_t; + +typedef struct jmpslot { + u_short opcode; + u_short addr[2]; + u_short reloc_index; +#define JMPSLOT_RELOC_MASK 0xffff +} jmpslot_t; + +#define NOP 0x90 +#define CALL 0xe890 /* NOP + CALL opcode */ +#define JUMP 0xe990 /* NOP + JMP opcode */ +#define TRAP 0xcc /* INT 3 */ + +/* + * Byte swap defs for cross linking + */ + +#if !defined(NEED_SWAP) + +#define md_swapin_exec_hdr(h) +#define md_swapout_exec_hdr(h) +#define md_swapin_symbols(s,n) +#define md_swapout_symbols(s,n) +#define md_swapin_zsymbols(s,n) +#define md_swapout_zsymbols(s,n) +#define md_swapin_reloc(r,n) +#define md_swapout_reloc(r,n) +#define md_swapin__dynamic(l) +#define md_swapout__dynamic(l) +#define md_swapin_section_dispatch_table(l) +#define md_swapout_section_dispatch_table(l) +#define md_swapin_so_debug(d) +#define md_swapout_so_debug(d) +#define md_swapin_rrs_hash(f,n) +#define md_swapout_rrs_hash(f,n) +#define md_swapin_sod(l,n) +#define md_swapout_sod(l,n) +#define md_swapout_jmpslot(j,n) +#define md_swapout_got(g,n) +#define md_swapin_ranlib_hdr(h,n) +#define md_swapout_ranlib_hdr(h,n) + +#endif /* NEED_SWAP */ + +#ifdef CROSS_LINKER + +#define get_byte(p) ( ((unsigned char *)(p))[0] ) + +#define get_short(p) ( ( ((unsigned char *)(p))[0] << 8) | \ + ( ((unsigned char *)(p))[1] ) \ + ) + +#define get_long(p) ( ( ((unsigned char *)(p))[0] << 24) | \ + ( ((unsigned char *)(p))[1] << 16) | \ + ( ((unsigned char *)(p))[2] << 8 ) | \ + ( ((unsigned char *)(p))[3] ) \ + ) + +#define put_byte(p, v) { ((unsigned char *)(p))[0] = ((unsigned long)(v)); } + +#define put_short(p, v) { ((unsigned char *)(p))[0] = \ + ((((unsigned long)(v)) >> 8) & 0xff); \ + ((unsigned char *)(p))[1] = \ + ((((unsigned long)(v)) ) & 0xff); } + +#define put_long(p, v) { ((unsigned char *)(p))[0] = \ + ((((unsigned long)(v)) >> 24) & 0xff); \ + ((unsigned char *)(p))[1] = \ + ((((unsigned long)(v)) >> 16) & 0xff); \ + ((unsigned char *)(p))[2] = \ + ((((unsigned long)(v)) >> 8) & 0xff); \ + ((unsigned char *)(p))[3] = \ + ((((unsigned long)(v)) ) & 0xff); } + +#ifdef NEED_SWAP + +/* Define IO byte swapping routines */ + +void md_swapin_exec_hdr __P((struct exec *)); +void md_swapout_exec_hdr __P((struct exec *)); +void md_swapin_reloc __P((struct relocation_info *, int)); +void md_swapout_reloc __P((struct relocation_info *, int)); +void md_swapout_jmpslot __P((jmpslot_t *, int)); + +#define md_swapin_symbols(s,n) swap_symbols(s,n) +#define md_swapout_symbols(s,n) swap_symbols(s,n) +#define md_swapin_zsymbols(s,n) swap_zsymbols(s,n) +#define md_swapout_zsymbols(s,n) swap_zsymbols(s,n) +#define md_swapin__dynamic(l) swap__dynamic(l) +#define md_swapout__dynamic(l) swap__dynamic(l) +#define md_swapin_section_dispatch_table(l) swap_section_dispatch_table(l) +#define md_swapout_section_dispatch_table(l) swap_section_dispatch_table(l) +#define md_swapin_so_debug(d) swap_so_debug(d) +#define md_swapout_so_debug(d) swap_so_debug(d) +#define md_swapin_rrs_hash(f,n) swap_rrs_hash(f,n) +#define md_swapout_rrs_hash(f,n) swap_rrs_hash(f,n) +#define md_swapin_sod(l,n) swapin_sod(l,n) +#define md_swapout_sod(l,n) swapout_sod(l,n) +#define md_swapout_got(g,n) swap_longs((long*)(g),n) +#define md_swapin_ranlib_hdr(h,n) swap_ranlib_hdr(h,n) +#define md_swapout_ranlib_hdr(h,n) swap_ranlib_hdr(h,n) + +#define md_swap_short(x) ( (((x) >> 8) & 0xff) | (((x) & 0xff) << 8) ) + +#define md_swap_long(x) ( (((x) >> 24) & 0xff ) | (((x) >> 8 ) & 0xff00 ) | \ + (((x) << 8 ) & 0xff0000) | (((x) << 24) & 0xff000000)) + +#else /* We need not swap, but must pay attention to alignment: */ + +#define md_swap_short(x) (x) +#define md_swap_long(x) (x) + +#endif /* NEED_SWAP */ + +#else /* Not a cross linker: use native */ + +#define md_swap_short(x) (x) +#define md_swap_long(x) (x) + +#define get_byte(where) (*(char *)(where)) +#define get_short(where) (*(short *)(where)) +#define get_long(where) (*(long *)(where)) + +#define put_byte(where,what) (*(char *)(where) = (what)) +#define put_short(where,what) (*(short *)(where) = (what)) +#define put_long(where,what) (*(long *)(where) = (what)) + +#endif /* CROSS_LINKER */ + +void md_init_header __P((struct exec *, int, int)); +long md_get_addend __P((struct relocation_info *, unsigned char *)); +void md_relocate __P((struct relocation_info *, long, unsigned char *, int)); +void md_make_jmpslot __P((jmpslot_t *, long, long)); +void md_fix_jmpslot __P((jmpslot_t *, long, u_long)); +void md_bind_jmpslot __P((u_long, caddr_t)); +int md_make_reloc __P((struct relocation_info *, struct relocation_info *, int)); +void md_make_jmpreloc __P((struct relocation_info *, struct relocation_info *, int)); +void md_make_gotreloc __P((struct relocation_info *, struct relocation_info *, int)); +void md_make_copyreloc __P((struct relocation_info *, struct relocation_info *)); +void md_set_breakpoint __P((long, long *)); + + +#endif /* __MD_H__ */ diff --git a/libexec/rtld-aout/i386/mdprologue.S b/libexec/rtld-aout/i386/mdprologue.S new file mode 100644 index 0000000..091fe7f --- /dev/null +++ b/libexec/rtld-aout/i386/mdprologue.S @@ -0,0 +1,93 @@ +/* + * Copyright (c) 1993 Paul Kranenburg + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Paul Kranenburg. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id$ + */ + +/* + * i386 run-time link editor entry points. + */ + +#include <sys/syscall.h> + + .text + .globl _binder, _binder_entry + +/* + * _rtl(int version, struct crt_ldso *crtp) + */ + +_rtl: # crt0 calls us here + pushl %ebp # Allocate stack frame + movl %esp,%ebp + pushl %ebx + + call 1f # PIC function prologue +1: + popl %ebx + addl $_GLOBAL_OFFSET_TABLE_+[.-1b],%ebx + + movl 12(%ebp),%eax # Extract data from interface structure + movl (%eax),%eax # base address of ld.so (first field) + # setup arguments for rtld() + movl (%ebx),%ecx # 1st entry in GOT is our __DYNAMIC + addl %eax,%ecx # add load address + pushl %ecx # 3rd arg + pushl 12(%ebp) # 2nd arg == &crt. + pushl 8(%ebp) # 1st arg == version + addl _rtld@GOT(%ebx),%eax # relocate address of function + call %eax # _rtld(version, crtp, DYNAMIC) + addl $12,%esp # pop arguments + + popl %ebx + leave # remove stack frame + ret + + # First call to a procedure generally comes through here for + # binding. + +_binder_entry: + pushl %ebp # setup a stack frame + movl %esp,%ebp + pusha # save all regs + + xorl %eax,%eax # clear + movl 4(%ebp),%esi # return address in PLT + movw (%esi),%ax # get hold of relocation number + subl $6,%esi # make it point to the jmpslot + + pushl %eax # pushd arguments + pushl %esi # + call _binder@PLT # _binder(rpc, index) + addl $8,%esp # pop arguments + movl %eax,4(%ebp) # return value from _binder() == actual + # address of function + popa # restore regs + leave # remove our stack frame + ret diff --git a/libexec/rtld-aout/md-prologue.c b/libexec/rtld-aout/md-prologue.c new file mode 100644 index 0000000..dae455e --- /dev/null +++ b/libexec/rtld-aout/md-prologue.c @@ -0,0 +1,39 @@ +/* + * rtld entry pseudo code - turn into assembler and tweak it + */ + +#include <sys/types.h> +#include <sys/types.h> +#include <a.out.h> +#include "link.h" +#include "md.h" + +extern long _GOT_[]; +extern void (*rtld)(); +extern void (*binder())(); + +void +rtld_entry(version, crtp) +int version; +struct crt *crtp; +{ + register struct link_dynamic *dp; + register void (*f)(); + + /* __DYNAMIC is first entry in GOT */ + dp = (struct link_dynamic *) (_GOT_[0]+crtp->crt_ba); + + f = (void (*)())((long)rtld + crtp->crt_ba); + (*f)(version, crtp, dp); +} + +void +binder_entry() +{ + extern int PC; + struct jmpslot *sp; + void (*func)(); + + func = binder(PC, sp->reloc_index & 0x003fffff); + (*func)(); +} diff --git a/libexec/rtld-aout/rtld.1 b/libexec/rtld-aout/rtld.1 new file mode 100644 index 0000000..dbd4dde --- /dev/null +++ b/libexec/rtld-aout/rtld.1 @@ -0,0 +1,224 @@ +.\" $Id: rtld.1,v 1.13 1997/02/22 15:46:47 peter Exp $ +.\" +.\" 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. +.\" +.Dd June 27, 1995 +.Dt RTLD 1 +.Os FreeBSD +.Sh NAME +.Nm ld.so +.Nd run-time link-editor +.Sh DESCRIPTION +.Nm +is a self-contained, position independent program image providing run-time +support for loading and link-editing shared objects into a process' +address space. It uses the data structures +.Po +see +.Xr link 5 +.Pc +contained within dynamically linked programs to determine which shared +libraries are needed and loads them at a convenient virtual address +using the +.Xr mmap 2 +system call. +.Pp +After all shared libraries have been 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 +.Nm +is itself a shared object that is initially loaded by the startup module +.Em crt0 . +Since +.Xr a.out 5 +formats do not provide easy access to the file header from within a running +process, +.Em crt0 +uses the special symbol +.Va _DYNAMIC +to determine whether a program is in fact dynamically linked or not. Whenever +the linker +.Xr ld 1 +has relocated this symbol to a location other than 0, +.Em crt0 +assumes the services of +.Nm +are needed +.Po +see +.Xr link 5 +for details +.Pc \&. +.Em crt0 +passes control to +.Nm +\&'s entry point before the program's +.Fn main +routine is called. Thus, +.Nm +can complete the link-editing process before the dynamic program calls upon +services of any dynamic library. +.Pp +To quickly locate the required shared objects in the filesystem, +.Nm +may use a +.Dq hints +file, prepared by the +.Xr ldconfig 8 +utility, in which the full path specification of the shared objects can be +looked up by hashing on the 3-tuple +.Ao +library-name, major-version-number, minor-version-number +.Ac \&. +.Pp +.Nm +recognises a number of environment variables that can be used to modify +its behaviour as follows: +.Pp +.Bl -tag -width "LD_IGNORE_MISSING_OBJECTS" +.It Ev LD_LIBRARY_PATH +A colon separated list of directories, overriding the default search path +for shared libraries. +This is ignored for set-user-ID and set-group-ID programs. +.It Ev LD_PRELOAD +A colon separated list of shared libraries, to be linked in before any +other shared libraries. If the directory is not specified then +the directories specified by LD_LIBRARY_PATH will be searched first +followed by the set of built-in standard directories. +This is ignored 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_WARN_NON_PURE_CODE +When set to a nonempty string, issue a warning whenever a link-editing +operation requires modification of the text segment of some loaded +object. This is usually indicative of an incorrectly built library. +.It Ev LD_SUPPRESS_WARNINGS +When set to a nonempty string, no warning messages of any kind are +issued. Normally, a warning is given if satisfactorily versioned +library could not be found. +.It Ev LD_IGNORE_MISSING_OBJECTS +When set to a nonempty string, makes it a nonfatal condition if +one or more required shared objects cannot be loaded. +Loading and execution proceeds using the objects that are +available. +A warning is produced for each missing object, unless the environment +variable +.Ev LD_SUPPRESS_WARNINGS +is set to a nonempty string. +.Pp +This is ignored for set-user-ID and set-group-ID programs. +.Pp +Missing shared objects can be ignored without errors if all the +following conditions hold: +.Bl -bullet +.It +They do not supply definitions for any required data symbols. +.It +No functions defined by them are called during program execution. +.It +The environment variable +.Ev LD_BIND_NOW +is unset or is set to the empty string. +.El +.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_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 's +.Fl f +option and allows +.Xr ldd 1 +to be operated as a filter more conveniently. +The following conversions can be used: +.Bl -tag -indent "LD_TRACE_LOADED_OBJECTS_FMT1 " -width "xxxx" +.It \&%a +The main program's name +.Po also known as +.Dq __progname +.Pc . +.It \&%A +The value of the environment variable +.Ev LD_TRACE_LOADED_OBJECTS_PROGNAME +.It \&%o +The library name. +.It \&%m +The library's major version number. +.It \&%n +The library's minor version number. +.It \&%p +The full pathname as determined by +.Nm rtld Ns 's +library search rules. +.It \&%x +The library's load address. +.El +.Pp +Additionally, +.Sy \en +and +.Sy \et +are recognised and have their usual meaning. +.\" .It Ev LD_NO_INTERN_SEARCH +.\" When set, +.\" .Nm +.\" does not process any internal search paths that were recorded in the +.\" executable. +.\" .It Ev LD_NOSTD_PATH +.\" When set, do not include a set of built-in standard directory paths for +.\" searching. This might be useful when running on a system with a completely +.\" non-standard filesystem layout. +.El +.Pp +.Sh FILES +/var/run/ld.so.hints +.Pp +.Sh SEE ALSO +.Xr ld 1 , +.Xr link 5 , +.Xr ldconfig 8 +.Sh HISTORY +The shared library model employed first appeared in SunOS 4.0 diff --git a/libexec/rtld-aout/rtld.1aout b/libexec/rtld-aout/rtld.1aout new file mode 100644 index 0000000..dbd4dde --- /dev/null +++ b/libexec/rtld-aout/rtld.1aout @@ -0,0 +1,224 @@ +.\" $Id: rtld.1,v 1.13 1997/02/22 15:46:47 peter Exp $ +.\" +.\" 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. +.\" +.Dd June 27, 1995 +.Dt RTLD 1 +.Os FreeBSD +.Sh NAME +.Nm ld.so +.Nd run-time link-editor +.Sh DESCRIPTION +.Nm +is a self-contained, position independent program image providing run-time +support for loading and link-editing shared objects into a process' +address space. It uses the data structures +.Po +see +.Xr link 5 +.Pc +contained within dynamically linked programs to determine which shared +libraries are needed and loads them at a convenient virtual address +using the +.Xr mmap 2 +system call. +.Pp +After all shared libraries have been 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 +.Nm +is itself a shared object that is initially loaded by the startup module +.Em crt0 . +Since +.Xr a.out 5 +formats do not provide easy access to the file header from within a running +process, +.Em crt0 +uses the special symbol +.Va _DYNAMIC +to determine whether a program is in fact dynamically linked or not. Whenever +the linker +.Xr ld 1 +has relocated this symbol to a location other than 0, +.Em crt0 +assumes the services of +.Nm +are needed +.Po +see +.Xr link 5 +for details +.Pc \&. +.Em crt0 +passes control to +.Nm +\&'s entry point before the program's +.Fn main +routine is called. Thus, +.Nm +can complete the link-editing process before the dynamic program calls upon +services of any dynamic library. +.Pp +To quickly locate the required shared objects in the filesystem, +.Nm +may use a +.Dq hints +file, prepared by the +.Xr ldconfig 8 +utility, in which the full path specification of the shared objects can be +looked up by hashing on the 3-tuple +.Ao +library-name, major-version-number, minor-version-number +.Ac \&. +.Pp +.Nm +recognises a number of environment variables that can be used to modify +its behaviour as follows: +.Pp +.Bl -tag -width "LD_IGNORE_MISSING_OBJECTS" +.It Ev LD_LIBRARY_PATH +A colon separated list of directories, overriding the default search path +for shared libraries. +This is ignored for set-user-ID and set-group-ID programs. +.It Ev LD_PRELOAD +A colon separated list of shared libraries, to be linked in before any +other shared libraries. If the directory is not specified then +the directories specified by LD_LIBRARY_PATH will be searched first +followed by the set of built-in standard directories. +This is ignored 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_WARN_NON_PURE_CODE +When set to a nonempty string, issue a warning whenever a link-editing +operation requires modification of the text segment of some loaded +object. This is usually indicative of an incorrectly built library. +.It Ev LD_SUPPRESS_WARNINGS +When set to a nonempty string, no warning messages of any kind are +issued. Normally, a warning is given if satisfactorily versioned +library could not be found. +.It Ev LD_IGNORE_MISSING_OBJECTS +When set to a nonempty string, makes it a nonfatal condition if +one or more required shared objects cannot be loaded. +Loading and execution proceeds using the objects that are +available. +A warning is produced for each missing object, unless the environment +variable +.Ev LD_SUPPRESS_WARNINGS +is set to a nonempty string. +.Pp +This is ignored for set-user-ID and set-group-ID programs. +.Pp +Missing shared objects can be ignored without errors if all the +following conditions hold: +.Bl -bullet +.It +They do not supply definitions for any required data symbols. +.It +No functions defined by them are called during program execution. +.It +The environment variable +.Ev LD_BIND_NOW +is unset or is set to the empty string. +.El +.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_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 's +.Fl f +option and allows +.Xr ldd 1 +to be operated as a filter more conveniently. +The following conversions can be used: +.Bl -tag -indent "LD_TRACE_LOADED_OBJECTS_FMT1 " -width "xxxx" +.It \&%a +The main program's name +.Po also known as +.Dq __progname +.Pc . +.It \&%A +The value of the environment variable +.Ev LD_TRACE_LOADED_OBJECTS_PROGNAME +.It \&%o +The library name. +.It \&%m +The library's major version number. +.It \&%n +The library's minor version number. +.It \&%p +The full pathname as determined by +.Nm rtld Ns 's +library search rules. +.It \&%x +The library's load address. +.El +.Pp +Additionally, +.Sy \en +and +.Sy \et +are recognised and have their usual meaning. +.\" .It Ev LD_NO_INTERN_SEARCH +.\" When set, +.\" .Nm +.\" does not process any internal search paths that were recorded in the +.\" executable. +.\" .It Ev LD_NOSTD_PATH +.\" When set, do not include a set of built-in standard directory paths for +.\" searching. This might be useful when running on a system with a completely +.\" non-standard filesystem layout. +.El +.Pp +.Sh FILES +/var/run/ld.so.hints +.Pp +.Sh SEE ALSO +.Xr ld 1 , +.Xr link 5 , +.Xr ldconfig 8 +.Sh HISTORY +The shared library model employed first appeared in SunOS 4.0 diff --git a/libexec/rtld-aout/rtld.c b/libexec/rtld-aout/rtld.c new file mode 100644 index 0000000..ea33fe8 --- /dev/null +++ b/libexec/rtld-aout/rtld.c @@ -0,0 +1,2120 @@ +/* + * Copyright (c) 1993 Paul Kranenburg + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Paul Kranenburg. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: rtld.c,v 1.46 1997/02/22 15:46:48 peter Exp $ + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/errno.h> +#include <sys/mman.h> +#ifndef MAP_COPY +#define MAP_COPY MAP_PRIVATE +#endif +#include <dlfcn.h> +#include <err.h> +#include <fcntl.h> +#include <a.out.h> +#include <stab.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include <link.h> + +#include "md.h" +#include "shlib.h" +#include "support.h" +#include "dynamic.h" + +#ifndef MAP_ANON +#define MAP_ANON 0 +#define anon_open() do { \ + if ((anon_fd = open("/dev/zero", O_RDWR, 0)) == -1) \ + err("open: %s", "/dev/zero"); \ +} while (0) +#define anon_close() do { \ + (void)close(anon_fd); \ + anon_fd = -1; \ +} while (0) +#else +#define anon_open() +#define anon_close() +#endif + +/* + * Structure for building a list of shared objects. + */ +struct so_list { + struct so_map *sol_map; /* Link map for shared object */ + struct so_list *sol_next; /* Next entry in the list */ +}; + +/* + * Loader private data, hung off <so_map>->som_spd + */ +struct somap_private { + int spd_version; + struct so_map *spd_parent; + struct so_list *spd_children; + struct so_map *spd_prev; + dev_t spd_dev; + ino_t spd_ino; + int spd_refcount; + int spd_flags; +#define RTLD_MAIN 0x01 +#define RTLD_RTLD 0x02 +#define RTLD_DL 0x04 +#define RTLD_INIT 0x08 + unsigned long a_text; /* text size, if known */ + unsigned long a_data; /* initialized data size */ + unsigned long a_bss; /* uninitialized data size */ + +#ifdef SUN_COMPAT + long spd_offset; /* Correction for Sun main programs */ +#endif +}; + +#define LM_PRIVATE(smp) ((struct somap_private *)(smp)->som_spd) + +#ifdef SUN_COMPAT +#define LM_OFFSET(smp) (LM_PRIVATE(smp)->spd_offset) +#else +#define LM_OFFSET(smp) (0) +#endif + +/* Base address for section_dispatch_table entries */ +#define LM_LDBASE(smp) (smp->som_addr + LM_OFFSET(smp)) + +/* Start of text segment */ +#define LM_TXTADDR(smp) (smp->som_addr == (caddr_t)0 ? PAGSIZ : 0) + +/* Start of run-time relocation_info */ +#define LM_REL(smp) ((struct relocation_info *) \ + (smp->som_addr + LM_OFFSET(smp) + LD_REL((smp)->som_dynamic))) + +/* Start of symbols */ +#define LM_SYMBOL(smp, i) ((struct nzlist *) \ + (smp->som_addr + LM_OFFSET(smp) + LD_SYMBOL((smp)->som_dynamic) + \ + i * (LD_VERSION_NZLIST_P(smp->som_dynamic->d_version) ? \ + sizeof(struct nzlist) : sizeof(struct nlist)))) + +/* Start of hash table */ +#define LM_HASH(smp) ((struct rrs_hash *) \ + ((smp)->som_addr + LM_OFFSET(smp) + LD_HASH((smp)->som_dynamic))) + +/* Start of strings */ +#define LM_STRINGS(smp) ((char *) \ + ((smp)->som_addr + LM_OFFSET(smp) + LD_STRINGS((smp)->som_dynamic))) + +/* Start of search paths */ +#define LM_PATHS(smp) ((char *) \ + ((smp)->som_addr + LM_OFFSET(smp) + LD_PATHS((smp)->som_dynamic))) + +/* End of text */ +#define LM_ETEXT(smp) ((char *) \ + ((smp)->som_addr + LM_TXTADDR(smp) + LD_TEXTSZ((smp)->som_dynamic))) + +/* Needed shared objects */ +#define LM_NEED(smp) ((struct sod *) \ + ((smp)->som_addr + LM_TXTADDR(smp) + LD_NEED((smp)->som_dynamic))) + +/* PLT is in data segment, so don't use LM_OFFSET here */ +#define LM_PLT(smp) ((jmpslot_t *) \ + ((smp)->som_addr + LD_PLT((smp)->som_dynamic))) + +/* Parent of link map */ +#define LM_PARENT(smp) (LM_PRIVATE(smp)->spd_parent) + +#ifndef RELOC_EXTERN_P +#define RELOC_EXTERN_P(s) ((s)->r_extern) +#endif + +#ifndef RELOC_SYMBOL +#define RELOC_SYMBOL(s) ((s)->r_symbolnum) +#endif + +#ifndef RELOC_PCREL_P +#define RELOC_PCREL_P(s) ((s)->r_pcrel) +#endif + +static char __main_progname[] = "main"; +static char *main_progname = __main_progname; +static char us[] = "/usr/libexec/ld.so"; + +char **environ; +char *__progname; +int errno; + +static uid_t uid, euid; +static gid_t gid, egid; +static int careful; +static int anon_fd = -1; + +static char *ld_bind_now; +static char *ld_ignore_missing_objects; +static char *ld_library_path; +static char *ld_preload; +static char *ld_tracing; +static char *ld_suppress_warnings; +static char *ld_warn_non_pure_code; + +struct so_map *link_map_head; +struct so_map *link_map_tail; +struct rt_symbol *rt_symbol_head; + +static void *__dlopen __P((char *, int)); +static int __dlclose __P((void *)); +static void *__dlsym __P((void *, char *)); +static char *__dlerror __P((void)); +static void __dlexit __P((void)); +static void *__dlsym3 __P((void *, char *, void *)); + +static struct ld_entry ld_entry = { + __dlopen, __dlclose, __dlsym, __dlerror, __dlexit, __dlsym3 +}; + + void xprintf __P((char *, ...)); +static struct so_map *map_object __P(( char *, + struct sod *, + struct so_map *)); +static int map_preload __P((void)); +static int map_sods __P((struct so_map *)); +static int reloc_and_init __P((struct so_map *, int)); +static void unmap_object __P((struct so_map *, int)); +static struct so_map *alloc_link_map __P(( char *, struct sod *, + struct so_map *, caddr_t, + struct _dynamic *)); +static void free_link_map __P((struct so_map *)); +static inline int check_text_reloc __P(( struct relocation_info *, + struct so_map *, + caddr_t)); +static int reloc_map __P((struct so_map *, int)); +static void reloc_copy __P((struct so_map *)); +static void init_object __P((struct so_map *)); +static void init_sods __P((struct so_list *)); +static int call_map __P((struct so_map *, char *)); +static char *findhint __P((char *, int, int *)); +static char *rtfindlib __P((char *, int, int)); +static char *rtfindfile __P((char *)); +void binder_entry __P((void)); +long binder __P((jmpslot_t *)); +static struct nzlist *lookup __P((char *, struct so_map **, int)); +static inline struct rt_symbol *lookup_rts __P((char *)); +static struct rt_symbol *enter_rts __P((char *, long, int, caddr_t, + long, struct so_map *)); +static void die __P((void)); +static void generror __P((char *, ...)); +static int maphints __P((void)); +static void unmaphints __P((void)); +static void ld_trace __P((struct so_map *)); +static void rt_readenv __P((void)); +static int hinthash __P((char *, int)); +int rtld __P((int, struct crt_ldso *, struct _dynamic *)); + +static inline int +strcmp (register const char *s1, register const char *s2) +{ + while (*s1 == *s2++) + if (*s1++ == 0) + return (0); + return (*(unsigned char *)s1 - *(unsigned char *)--s2); +} + +#include "md-static-funcs.c" + +/* + * Called from assembler stub that has set up crtp (passed from crt0) + * and dp (our __DYNAMIC). + */ +int +rtld(version, crtp, dp) +int version; +struct crt_ldso *crtp; +struct _dynamic *dp; +{ + struct relocation_info *reloc; + struct relocation_info *reloc_limit; /* End+1 of relocation */ + struct so_debug *ddp; + struct so_map *main_map; + struct so_map *smp; + char *add_paths; + + /* Check version */ + if (version != CRT_VERSION_BSD_2 && + version != CRT_VERSION_BSD_3 && + version != CRT_VERSION_BSD_4 && + version != CRT_VERSION_SUN) + return -1; + + /* Fixup __DYNAMIC structure */ + (long)dp->d_un.d_sdt += crtp->crt_ba; + + /* Relocate ourselves */ + reloc = (struct relocation_info *) (LD_REL(dp) + crtp->crt_ba); + reloc_limit = + (struct relocation_info *) ((char *) reloc + LD_RELSZ(dp)); + while(reloc < reloc_limit) { + /* + * Objects linked with "-Bsymbolic" (in particular, ld.so + * itself) can end up having unused relocation entries at + * the end. These can be detected by the fact that they + * have an address of 0. + */ + if(reloc->r_address == 0) /* We're done */ + break; + md_relocate_simple(reloc, crtp->crt_ba, + reloc->r_address + crtp->crt_ba); + ++reloc; + } + + if (version >= CRT_VERSION_BSD_4) + __progname = crtp->crt_ldso; + if (version >= CRT_VERSION_BSD_3) + main_progname = crtp->crt_prog; + + /* Some buggy versions of crt0.o have crt_ldso filled in as NULL. */ + if (__progname == NULL) + __progname = us; + + /* Fill in some fields in _DYNAMIC or crt structure */ + if (version >= CRT_VERSION_BSD_4) + crtp->crt_ldentry = &ld_entry; /* crt */ + else + crtp->crt_dp->d_entry = &ld_entry; /* _DYNAMIC */ + + /* Setup out (private) environ variable */ + environ = crtp->crt_ep; + + /* Get user and group identifiers */ + uid = getuid(); euid = geteuid(); + gid = getgid(); egid = getegid(); + + careful = (uid != euid) || (gid != egid); + + rt_readenv(); + + anon_open(); + + /* Make a link map entry for the main program */ + main_map = alloc_link_map(main_progname, + (struct sod *) NULL, (struct so_map *) NULL, + (caddr_t) 0, crtp->crt_dp); + LM_PRIVATE(main_map)->spd_refcount++; + LM_PRIVATE(main_map)->spd_flags |= RTLD_MAIN; + + /* Make a link map entry for ourselves */ + smp = alloc_link_map(us, + (struct sod *) NULL, (struct so_map *) NULL, + (caddr_t) crtp->crt_ba, dp); + LM_PRIVATE(smp)->spd_refcount++; + LM_PRIVATE(smp)->spd_flags |= RTLD_RTLD; + + /* + * Setup the executable's run path + */ + if (version >= CRT_VERSION_BSD_4) { + add_paths = LM_PATHS(main_map); + if (add_paths) + add_search_path(add_paths); + } + + /* + * Setup the directory search list for findshlib. We use only + * the standard search path. Any extra directories from + * LD_LIBRARY_PATH are searched explicitly, in rtfindlib. + */ + std_search_path(); + + /* Map in LD_PRELOADs before the main program's shared objects so we + can intercept those calls */ + if (ld_preload != NULL) { + if(map_preload() == -1) /* Failed */ + die(); + } + + /* Map all the shared objects that the main program depends upon */ + if(map_sods(main_map) == -1) + die(); + + if(ld_tracing) { /* We're done */ + ld_trace(link_map_head); + exit(0); + } + + crtp->crt_dp->d_un.d_sdt->sdt_loaded = link_map_head->som_next; + + /* Relocate and initialize all mapped objects */ + if(reloc_and_init(main_map, ld_bind_now != NULL) == -1) /* Failed */ + die(); + + ddp = crtp->crt_dp->d_debug; + ddp->dd_cc = rt_symbol_head; + if (ddp->dd_in_debugger) { + caddr_t addr = (caddr_t)((long)crtp->crt_bp & (~(PAGSIZ - 1))); + + /* Set breakpoint for the benefit of debuggers */ + if (mprotect(addr, PAGSIZ, + PROT_READ|PROT_WRITE|PROT_EXEC) == -1) { + err(1, "Cannot set breakpoint (%s)", main_progname); + } + md_set_breakpoint((long)crtp->crt_bp, (long *)&ddp->dd_bpt_shadow); + if (mprotect(addr, PAGSIZ, PROT_READ|PROT_EXEC) == -1) { + err(1, "Cannot re-protect breakpoint (%s)", + main_progname); + } + + ddp->dd_bpt_addr = crtp->crt_bp; + if (link_map_head) + ddp->dd_sym_loaded = 1; + } + + /* Close the hints file */ + unmaphints(); + + /* Close our file descriptor */ + (void)close(crtp->crt_ldfd); + anon_close(); + + return LDSO_VERSION_HAS_DLSYM3; +} + +void +ld_trace(smp) + struct so_map *smp; +{ + char *fmt1, *fmt2, *fmt, *main_local; + 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-l%o.%m => %p (%x)\n"; + + if ((fmt2 = getenv("LD_TRACE_LOADED_OBJECTS_FMT2")) == NULL) + fmt2 = "\t%o (%x)\n"; + + for (; smp; smp = smp->som_next) { + struct sod *sodp; + char *name, *path; + + if ((sodp = smp->som_sod) == NULL) + continue; + + name = (char *)sodp->sod_name; + if (LM_PARENT(smp)) + name += (long)LM_LDBASE(LM_PARENT(smp)); + + if ((path = smp->som_path) == NULL) + path = "not found"; + + fmt = sodp->sod_library ? fmt1 : fmt2; + while ((c = *fmt++) != '\0') { + switch (c) { + default: + putchar(c); + continue; + case '\\': + switch (c = *fmt) { + case '\0': + continue; + case 'n': + putchar('\n'); + break; + case 't': + putchar('\t'); + break; + } + break; + case '%': + switch (c = *fmt) { + case '\0': + continue; + case '%': + default: + putchar(c); + break; + case 'A': + printf("%s", main_local); + break; + case 'a': + printf("%s", main_progname); + break; + case 'o': + printf("%s", name); + break; + case 'm': + printf("%d", sodp->sod_major); + break; + case 'n': + printf("%d", sodp->sod_minor); + break; + case 'p': + printf("%s", path); + break; + case 'x': + printf("%p", smp->som_addr); + break; + } + break; + } + ++fmt; + } + } +} + +/* + * Allocate a new link map and return a pointer to it. + * + * PATH is the pathname of the shared object. + * + * SODP is a pointer to the shared object dependency structure responsible + * for causing the new object to be loaded. PARENT is the shared object + * into which SODP points. Both can be NULL if the new object is not + * being loaded as a result of a shared object dependency. + * + * ADDR is the address at which the object has been mapped. DP is a pointer + * to its _dynamic structure. + */ + static struct so_map * +alloc_link_map(path, sodp, parent, addr, dp) + char *path; + struct sod *sodp; + struct so_map *parent; + caddr_t addr; + struct _dynamic *dp; +{ + struct so_map *smp; + struct somap_private *smpp; + size_t smp_size; + +#ifdef DEBUG /* { */ + xprintf("alloc_link_map: \"%s\" at %p\n", path, addr); +#endif /* } */ + + /* + * Allocate so_map and private area with a single malloc. Round + * up the size of so_map so the private area is aligned. + */ + smp_size = ((((sizeof(struct so_map)) + sizeof (void *) - 1) / + sizeof (void *)) * sizeof (void *)); + + smp = (struct so_map *)xmalloc(smp_size + + sizeof (struct somap_private)); + smpp = (struct somap_private *) (((caddr_t) smp) + smp_size); + + /* Link the new entry into the list of link maps */ + smp->som_next = NULL; + smpp->spd_prev = link_map_tail; + if(link_map_tail == NULL) /* First link map entered into list */ + link_map_head = link_map_tail = smp; + else { /* Append to end of list */ + link_map_tail->som_next = smp; + link_map_tail = smp; + } + + smp->som_addr = addr; + smp->som_path = path ? strdup(path) : NULL; + smp->som_sod = sodp; + smp->som_dynamic = dp; + smp->som_spd = (caddr_t)smpp; + + smpp->spd_refcount = 0; + smpp->spd_flags = 0; + smpp->spd_parent = parent; + smpp->spd_children = NULL; + smpp->a_text = 0; + smpp->a_data = 0; + smpp->a_bss = 0; +#ifdef SUN_COMPAT + smpp->spd_offset = + (addr==0 && dp && dp->d_version==LD_VERSION_SUN) ? PAGSIZ : 0; +#endif + return smp; +} + +/* + * Remove the specified link map entry from the list of link maps, and free + * the associated storage. + */ + static void +free_link_map(smp) + struct so_map *smp; +{ + struct somap_private *smpp = LM_PRIVATE(smp); + +#ifdef DEBUG /* { */ + xprintf("free_link_map: \"%s\"\n", smp->som_path); +#endif /* } */ + + if(smpp->spd_prev == NULL) /* Removing first entry in list */ + link_map_head = smp->som_next; + else /* Update link of previous entry */ + smpp->spd_prev->som_next = smp->som_next; + + if(smp->som_next == NULL) /* Removing last entry in list */ + link_map_tail = smpp->spd_prev; + else /* Update back link of next entry */ + LM_PRIVATE(smp->som_next)->spd_prev = smpp->spd_prev; + + free(smp->som_path); + free(smp); +} + +/* + * Map the shared object specified by PATH into memory, if it is not + * already mapped. Increment the object's reference count, and return a + * pointer to its link map. + * + * As a special case, if PATH is NULL, it is taken to refer to the main + * program. + * + * SODP is a pointer to the shared object dependency structure that caused + * this object to be requested. PARENT is a pointer to the link map of + * the shared object containing that structure. For a shared object not + * being mapped as a result of a shared object dependency, these pointers + * should be NULL. An example of this is a shared object that is explicitly + * loaded via dlopen(). + * + * The return value is a pointer to the link map for the requested object. + * If the operation failed, the return value is NULL. In that case, an + * error message can be retrieved by calling dlerror(). + */ + static struct so_map * +map_object(path, sodp, parent) + char *path; + struct sod *sodp; + struct so_map *parent; +{ + struct so_map *smp; + struct stat statbuf; + + if(path == NULL) /* Special case for the main program itself */ + smp = link_map_head; + else { + /* + * Check whether the shared object is already mapped. + * We check first for an exact match by pathname. That + * will detect the usual case. If no match is found by + * pathname, then stat the file, and check for a match by + * device and inode. That will detect the less common case + * involving multiple links to the same library. + */ + for(smp = link_map_head; smp != NULL; smp = smp->som_next) { + if(!(LM_PRIVATE(smp)->spd_flags & (RTLD_MAIN|RTLD_RTLD)) + && smp->som_path != NULL + && strcmp(smp->som_path, path) == 0) + break; + } + if(smp == NULL) { /* Check for a match by device and inode */ + if (stat(path, &statbuf) == -1) { + generror ("cannot stat \"%s\" : %s", + path, strerror(errno)); + return NULL; + } + for (smp = link_map_head; smp != NULL; + smp = smp->som_next) { + struct somap_private *smpp = LM_PRIVATE(smp); + + if (!(smpp->spd_flags & (RTLD_MAIN | RTLD_RTLD)) + && smpp->spd_ino == statbuf.st_ino + && smpp->spd_dev == statbuf.st_dev) + break; + } + } + } + + if (smp == NULL) { /* We must map the object */ + struct _dynamic *dp; + int fd; + caddr_t addr; + struct exec hdr; + struct somap_private *smpp; + + if ((fd = open(path, O_RDONLY, 0)) == -1) { + generror ("open failed for \"%s\" : %s", + path, strerror (errno)); + return NULL; + } + + if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { + generror ("header read failed for \"%s\"", path); + (void)close(fd); + return NULL; + } + + if (N_BADMAG(hdr)) { + generror ("bad magic number in \"%s\"", path); + (void)close(fd); + return NULL; + } + + /* + * Map the entire address space of the object. It is + * tempting to map just the text segment at first, in + * order to avoid having to use mprotect to change the + * protections of the data segment. But that would not + * be correct. Mmap might find a group of free pages + * large enough to hold the text segment, but not large + * enough for the entire object. When we then mapped + * in the data and BSS segments, they would either be + * non-contiguous with the text segment (if we didn't + * specify MAP_FIXED), or they would map over some + * previously mapped region (if we did use MAP_FIXED). + * The only way we can be sure of getting a contigous + * region that is large enough is to map the entire + * region at once. + */ + if ((addr = mmap(0, hdr.a_text + hdr.a_data + hdr.a_bss, + PROT_READ|PROT_EXEC, + MAP_COPY, fd, 0)) == (caddr_t)-1) { + generror ("mmap failed for \"%s\" : %s", + path, strerror (errno)); + (void)close(fd); + return NULL; + } + + (void)close(fd); + + /* Change the data segment to writable */ + if (mprotect(addr + hdr.a_text, hdr.a_data, + PROT_READ|PROT_WRITE|PROT_EXEC) != 0) { + generror ("mprotect failed for \"%s\" : %s", + path, strerror (errno)); + (void)munmap(addr, hdr.a_text + hdr.a_data + hdr.a_bss); + return NULL; + } + + /* Map in pages of zeros for the BSS segment */ + if (mmap(addr + hdr.a_text + hdr.a_data, hdr.a_bss, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_ANON|MAP_COPY|MAP_FIXED, + anon_fd, 0) == (caddr_t)-1) { + generror ("mmap failed for \"%s\" : %s", + path, strerror (errno)); + (void)munmap(addr, hdr.a_text + hdr.a_data + hdr.a_bss); + return NULL; + } + + /* Assume _DYNAMIC is the first data item */ + dp = (struct _dynamic *)(addr+hdr.a_text); + + /* Fixup __DYNAMIC structure */ + (long)dp->d_un.d_sdt += (long)addr; + + smp = alloc_link_map(path, sodp, parent, addr, dp); + + /* save segment sizes for unmap. */ + smpp = LM_PRIVATE(smp); + smpp->a_text = hdr.a_text; + smpp->a_data = hdr.a_data; + smpp->a_bss = hdr.a_bss; + + /* + * Save the device and inode, so we can detect multiple links + * to the same library. Note, if we reach this point, then + * statbuf is guaranteed to have been filled in. + */ + smpp->spd_dev = statbuf.st_dev; + smpp->spd_ino = statbuf.st_ino; + } + + LM_PRIVATE(smp)->spd_refcount++; + if(LM_PRIVATE(smp)->spd_refcount == 1) { /* First use of object */ + /* + * Recursively map all of the shared objects that this + * one depends upon. + */ + if(map_sods(smp) == -1) { /* Failed */ + unmap_object(smp, 0); /* Clean up */ + return NULL; + } + } + + return smp; +} + +/* + * Map all the shared libraries named in the LD_PRELOAD environment + * variable. + * + * Returns 0 on success, -1 on failure. On failure, an error message can + * be gotten via dlerror(). + */ + static int +map_preload __P((void)) { + char *ld_name = ld_preload; + char *name; + + while ((name = strsep(&ld_name, ":")) != NULL) { + char *path = NULL; + struct so_map *smp = NULL; + + if (*name != '\0') { + path = (strchr(name, '/') != NULL) ? strdup(name) : + rtfindfile(name); + } + if (path == NULL) { + generror("Can't find LD_PRELOAD shared" + " library \"%s\"", name); + } else { + smp = map_object(path, (struct sod *) NULL, + (struct so_map *) NULL); + free(path); + } + if (ld_name != NULL) + *(ld_name - 1) = ':'; + if (smp == NULL) { + /* + * We don't bother to unmap already-loaded libraries + * on failure, because in that case the program is + * about to die anyway. + */ + return -1; + } + } + return 0; +} + +/* + * Map all of the shared objects that a given object depends upon. PARENT is + * a pointer to the link map for the shared object whose dependencies are + * to be mapped. + * + * Returns 0 on success. Returns -1 on failure. In that case, an error + * message can be retrieved by calling dlerror(). + */ + static int +map_sods(parent) + struct so_map *parent; +{ + struct somap_private *parpp = LM_PRIVATE(parent); + struct so_list **soltail = &parpp->spd_children; + long next = LD_NEED(parent->som_dynamic); + + while(next != 0) { + struct sod *sodp = + (struct sod *) (LM_LDBASE(parent) + next); + char *name = + (char *) (LM_LDBASE(parent) + sodp->sod_name); + char *path = NULL; + struct so_map *smp = NULL; + + if(sodp->sod_library) { + path = rtfindlib(name, sodp->sod_major, + sodp->sod_minor); + if(path == NULL && !ld_tracing) { + generror ("Can't find shared library" + " \"lib%s.so.%d.%d\"", name, + sodp->sod_major, sodp->sod_minor); + } + } else { + if(careful && name[0] != '/') { + generror("Shared library path must start" + " with \"/\" for \"%s\"", name); + } else + path = strdup(name); + } + + if(path != NULL) { + smp = map_object(path, sodp, parent); + free(path); + } + + if(smp != NULL) { + struct so_list *solp = (struct so_list *) + xmalloc(sizeof(struct so_list)); + solp->sol_map = smp; + solp->sol_next = NULL; + *soltail = solp; + soltail = &solp->sol_next; + } else if(ld_tracing) { + /* + * Allocate a dummy map entry so that we will get the + * "not found" message. + */ + (void)alloc_link_map(NULL, sodp, parent, 0, 0); + } else if (ld_ignore_missing_objects) { + char *msg; + /* + * Call __dlerror() even it we're not going to use + * the message, in order to clear the saved message. + */ + msg = __dlerror(); /* Should never be NULL */ + if (!ld_suppress_warnings) + warnx("warning: %s", msg); + } else /* Give up */ + break; + + next = sodp->sod_next; + } + + if(next != 0) { + /* + * Oh drat, we have to clean up a mess. + * + * We failed to load a shared object that we depend upon. + * So now we have to unload any dependencies that we had + * already successfully loaded prior to the error. + * + * Cleaning up doesn't matter so much for the initial + * loading of the program, since any failure is going to + * terminate the program anyway. But it is very important + * to clean up properly when something is being loaded + * via dlopen(). + */ + struct so_list *solp; + + while((solp = parpp->spd_children) != NULL) { + unmap_object(solp->sol_map, 0); + parpp->spd_children = solp->sol_next; + free(solp); + } + + return -1; + } + + return 0; +} + +/* + * Relocate and initialize the tree of shared objects rooted at the given + * link map entry. Returns 0 on success, or -1 on failure. On failure, + * an error message can be retrieved via dlerror(). + */ + static int +reloc_and_init(root, bind_now) + struct so_map *root; + int bind_now; +{ + struct so_map *smp; + + /* + * Relocate all newly-loaded objects. We avoid recursion for this + * step by taking advantage of a few facts. This function is called + * only when there are in fact some newly-loaded objects to process. + * Furthermore, all newly-loaded objects will have their link map + * entries at the end of the link map list. And, the root of the + * tree of objects just loaded will have been the first to be loaded + * and therefore the first new object in the link map list. Finally, + * we take advantage of the fact that we can relocate the newly-loaded + * objects in any order. + * + * All these facts conspire to let us simply loop over the tail + * portion of the link map list, relocating each object so + * encountered. + */ + for(smp = root; smp != NULL; smp = smp->som_next) { + if(!(LM_PRIVATE(smp)->spd_flags & RTLD_RTLD)) { + if(reloc_map(smp, bind_now) < 0) + return -1; + } + } + + /* + * Copy any relocated initialized data. Again, we can just loop + * over the appropriate portion of the link map list. + */ + for(smp = root; smp != NULL; smp = smp->som_next) { + if(!(LM_PRIVATE(smp)->spd_flags & RTLD_RTLD)) + reloc_copy(smp); + } + + /* + * Call any object initialization routines. + * + * Here, the order is very important, and we cannot simply loop + * over the newly-loaded objects as we did before. Rather, we + * have to initialize the tree of new objects depth-first, and + * process the sibling objects at each level in reverse order + * relative to the dependency list. + * + * Here is the reason we initialize depth-first. If an object + * depends on one or more other objects, then the objects it + * depends on should be initialized first, before the parent + * object itself. For it is possible that the parent's + * initialization routine will need the services provided by the + * objects it depends on -- and those objects had better already + * be initialized. + * + * We initialize the objects at each level of the tree in reverse + * order for a similar reason. When an object is linked with + * several libraries, it is common for routines in the earlier + * libraries to call routines in the later libraries. So, again, + * the later libraries need to be initialized first. + * + * The upshot of these rules is that we have to use recursion to + * get the libraries initialized in the best order. But the + * recursion is never likely to be very deep. + */ + init_object(root); + + return 0; +} + +/* + * Remove a reference to the shared object specified by SMP. If no + * references remain, unmap the object and, recursively, its descendents. + * This function also takes care of calling the finalization routines for + * objects that are removed. + * + * If KEEP is true, then the actual calls to munmap() are skipped, + * and the object is kept in memory. That is used only for finalization, + * from dlexit(), when the program is exiting. There are two reasons + * for it. First, the program is exiting and there is no point in + * spending the time to explicitly unmap its shared objects. Second, + * even after dlexit() has been called, there are still a couple of + * calls that are made to functions in libc. (This is really a bug + * in crt0.) So libc and the main program, at least, must remain + * mapped in that situation. + * + * Under no reasonable circumstances should this function fail. If + * anything goes wrong, we consider it an internal error, and report + * it with err(). + */ + static void +unmap_object(smp, keep) + struct so_map *smp; + int keep; +{ + struct somap_private *smpp = LM_PRIVATE(smp); + + smpp->spd_refcount--; + if(smpp->spd_refcount == 0) { /* Finished with this object */ + struct so_list *solp; + + if(smpp->spd_flags & RTLD_INIT) { /* Was initialized */ + /* + * Call the object's finalization routine. For + * backward compatibility, we first try to call + * ".fini". If that does not exist, we call + * "__fini". + */ + if(call_map(smp, ".fini") == -1) + call_map(smp, "__fini"); + } + + /* Recursively unreference the object's descendents */ + while((solp = smpp->spd_children) != NULL) { + unmap_object(solp->sol_map, keep); + smpp->spd_children = solp->sol_next; + free(solp); + } + + if(!keep) { /* Unmap the object from memory */ + if(munmap(smp->som_addr, + smpp->a_text + smpp->a_data + smpp->a_bss) < 0) + err(1, "internal error 1: munmap failed"); + + /* Unlink and free the object's link map entry */ + free_link_map(smp); + } + } +} + +static inline int +check_text_reloc(r, smp, addr) +struct relocation_info *r; +struct so_map *smp; +caddr_t addr; +{ + char *sym; + + if (addr >= LM_ETEXT(smp)) + return 0; + + if (RELOC_EXTERN_P(r)) + sym = LM_STRINGS(smp) + + LM_SYMBOL(smp, RELOC_SYMBOL(r))->nz_strx; + else + sym = ""; + + if (!ld_suppress_warnings && ld_warn_non_pure_code) + warnx("warning: non pure code in %s at %x (%s)", + smp->som_path, r->r_address, sym); + + if (smp->som_write == 0 && + mprotect(smp->som_addr + LM_TXTADDR(smp), + LD_TEXTSZ(smp->som_dynamic), + PROT_READ|PROT_WRITE|PROT_EXEC) == -1) { + generror ("mprotect failed for \"%s\" : %s", + smp->som_path, strerror (errno)); + return -1; + } + + smp->som_write = 1; + return 0; +} + +static int +reloc_map(smp, bind_now) + struct so_map *smp; + int bind_now; +{ + /* + * Caching structure for reducing the number of calls to + * lookup() during relocation. + * + * While relocating a given shared object, the dynamic linker + * maintains a caching vector that is directly indexed by + * the symbol number in the relocation entry. The first time + * a given symbol is looked up, the caching vector is + * filled in with a pointer to the symbol table entry, and + * a pointer to the so_map of the shared object in which the + * symbol was defined. On subsequent uses of the same symbol, + * that information is retrieved directly from the caching + * vector, without calling lookup() again. + * + * A symbol that is referenced in a relocation entry is + * typically referenced in many relocation entries, so this + * caching reduces the number of calls to lookup() + * dramatically. The overall improvement in the speed of + * dynamic linking is also dramatic -- as much as a factor + * of three for programs that use many shared libaries. + */ + struct cacheent { + struct nzlist *np; /* Pointer to symbol entry */ + struct so_map *src_map; /* Shared object that defined symbol */ + }; + + struct _dynamic *dp = smp->som_dynamic; + struct relocation_info *r = LM_REL(smp); + struct relocation_info *rend = r + LD_RELSZ(dp)/sizeof(*r); + long symbolbase = (long)LM_SYMBOL(smp, 0); + char *stringbase = LM_STRINGS(smp); + int symsize = LD_VERSION_NZLIST_P(dp->d_version) ? + sizeof(struct nzlist) : + sizeof(struct nlist); + long numsyms = LD_STABSZ(dp) / symsize; + size_t cachebytes = numsyms * sizeof(struct cacheent); + struct cacheent *symcache = + (struct cacheent *) alloca(cachebytes); + + if(symcache == NULL) { + generror("Cannot allocate symbol caching vector for %s", + smp->som_path); + return -1; + } + bzero(symcache, cachebytes); + + if (LD_PLTSZ(dp)) + md_fix_jmpslot(LM_PLT(smp), + (long)LM_PLT(smp), (long)binder_entry); + + for (; r < rend; r++) { + char *sym; + caddr_t addr; + + /* + * Objects linked with "-Bsymbolic" can end up having unused + * relocation entries at the end. These can be detected by + * the fact that they have an address of 0. + */ + if(r->r_address == 0) /* Finished relocating this object */ + break; + + addr = smp->som_addr + r->r_address; + if (check_text_reloc(r, smp, addr) < 0) + return -1; + + if (RELOC_EXTERN_P(r)) { + struct so_map *src_map = NULL; + struct nzlist *p, *np; + long relocation; + + if (RELOC_JMPTAB_P(r) && !bind_now) + continue; + + p = (struct nzlist *) + (symbolbase + symsize * RELOC_SYMBOL(r)); + + if (p->nz_type == (N_SETV + N_EXT)) + src_map = smp; + + sym = stringbase + p->nz_strx; + + /* + * Look up the symbol, checking the caching + * vector first. + */ + np = symcache[RELOC_SYMBOL(r)].np; + if(np != NULL) /* Symbol already cached */ + src_map = symcache[RELOC_SYMBOL(r)].src_map; + else { /* Symbol not cached yet */ + np = lookup(sym, &src_map, RELOC_JMPTAB_P(r)); + /* + * Record the needed information about + * the symbol in the caching vector, + * so that we won't have to call + * lookup the next time we encounter + * the symbol. + */ + symcache[RELOC_SYMBOL(r)].np = np; + symcache[RELOC_SYMBOL(r)].src_map = src_map; + } + + if (np == NULL) { + generror ("Undefined symbol \"%s\" in %s:%s", + sym, main_progname, smp->som_path); + return -1; + } + + /* + * Found symbol definition. + * If it's in a link map, adjust value + * according to the load address of that map. + * Otherwise it's a run-time allocated common + * whose value is already up-to-date. + */ + relocation = np->nz_value; + if (src_map) + relocation += (long)src_map->som_addr; + + if (RELOC_JMPTAB_P(r)) { + md_bind_jmpslot(relocation, addr); + continue; + } + + relocation += md_get_addend(r, addr); + + if (RELOC_PCREL_P(r)) + relocation -= (long)smp->som_addr; + + if (RELOC_COPY_P(r) && src_map) { + (void)enter_rts(sym, + (long)addr, + N_DATA + N_EXT, + src_map->som_addr + np->nz_value, + np->nz_size, src_map); + continue; + } + + md_relocate(r, relocation, addr, 0); + } else { + md_relocate(r, +#ifdef SUN_COMPAT + md_get_rt_segment_addend(r, addr) +#else + md_get_addend(r, addr) +#endif + + (long)smp->som_addr, addr, 0); + } + + } + + if (smp->som_write) { + if (mprotect(smp->som_addr + LM_TXTADDR(smp), + LD_TEXTSZ(smp->som_dynamic), + PROT_READ|PROT_EXEC) == -1) { + generror ("mprotect failed for \"%s\" : %s", + smp->som_path, strerror (errno)); + return -1; + } + smp->som_write = 0; + } + return 0; +} + + static void +reloc_copy(smp) + struct so_map *smp; +{ + struct rt_symbol *rtsp; + + for (rtsp = rt_symbol_head; rtsp; rtsp = rtsp->rt_next) + if ((rtsp->rt_smp == NULL || rtsp->rt_smp == smp) && + rtsp->rt_sp->nz_type == N_DATA + N_EXT) { + bcopy(rtsp->rt_srcaddr, (caddr_t)rtsp->rt_sp->nz_value, + rtsp->rt_sp->nz_size); + } +} + + static void +init_object(smp) + struct so_map *smp; +{ + struct somap_private *smpp = LM_PRIVATE(smp); + + if(!(smpp->spd_flags & RTLD_INIT)) { /* Not initialized yet */ + smpp->spd_flags |= RTLD_INIT; + + /* Make sure all the children are initialized */ + if(smpp->spd_children != NULL) + init_sods(smpp->spd_children); + + if(call_map(smp, ".init") == -1) + call_map(smp, "__init"); + } +} + + static void +init_sods(solp) + struct so_list *solp; +{ + /* Recursively initialize the rest of the list */ + if(solp->sol_next != NULL) + init_sods(solp->sol_next); + + /* Initialize the first element of the list */ + init_object(solp->sol_map); +} + + +/* + * Call a function in a given shared object. SMP is the shared object, and + * SYM is the name of the function. + * + * Returns 0 on success, or -1 if the symbol was not found. Failure is not + * necessarily an error condition, so no error message is generated. + */ + static int +call_map(smp, sym) + struct so_map *smp; + char *sym; +{ + struct so_map *src_map = smp; + struct nzlist *np; + + np = lookup(sym, &src_map, 1); + if (np) { + (*(void (*)())(src_map->som_addr + np->nz_value))(); + return 0; + } + + return -1; +} + +/* + * Run-time common symbol table. + */ + +#define RTC_TABSIZE 57 +static struct rt_symbol *rt_symtab[RTC_TABSIZE]; + +/* + * Compute hash value for run-time symbol table + */ + static inline int +hash_string(key) + char *key; +{ + register char *cp; + register int k; + + cp = key; + k = 0; + while (*cp) + k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff; + + return k; +} + +/* + * Lookup KEY in the run-time common symbol table. + */ + + static inline struct rt_symbol * +lookup_rts(key) + char *key; +{ + register int hashval; + register struct rt_symbol *rtsp; + + /* Determine which bucket. */ + + hashval = hash_string(key) % RTC_TABSIZE; + + /* Search the bucket. */ + + for (rtsp = rt_symtab[hashval]; rtsp; rtsp = rtsp->rt_link) + if (strcmp(key, rtsp->rt_sp->nz_name) == 0) + return rtsp; + + return NULL; +} + + static struct rt_symbol * +enter_rts(name, value, type, srcaddr, size, smp) + char *name; + long value; + int type; + caddr_t srcaddr; + long size; + struct so_map *smp; +{ + register int hashval; + register struct rt_symbol *rtsp, **rpp; + + /* Determine which bucket */ + hashval = hash_string(name) % RTC_TABSIZE; + + /* Find end of bucket */ + for (rpp = &rt_symtab[hashval]; *rpp; rpp = &(*rpp)->rt_link) + continue; + + /* Allocate new common symbol */ + rtsp = (struct rt_symbol *)malloc(sizeof(struct rt_symbol)); + rtsp->rt_sp = (struct nzlist *)malloc(sizeof(struct nzlist)); + rtsp->rt_sp->nz_name = strdup(name); + rtsp->rt_sp->nz_value = value; + rtsp->rt_sp->nz_type = type; + rtsp->rt_sp->nz_size = size; + rtsp->rt_srcaddr = srcaddr; + rtsp->rt_smp = smp; + rtsp->rt_link = NULL; + + /* Link onto linear list as well */ + rtsp->rt_next = rt_symbol_head; + rt_symbol_head = rtsp; + + *rpp = rtsp; + + return rtsp; +} + + +/* + * Lookup NAME in the link maps. The link map producing a definition + * is returned in SRC_MAP. If SRC_MAP is not NULL on entry the search is + * confined to that map. If STRONG is set, the symbol returned must + * have a proper type (used by binder()). + */ + static struct nzlist * +lookup(name, src_map, strong) + char *name; + struct so_map **src_map; /* IN/OUT */ + int strong; +{ + long common_size = 0; + struct so_map *smp; + struct rt_symbol *rtsp; + + if ((rtsp = lookup_rts(name)) != NULL) + return rtsp->rt_sp; + + /* + * Search all maps for a definition of NAME + */ + for (smp = link_map_head; smp; smp = smp->som_next) { + int buckets; + long hashval; + struct rrs_hash *hp; + char *cp; + struct nzlist *np; + + /* Some local caching */ + long symbolbase; + struct rrs_hash *hashbase; + char *stringbase; + int symsize; + + if (*src_map && smp != *src_map) + continue; + + if ((buckets = LD_BUCKETS(smp->som_dynamic)) == 0) + continue; + + if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) + continue; + +restart: + /* + * Compute bucket in which the symbol might be found. + */ + for (hashval = 0, cp = name; *cp; cp++) + hashval = (hashval << 1) + *cp; + + hashval = (hashval & 0x7fffffff) % buckets; + + hashbase = LM_HASH(smp); + hp = hashbase + hashval; + if (hp->rh_symbolnum == -1) + /* Nothing in this bucket */ + continue; + + symbolbase = (long)LM_SYMBOL(smp, 0); + stringbase = LM_STRINGS(smp); + symsize = LD_VERSION_NZLIST_P(smp->som_dynamic->d_version)? + sizeof(struct nzlist) : + sizeof(struct nlist); + while (hp) { + np = (struct nzlist *) + (symbolbase + hp->rh_symbolnum * symsize); + cp = stringbase + np->nz_strx; + if (strcmp(cp, name) == 0) + break; + if (hp->rh_next == 0) + hp = NULL; + else + hp = hashbase + hp->rh_next; + } + if (hp == NULL) + /* Nothing in this bucket */ + continue; + + /* + * We have a symbol with the name we're looking for. + */ + if (np->nz_type == N_INDR+N_EXT) { + /* + * Next symbol gives the aliased name. Restart + * search with new name and confine to this map. + */ + name = stringbase + (++np)->nz_strx; + *src_map = smp; + goto restart; + } + + if (np->nz_value == 0) + /* It's not a definition */ + continue; + + if (np->nz_type == N_UNDF+N_EXT && np->nz_value != 0) { + if (np->nz_other == AUX_FUNC) { + /* It's a weak function definition */ + if (strong) + continue; + } else { + /* It's a common, note value and continue search */ + if (common_size < np->nz_value) + common_size = np->nz_value; + continue; + } + } + + *src_map = smp; + return np; + } + + if (common_size == 0) + /* Not found */ + return NULL; + + /* + * It's a common, enter into run-time common symbol table. + */ + rtsp = enter_rts(name, (long)calloc(1, common_size), + N_UNDF + N_EXT, 0, common_size, NULL); + +#if DEBUG + xprintf("Allocating common: %s size %d at %#x\n", name, common_size, + rtsp->rt_sp->nz_value); +#endif + + return rtsp->rt_sp; +} + +/* + * This routine is called from the jumptable to resolve + * procedure calls to shared objects. + */ + long +binder(jsp) + jmpslot_t *jsp; +{ + struct so_map *smp, *src_map = NULL; + long addr; + char *sym; + struct nzlist *np; + int index; + + /* + * Find the PLT map that contains JSP. + */ + for (smp = link_map_head; smp; smp = smp->som_next) { + if (LM_PLT(smp) < jsp && + jsp < LM_PLT(smp) + LD_PLTSZ(smp->som_dynamic)/sizeof(*jsp)) + break; + } + + if (smp == NULL) + errx(1, "Call to binder from unknown location: %#x\n", jsp); + + index = jsp->reloc_index & JMPSLOT_RELOC_MASK; + + /* Get the local symbol this jmpslot refers to */ + sym = LM_STRINGS(smp) + + LM_SYMBOL(smp,RELOC_SYMBOL(&LM_REL(smp)[index]))->nz_strx; + + np = lookup(sym, &src_map, 1); + if (np == NULL) + errx(1, "Undefined symbol \"%s\" called from %s:%s at %#x", + sym, main_progname, smp->som_path, jsp); + + /* Fixup jmpslot so future calls transfer directly to target */ + addr = np->nz_value; + if (src_map) + addr += (long)src_map->som_addr; + + md_fix_jmpslot(jsp, (long)jsp, addr); + +#if DEBUG + xprintf(" BINDER: %s located at = %#x in %s\n", sym, addr, + src_map->som_path); +#endif + return addr; +} + +static struct hints_header *hheader; /* NULL means not mapped */ +static struct hints_bucket *hbuckets; +static char *hstrtab; + +/* + * Map the hints file into memory, if it is not already mapped. Returns + * 0 on success, or -1 on failure. + */ + static int +maphints __P((void)) +{ + static int hints_bad; /* TRUE if hints are unusable */ + static int paths_added; + int hfd; + struct hints_header hdr; + caddr_t addr; + + if (hheader != NULL) /* Already mapped */ + return 0; + + if (hints_bad) /* Known to be corrupt or unavailable */ + return -1; + + if ((hfd = open(_PATH_LD_HINTS, O_RDONLY, 0)) == -1) { + hints_bad = 1; + return -1; + } + + /* Read the header and check it */ + + if (read(hfd, &hdr, sizeof hdr) != sizeof hdr || + HH_BADMAG(hdr) || + (hdr.hh_version != LD_HINTS_VERSION_1 && + hdr.hh_version != LD_HINTS_VERSION_2)) { + close(hfd); + hints_bad = 1; + return -1; + } + + /* Map the hints into memory */ + + addr = mmap(0, hdr.hh_ehints, PROT_READ, MAP_SHARED, hfd, 0); + if (addr == (caddr_t)-1) { + close(hfd); + hints_bad = 1; + return -1; + } + + close(hfd); + + hheader = (struct hints_header *)addr; + hbuckets = (struct hints_bucket *)(addr + hheader->hh_hashtab); + hstrtab = (char *)(addr + hheader->hh_strtab); + /* pluck out the system ldconfig path */ + if (hheader->hh_version >= LD_HINTS_VERSION_2 && !paths_added) { + add_search_path(hstrtab + hheader->hh_dirlist); + paths_added = 1; + } + + return 0; +} + +/* + * Unmap the hints file, if it is currently mapped. + */ + static void +unmaphints() +{ + if (hheader != NULL) { + munmap((caddr_t)hheader, hheader->hh_ehints); + hheader = NULL; + } +} + + int +hinthash(cp, vmajor) + char *cp; + int vmajor; +{ + int k = 0; + + while (*cp) + k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff; + + k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff; + + return k; +} + +#undef major +#undef minor + +/* + * Search for a library in the hints generated by ldconfig. On success, + * returns the full pathname of the matching library. This string is + * always dynamically allocated on the heap. + * + * Returns the minor number of the matching library via the pointer + * argument MINORP. + * + * Returns NULL if the library cannot be found. + */ + static char * +findhint(name, major, minorp) + char *name; + int major; + int *minorp; +{ + struct hints_bucket *bp = + hbuckets + (hinthash(name, major) % hheader->hh_nbucket); + + while (1) { + /* Sanity check */ + if (bp->hi_namex >= hheader->hh_strtab_sz) { + warnx("Bad name index: %#x\n", bp->hi_namex); + break; + } + if (bp->hi_pathx >= hheader->hh_strtab_sz) { + warnx("Bad path index: %#x\n", bp->hi_pathx); + break; + } + + /* + * We accept the current hints entry if its name matches + * and its major number matches. We don't have to search + * for the best minor number, because that was already + * done by "ldconfig" when it built the hints file. + */ + if (strcmp(name, hstrtab + bp->hi_namex) == 0 && + bp->hi_major == major) { + struct stat s; + + if (stat(hstrtab + bp->hi_pathx, &s) == -1) + return NULL; /* Doesn't actually exist */ + *minorp = bp->hi_ndewey >= 2 ? bp->hi_minor : -1; + return strdup(hstrtab + bp->hi_pathx); + } + + if (bp->hi_next == -1) + break; + + /* Move on to next in bucket */ + bp = &hbuckets[bp->hi_next]; + } + + /* No hints available for name */ + return NULL; +} + +/* + * Search for the given shared library. On success, returns a string + * containing the full pathname for the library. This string is always + * dynamically allocated on the heap. + * + * Returns NULL if the library cannot be found. + */ + static char * +rtfindlib(name, major, minor) + char *name; + int major, minor; +{ + char *ld_path = ld_library_path; + char *path = NULL; + int realminor = -1; + + if (ld_path != NULL) { /* First, search the directories in ld_path */ + /* + * There is no point in trying to use the hints file for this. + */ + char *dir; + + while (path == NULL && (dir = strsep(&ld_path, ":")) != NULL) { + path = search_lib_dir(dir, name, &major, &realminor, 0); + if (ld_path != NULL) + *(ld_path - 1) = ':'; + } + } + + if (path == NULL && maphints() == 0) /* Search the hints file */ + path = findhint(name, major, &realminor); + + if (path == NULL) /* Search the standard directories */ + path = findshlib(name, &major, &realminor, 0); + + if (path != NULL && realminor < minor && !ld_suppress_warnings) { + warnx("warning: %s: minor version %d" + " older than expected %d, using it anyway", + path, realminor, minor); + } + + return path; +} + +/* + * Search for the given shared library file. This is similar to rtfindlib, + * except that the argument is the actual name of the desired library file. + * Thus there is no need to worry about version numbers. The return value + * is a string containing the full pathname for the library. This string + * is always dynamically allocated on the heap. + * + * Returns NULL if the library cannot be found. + */ + static char * +rtfindfile(name) + char *name; +{ + char *ld_path = ld_library_path; + char *path = NULL; + + if (ld_path != NULL) { /* First, search the directories in ld_path */ + char *dir; + + while (path == NULL && (dir = strsep(&ld_path, ":")) != NULL) { + struct stat sb; + + path = concat(dir, "/", name); + if (lstat(path, &sb) == -1) { /* Does not exist */ + free(path); + path = NULL; + } + if (ld_path != NULL) + *(ld_path - 1) = ':'; + } + } + + /* + * We don't search the hints file. It is organized around major + * and minor version numbers, so it is not suitable for finding + * a specific file name. + */ + + if (path == NULL) /* Search the standard directories */ + path = find_lib_file(name); + + return path; +} + +/* + * Buffer for error messages and a pointer that is set to point to the buffer + * when a error occurs. It acts as a last error flag, being set to NULL + * after an error is returned. + */ +#define DLERROR_BUF_SIZE 512 +static char dlerror_buf [DLERROR_BUF_SIZE]; +static char *dlerror_msg = NULL; + + + static void * +__dlopen(path, mode) + char *path; + int mode; +{ + struct so_map *old_tail = link_map_tail; + struct so_map *smp; + int bind_now = mode == RTLD_NOW; + + /* + * path == NULL is handled by map_object() + */ + + anon_open(); + + /* Map the object, and the objects on which it depends */ + smp = map_object(path, (struct sod *) NULL, (struct so_map *) NULL); + if(smp == NULL) /* Failed */ + return NULL; + LM_PRIVATE(smp)->spd_flags |= RTLD_DL; + + /* Relocate and initialize all newly-mapped objects */ + if(link_map_tail != old_tail) { /* We have mapped some new objects */ + if(reloc_and_init(smp, bind_now) == -1) /* Failed */ + return NULL; + } + + unmaphints(); + anon_close(); + + return smp; +} + + static int +__dlclose(fd) + void *fd; +{ + struct so_map *smp = (struct so_map *)fd; + struct so_map *scanp; + +#ifdef DEBUG + xprintf("dlclose(%s): refcount = %d\n", smp->som_path, + LM_PRIVATE(smp)->spd_refcount); +#endif + /* Check the argument for validity */ + for(scanp = link_map_head; scanp != NULL; scanp = scanp->som_next) + if(scanp == smp) /* We found the map in the list */ + break; + if(scanp == NULL || !(LM_PRIVATE(smp)->spd_flags & RTLD_DL)) { + generror("Invalid argument to dlclose"); + return -1; + } + + unmap_object(smp, 0); + + return 0; +} + +/* + * This form of dlsym is obsolete. Current versions of crt0 don't call + * it. It can still be called by old executables that were linked with + * old versions of crt0. + */ + static void * +__dlsym(fd, sym) + void *fd; + char *sym; +{ + if (fd == RTLD_NEXT) { + generror("RTLD_NEXT not supported by this version of" + " /usr/lib/crt0.o"); + return NULL; + } + return __dlsym3(fd, sym, NULL); +} + + static void * +__dlsym3(fd, sym, retaddr) + void *fd; + char *sym; + void *retaddr; +{ + struct so_map *smp; + struct so_map *src_map; + struct nzlist *np; + long addr; + + if (fd == RTLD_NEXT) { + /* Find the shared object that contains the caller. */ + for (smp = link_map_head; smp != NULL; smp = smp->som_next) { + void *textbase = smp->som_addr + LM_TXTADDR(smp); + void *textlimit = LM_ETEXT(smp); + + if (textbase <= retaddr && retaddr < textlimit) + break; + } + if (smp == NULL) { + generror("Cannot determine caller's shared object"); + return NULL; + } + smp = smp->som_next; + if (smp != NULL && LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) + smp = smp->som_next; + if (smp == NULL) { + generror("No next shared object for RTLD_NEXT"); + return NULL; + } + do { + src_map = smp; + np = lookup(sym, &src_map, 1); + } while (np == NULL && (smp = smp->som_next) != NULL); + } else { + smp = (struct so_map *)fd; + src_map = NULL; + + /* + * Restrict search to passed map if dlopen()ed. + */ + if (smp != NULL && LM_PRIVATE(smp)->spd_flags & RTLD_DL) + src_map = smp; + + np = lookup(sym, &src_map, 1); + } + + if (np == NULL) { + generror("Undefined symbol"); + return NULL; + } + + addr = np->nz_value; + if (src_map) + addr += (long)src_map->som_addr; + + return (void *)addr; +} + + static char * +__dlerror __P((void)) +{ + char *err; + + err = dlerror_msg; + dlerror_msg = NULL; /* Next call will return NULL */ + + return err; +} + + static void +__dlexit __P((void)) +{ +#ifdef DEBUG +xprintf("__dlexit called\n"); +#endif + + unmap_object(link_map_head, 1); +} + +/* + * Print the current error message and exit with failure status. + */ +static void +die __P((void)) +{ + char *msg; + + fprintf(stderr, "ld.so failed"); + if ((msg = __dlerror()) != NULL) + fprintf(stderr, ": %s", msg); + putc('\n', stderr); + _exit(1); +} + + +/* + * Generate an error message that can be later be retrieved via dlerror. + */ +static void +#if __STDC__ +generror(char *fmt, ...) +#else +generror(fmt, va_alist) +char *fmt; +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vsnprintf (dlerror_buf, DLERROR_BUF_SIZE, fmt, ap); + dlerror_msg = dlerror_buf; + + va_end(ap); +} + +void +#if __STDC__ +xprintf(char *fmt, ...) +#else +xprintf(fmt, va_alist) +char *fmt; +#endif +{ + char buf[256]; + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + + vsnprintf(buf, sizeof(buf), fmt, ap); + (void)write(1, buf, strlen(buf)); + va_end(ap); +} + +/* + * rt_readenv() etc. + * + * Do a sweep over the environment once only, pick up what + * looks interesting. + * + * This is pretty obscure, but is relatively simple. Simply + * look at each environment variable, if it starts with "LD_" then + * look closer at it. If it's in our table, set the variable + * listed. effectively, this is like: + * ld_preload = careful ? NULL : getenv("LD_PRELOAD"); + * except that the environment is scanned once only to pick up all + * known variables, rather than scanned multiple times for each + * variable. + * + * If an environment variable of interest is set to the empty string, we + * treat it as if it were unset. + */ + +#define L(n, u, v) { n, sizeof(n) - 1, u, v }, +struct env_scan_tab { + char *name; + int len; + int unsafe; + char **value; +} scan_tab[] = { + L("LD_LIBRARY_PATH=", 1, &ld_library_path) + L("LD_PRELOAD=", 1, &ld_preload) + L("LD_IGNORE_MISSING_OBJECTS=", 1, &ld_ignore_missing_objects) + L("LD_TRACE_LOADED_OBJECTS=", 0, &ld_tracing) + L("LD_BIND_NOW=", 0, &ld_bind_now) + L("LD_SUPPRESS_WARNINGS=", 0, &ld_suppress_warnings) + L("LD_WARN_NON_PURE_CODE=", 0, &ld_warn_non_pure_code) + { NULL, 0, NULL } +}; +#undef L + +static void +rt_readenv() +{ + char **p = environ; + char *v; + struct env_scan_tab *t; + + /* for each string in the environment... */ + while ((v = *p++)) { + + /* check for LD_xxx */ + if (v[0] != 'L' || v[1] != 'D' || v[2] != '_') + continue; + + for (t = scan_tab; t->name; t++) { + if (careful && t->unsafe) + continue; /* skip for set[ug]id */ + if (strncmp(t->name, v, t->len) == 0) { + if (*(v + t->len) != '\0') /* Not empty */ + *t->value = v + t->len; + break; + } + } + } +} diff --git a/libexec/rtld-aout/shlib.c b/libexec/rtld-aout/shlib.c new file mode 100644 index 0000000..efb0bb6 --- /dev/null +++ b/libexec/rtld-aout/shlib.c @@ -0,0 +1,342 @@ +/* + * Copyright (c) 1993 Paul Kranenburg + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Paul Kranenburg. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id$ + */ + +#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 <link.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" +#endif + +/* + * Actual vector of library search directories, + * including `-L'ed and LD_LIBRARY_PATH spec'd ones. + */ +char **search_dirs; +int n_search_dirs; + +char *standard_search_dirs[] = { + STANDARD_SEARCH_DIRS +}; + + +void +add_search_dir(name) + char *name; +{ + 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(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() +{ + int i, n; + + /* Append standard search directories */ + n = sizeof standard_search_dirs / sizeof standard_search_dirs[0]; + for (i = 0; i < n; i++) + add_search_dir(standard_search_dirs[i]); +} + +/* + * Return true if CP points to a valid dewey number. + * Decode and leave the result in the array DEWEY. + * Return the number of decoded entries in DEWEY. + */ + +int +getdewey(dewey, cp) +int dewey[]; +char *cp; +{ + int i, n; + + for (n = 0, i = 0; i < MAXDEWEY; i++) { + if (*cp == '\0') + break; + + if (*cp == '.') cp++; + if (!isdigit(*cp)) + return 0; + + dewey[n++] = strtol(cp, &cp, 10); + } + + return n; +} + +/* + * Compare two dewey arrays. + * Return -1 if `d1' represents a smaller value than `d2'. + * Return 1 if `d1' represents a greater value than `d2'. + * Return 0 if equal. + */ +int +cmpndewey(d1, n1, d2, n2) +int d1[], d2[]; +int n1, n2; +{ + register int i; + + for (i = 0; i < n1 && i < n2; i++) { + if (d1[i] < d2[i]) + return -1; + if (d1[i] > d2[i]) + return 1; + } + + if (n1 == n2) + return 0; + + if (i == n1) + return -1; + + if (i == n2) + return 1; + + errx(1, "cmpndewey: cant happen"); + return 0; +} + +/* + * Search directories for a shared library matching the given + * major and minor version numbers. 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(name, majorp, minorp, do_dot_a) +char *name; +int *majorp, *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(name) + 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(dir, name, majorp, minorp, do_dot_a) + char *dir; + char *name; + int *majorp; + int *minorp; + int do_dot_a; +{ + int 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 == 0) /* No version number */ + continue; + + if(*majorp != -1) { /* Need exact match on major */ + if(cur_dewey[0] != *majorp) + continue; + if(*minorp != -1) { /* Need minor >= minimum */ + if(cur_ndewey < 2 || + 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]; + if(best_ndewey >= 2) + *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..796d37e --- /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. + *- + * $Id$ + */ + +/* + * prototypes for shlib.c. Big deal. + */ + +extern char **search_dirs; +extern int n_search_dirs; + +void add_search_dir __P((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((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..0d9df2a --- /dev/null +++ b/libexec/rtld-aout/support.c @@ -0,0 +1,86 @@ +/* + * 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. + *- + * $Id$ + */ +#include <sys/types.h> +#include <string.h> +#include <stdlib.h> +#include <err.h> + +#include "support.h" + +char * +concat(s1, s2, s3) + const char *s1, *s2, *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(n) + size_t n; +{ + char *p = malloc(n); + + if (p == NULL) + errx(1, "Could not allocate memory"); + + return p; +} + +void * +xrealloc(p, n) + 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..5be1e31 --- /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. + *- + * $Id$ + */ + +/* + * 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/rtld.1 b/libexec/rtld-elf/rtld.1 new file mode 100644 index 0000000..dbd4dde --- /dev/null +++ b/libexec/rtld-elf/rtld.1 @@ -0,0 +1,224 @@ +.\" $Id: rtld.1,v 1.13 1997/02/22 15:46:47 peter Exp $ +.\" +.\" 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. +.\" +.Dd June 27, 1995 +.Dt RTLD 1 +.Os FreeBSD +.Sh NAME +.Nm ld.so +.Nd run-time link-editor +.Sh DESCRIPTION +.Nm +is a self-contained, position independent program image providing run-time +support for loading and link-editing shared objects into a process' +address space. It uses the data structures +.Po +see +.Xr link 5 +.Pc +contained within dynamically linked programs to determine which shared +libraries are needed and loads them at a convenient virtual address +using the +.Xr mmap 2 +system call. +.Pp +After all shared libraries have been 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 +.Nm +is itself a shared object that is initially loaded by the startup module +.Em crt0 . +Since +.Xr a.out 5 +formats do not provide easy access to the file header from within a running +process, +.Em crt0 +uses the special symbol +.Va _DYNAMIC +to determine whether a program is in fact dynamically linked or not. Whenever +the linker +.Xr ld 1 +has relocated this symbol to a location other than 0, +.Em crt0 +assumes the services of +.Nm +are needed +.Po +see +.Xr link 5 +for details +.Pc \&. +.Em crt0 +passes control to +.Nm +\&'s entry point before the program's +.Fn main +routine is called. Thus, +.Nm +can complete the link-editing process before the dynamic program calls upon +services of any dynamic library. +.Pp +To quickly locate the required shared objects in the filesystem, +.Nm +may use a +.Dq hints +file, prepared by the +.Xr ldconfig 8 +utility, in which the full path specification of the shared objects can be +looked up by hashing on the 3-tuple +.Ao +library-name, major-version-number, minor-version-number +.Ac \&. +.Pp +.Nm +recognises a number of environment variables that can be used to modify +its behaviour as follows: +.Pp +.Bl -tag -width "LD_IGNORE_MISSING_OBJECTS" +.It Ev LD_LIBRARY_PATH +A colon separated list of directories, overriding the default search path +for shared libraries. +This is ignored for set-user-ID and set-group-ID programs. +.It Ev LD_PRELOAD +A colon separated list of shared libraries, to be linked in before any +other shared libraries. If the directory is not specified then +the directories specified by LD_LIBRARY_PATH will be searched first +followed by the set of built-in standard directories. +This is ignored 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_WARN_NON_PURE_CODE +When set to a nonempty string, issue a warning whenever a link-editing +operation requires modification of the text segment of some loaded +object. This is usually indicative of an incorrectly built library. +.It Ev LD_SUPPRESS_WARNINGS +When set to a nonempty string, no warning messages of any kind are +issued. Normally, a warning is given if satisfactorily versioned +library could not be found. +.It Ev LD_IGNORE_MISSING_OBJECTS +When set to a nonempty string, makes it a nonfatal condition if +one or more required shared objects cannot be loaded. +Loading and execution proceeds using the objects that are +available. +A warning is produced for each missing object, unless the environment +variable +.Ev LD_SUPPRESS_WARNINGS +is set to a nonempty string. +.Pp +This is ignored for set-user-ID and set-group-ID programs. +.Pp +Missing shared objects can be ignored without errors if all the +following conditions hold: +.Bl -bullet +.It +They do not supply definitions for any required data symbols. +.It +No functions defined by them are called during program execution. +.It +The environment variable +.Ev LD_BIND_NOW +is unset or is set to the empty string. +.El +.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_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 's +.Fl f +option and allows +.Xr ldd 1 +to be operated as a filter more conveniently. +The following conversions can be used: +.Bl -tag -indent "LD_TRACE_LOADED_OBJECTS_FMT1 " -width "xxxx" +.It \&%a +The main program's name +.Po also known as +.Dq __progname +.Pc . +.It \&%A +The value of the environment variable +.Ev LD_TRACE_LOADED_OBJECTS_PROGNAME +.It \&%o +The library name. +.It \&%m +The library's major version number. +.It \&%n +The library's minor version number. +.It \&%p +The full pathname as determined by +.Nm rtld Ns 's +library search rules. +.It \&%x +The library's load address. +.El +.Pp +Additionally, +.Sy \en +and +.Sy \et +are recognised and have their usual meaning. +.\" .It Ev LD_NO_INTERN_SEARCH +.\" When set, +.\" .Nm +.\" does not process any internal search paths that were recorded in the +.\" executable. +.\" .It Ev LD_NOSTD_PATH +.\" When set, do not include a set of built-in standard directory paths for +.\" searching. This might be useful when running on a system with a completely +.\" non-standard filesystem layout. +.El +.Pp +.Sh FILES +/var/run/ld.so.hints +.Pp +.Sh SEE ALSO +.Xr ld 1 , +.Xr link 5 , +.Xr ldconfig 8 +.Sh HISTORY +The shared library model employed first appeared in SunOS 4.0 diff --git a/libexec/talkd/Makefile b/libexec/talkd/Makefile index 51f6806..1754066 100644 --- a/libexec/talkd/Makefile +++ b/libexec/talkd/Makefile @@ -1,8 +1,9 @@ # @(#)Makefile 8.1 (Berkeley) 6/4/93 +# $Id$ PROG= ntalkd SRCS= talkd.c announce.c process.c table.c print.c ttymsg.c .PATH: ${.CURDIR}/../../usr.bin/wall -MAN8= talkd.0 +MAN8= talkd.8 .include <bsd.prog.mk> diff --git a/libexec/talkd/announce.c b/libexec/talkd/announce.c index 7c99ea4..2ce6e53 100644 --- a/libexec/talkd/announce.c +++ b/libexec/talkd/announce.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id$ */ #ifndef lint @@ -41,24 +43,26 @@ static char sccsid[] = "@(#)announce.c 8.3 (Berkeley) 4/28/95"; #include <sys/time.h> #include <sys/wait.h> #include <sys/socket.h> + #include <protocols/talkd.h> -#include <sgtty.h> + #include <errno.h> -#include <syslog.h> -#include <unistd.h> +#include <paths.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> +#include <syslog.h> +#include <unistd.h> #include <vis.h> -#include <paths.h> extern char hostname[]; /* * Announce an invitation to talk. */ - + /* - * See if the user is accepting messages. If so, announce that + * See if the user is accepting messages. If so, announce that * a talk is requested. */ announce(request, remote_machine) @@ -81,7 +85,7 @@ announce(request, remote_machine) #define N_CHARS 256 /* - * Build a block of characters containing the message. + * Build a block of characters containing the message. * It is sent blank filled and in a single block to * try to keep the message in one piece if the recipient * in in vi at the time @@ -107,33 +111,35 @@ print_mesg(tty, tf, request, remote_machine) max_size = 0; gettimeofday(&clock, &zone); localclock = localtime( &clock.tv_sec ); - (void)sprintf(line_buf[i], " "); + (void)snprintf(line_buf[i], N_CHARS, " "); sizes[i] = strlen(line_buf[i]); max_size = max(max_size, sizes[i]); i++; - (void)sprintf(line_buf[i], "Message from Talk_Daemon@%s at %d:%02d ...", - hostname, localclock->tm_hour , localclock->tm_min ); + (void)snprintf(line_buf[i], N_CHARS, + "Message from Talk_Daemon@%s at %d:%02d ...", + hostname, localclock->tm_hour , localclock->tm_min ); sizes[i] = strlen(line_buf[i]); max_size = max(max_size, sizes[i]); i++; - vis_user = (char *) malloc(strlen(request->l_name) * 4 + 1); + + vis_user = malloc(strlen(request->l_name) * 4 + 1); strvis(vis_user, request->l_name, VIS_CSTYLE); - (void)sprintf(line_buf[i], "talk: connection requested by %s@%s", - vis_user, remote_machine); + (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)sprintf(line_buf[i], "talk: respond with: talk %s@%s", - vis_user, remote_machine); + (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)sprintf(line_buf[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++ = ''; /* send something to wake them up */ + *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++) { diff --git a/libexec/talkd/print.c b/libexec/talkd/print.c index 9c0085b..af9e119 100644 --- a/libexec/talkd/print.c +++ b/libexec/talkd/print.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id$ */ #ifndef lint @@ -46,7 +48,7 @@ static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/4/93"; static char *types[] = { "leave_invite", "look_up", "delete", "announce" }; #define NTYPES (sizeof (types) / sizeof (types[0])) -static char *answers[] = +static char *answers[] = { "success", "not_here", "failed", "machine_unknown", "permission_denied", "unknown_request", "badversion", "badaddr", "badctladdr" }; #define NANSWERS (sizeof (answers) / sizeof (answers[0])) @@ -56,7 +58,7 @@ print_request(cp, mp) register CTL_MSG *mp; { char tbuf[80], *tp; - + if (mp->type > NTYPES) { (void)sprintf(tbuf, "type %d", mp->type); tp = tbuf; @@ -71,7 +73,7 @@ print_response(cp, rp) register CTL_RESPONSE *rp; { char tbuf[80], *tp, abuf[80], *ap; - + if (rp->type > NTYPES) { (void)sprintf(tbuf, "type %d", rp->type); tp = tbuf; diff --git a/libexec/talkd/process.c b/libexec/talkd/process.c index dd05e6b..df087e3 100644 --- a/libexec/talkd/process.c +++ b/libexec/talkd/process.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id$ */ #ifndef lint @@ -52,6 +54,7 @@ static char sccsid[] = "@(#)process.c 8.2 (Berkeley) 11/16/93"; #include <syslog.h> #include <stdio.h> #include <string.h> +#include <ctype.h> #include <paths.h> CTL_MSG *find_request(); @@ -63,6 +66,7 @@ process_request(mp, rp) { register CTL_MSG *ptr; extern int debug; + char *s; rp->vers = TALK_VERSION; rp->type = mp->type; @@ -87,6 +91,12 @@ process_request(mp, rp) 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); @@ -182,6 +192,7 @@ find_user(name, tty) int status; FILE *fd; struct stat statb; + time_t best = 0; char line[sizeof(ubuf.ut_line) + 1]; char ftty[sizeof(_PATH_DEV) - 1 + sizeof(line)]; @@ -196,17 +207,21 @@ find_user(name, tty) if (SCMPN(ubuf.ut_name, name) == 0) { strncpy(line, ubuf.ut_line, sizeof(ubuf.ut_line)); line[sizeof(ubuf.ut_line)] = '\0'; - if (*tty == '\0') { - status = PERMISSION_DENIED; + if (*tty == '\0' || best != 0) { + if (best == 0) + status = PERMISSION_DENIED; /* no particular tty was requested */ (void) strcpy(ftty + sizeof(_PATH_DEV) - 1, line); if (stat(ftty, &statb) == 0) { if (!(statb.st_mode & 020)) continue; - (void) strcpy(tty, line); - status = SUCCESS; - break; + if (statb.st_atime > best) { + best = statb.st_atime; + (void) strcpy(tty, line); + status = SUCCESS; + continue; + } } } if (strcmp(line, tty) == 0) { diff --git a/libexec/talkd/table.c b/libexec/talkd/table.c index cc8cb66..a31c611 100644 --- a/libexec/talkd/table.c +++ b/libexec/talkd/table.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id$ */ #ifndef lint @@ -38,7 +40,7 @@ static char sccsid[] = "@(#)table.c 8.1 (Berkeley) 6/4/93"; /* * 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 + * 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 @@ -110,7 +112,7 @@ find_match(request) /* * Look for an identical request, as opposed to a complimentary - * one as find_match does + * one as find_match does */ CTL_MSG * find_request(request) diff --git a/libexec/talkd/talkd.c b/libexec/talkd/talkd.c index 669931b..acaa888 100644 --- a/libexec/talkd/talkd.c +++ b/libexec/talkd/talkd.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id$ */ #ifndef lint @@ -43,12 +45,13 @@ static char sccsid[] = "@(#)talkd.c 8.1 (Berkeley) 6/4/93"; /* * The top level of the daemon, the format is heavily borrowed - * from rwhod.c. Basically: find out who and where you are; + * from rwhod.c. Basically: find out who and where you are; * disconnect all descriptors and ttys, and then endless * loop on waiting for and processing requests */ #include <sys/types.h> #include <sys/socket.h> +#include <sys/param.h> #include <protocols/talkd.h> #include <signal.h> #include <syslog.h> @@ -68,7 +71,7 @@ int debug = 0; void timeout(); long lastmsgtime; -char hostname[32]; +char hostname[MAXHOSTNAMELEN + 1]; #define TIMEOUT 30 #define MAXIDLE 120 @@ -109,6 +112,7 @@ main(argc, argv) lastmsgtime = time(0); process_request(mp, &response); /* can block here, is this what I want? */ + mp->ctl_addr.sa_family = htons(mp->ctl_addr.sa_family); cc = sendto(sockt, (char *)&response, sizeof (response), 0, (struct sockaddr *)&mp->ctl_addr, sizeof (mp->ctl_addr)); diff --git a/libexec/telnetd/Makefile b/libexec/telnetd/Makefile index 4b1d530..7c9919e 100644 --- a/libexec/telnetd/Makefile +++ b/libexec/telnetd/Makefile @@ -1,36 +1,19 @@ # @(#)Makefile 8.2 (Berkeley) 12/15/93 +# $Id$ PROG= telnetd -CFLAGS+=-DLINEMODE -DKLUDGELINEMODE -DUSE_TERMIO -DDIAGNOSTICS +CFLAGS+=-DLINEMODE -DUSE_TERMIO -DDIAGNOSTICS +#CFLAGS+=-DKLUDGELINEMODE CFLAGS+=-DOLD_ENVIRON -DENV_HACK -CFLAGS+=-DAUTHENTICATION -DENCRYPTION -I${.CURDIR}/../../lib -SRCS= authenc.c global.c slc.c state.c sys_term.c telnetd.c \ +CFLAGS+=-I${.CURDIR}/../../lib +#CFLAGS+=-DAUTHENTICATION -DENCRYPTION +SRCS= global.c slc.c state.c sys_term.c telnetd.c \ termstat.c utility.c -DPADD= ${LIBUTIL} ${LIBTERM} +#SRCS+= authenc.c +DPADD= ${LIBUTIL} ${LIBTERMCAP} ${LIBTELNET} LDADD= -lutil -ltermcap -ltelnet -LDADD+= -lkrb -ldes -MAN8= telnetd.0 - -# These are the sources that have encryption stuff in them. -CRYPT_SRC= authenc.c ext.h state.c telnetd.c termstat.c -CRYPT_SRC+= utility.c Makefile -NOCRYPT_DIR=${.CURDIR}/Nocrypt +#DPADD+= $LIBKRB} ${LIBDES} +#LDADD+= -lkrb -ldes +MAN8= telnetd.8 .include <bsd.prog.mk> - -nocrypt: -#ifdef ENCRYPTION - @for i in ${CRYPT_SRC}; do \ - if [ ! -d ${NOCRYPT_DIR} ]; then \ - echo Creating subdirectory ${NOCRYPT_DIR}; \ - mkdir ${NOCRYPT_DIR}; \ - fi; \ - echo ${NOCRYPT_DIR}/$$i; \ - unifdef -UENCRYPTION ${.CURDIR}/$$i | \ - sed "s/ || defined(ENCRYPTION)//" > ${NOCRYPT_DIR}/$$i; \ - done - -placeholder: -#else /* ENCRYPTION */ - @echo "Encryption code already removed." -#endif /* ENCRYPTION */ diff --git a/libexec/telnetd/ext.h b/libexec/telnetd/ext.h index 19bc0d6..05898fb 100644 --- a/libexec/telnetd/ext.h +++ b/libexec/telnetd/ext.h @@ -31,6 +31,7 @@ * SUCH DAMAGE. * * @(#)ext.h 8.2 (Berkeley) 12/15/93 + * $Id$ */ /* @@ -191,11 +192,6 @@ extern void wontoption P((int)), writenet P((unsigned char *, int)); -#ifdef ENCRYPTION -extern void (*encrypt_output) P((unsigned char *, int)); -extern int (*decrypt_input) P((int)); -extern char *nclearto; -#endif /* ENCRYPTION */ /* @@ -233,7 +229,7 @@ extern int needtermstat; # ifdef ultrix # define DEFAULT_IM "\r\n\r\nULTRIX (%h) (%t)\r\n\r\r\n\r" # else -# define DEFAULT_IM "\r\n\r\n4.4 BSD UNIX (%h) (%t)\r\n\r\r\n\r" +# define DEFAULT_IM "\r\n\r\nFreeBSD (%h) (%t)\r\n\r\r\n\r" # endif # endif # endif diff --git a/libexec/telnetd/pathnames.h b/libexec/telnetd/pathnames.h index c8b0806..979f4d0 100644 --- a/libexec/telnetd/pathnames.h +++ b/libexec/telnetd/pathnames.h @@ -31,6 +31,7 @@ * SUCH DAMAGE. * * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + * $Id$ */ #if BSD > 43 @@ -42,7 +43,7 @@ # endif #else - + # define _PATH_TTY "/dev/tty" # ifndef _PATH_LOGIN # define _PATH_LOGIN "/bin/login" diff --git a/libexec/telnetd/slc.c b/libexec/telnetd/slc.c index 6cbb7ab..a3799f3 100644 --- a/libexec/telnetd/slc.c +++ b/libexec/telnetd/slc.c @@ -29,10 +29,12 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id$ */ #ifndef lint -static char sccsid[] = "@(#)slc.c 8.2 (Berkeley) 5/30/95"; +static char sccsid[] = "@(#)slc.c 8.1 (Berkeley) 6/4/93"; #endif /* not lint */ #include "telnetd.h" @@ -372,6 +374,7 @@ change_slc(func, flag, val) slctab[func].defset.val; val = slctab[func].current.val; } + } add_slc(func, flag, val); } @@ -422,6 +425,7 @@ check_slc() slctab[i].current.val); } } + } /* check_slc */ /* @@ -463,7 +467,7 @@ do_opt_slc(ptr, len) def_slcbuf = (unsigned char *)malloc((unsigned)len); if (def_slcbuf == (unsigned char *)0) return; /* too bad */ - memmove(def_slcbuf, ptr, len); + bcopy(ptr, def_slcbuf, len); } } diff --git a/libexec/telnetd/state.c b/libexec/telnetd/state.c index 4ee8bea..4e72666 100644 --- a/libexec/telnetd/state.c +++ b/libexec/telnetd/state.c @@ -29,10 +29,12 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id$ */ #ifndef lint -static char sccsid[] = "@(#)state.c 8.5 (Berkeley) 5/30/95"; +static char sccsid[] = "@(#)state.c 8.2 (Berkeley) 12/15/93"; #endif /* not lint */ #include "telnetd.h" @@ -94,10 +96,6 @@ telrcv() if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) break; c = *netip++ & 0377, ncc--; -#ifdef ENCRYPTION - if (decrypt_input) - c = (*decrypt_input)(c); -#endif /* ENCRYPTION */ switch (state) { case TS_CR: @@ -126,10 +124,6 @@ telrcv() */ if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) { int nc = *netip; -#ifdef ENCRYPTION - if (decrypt_input) - nc = (*decrypt_input)(nc & 0xff); -#endif /* ENCRYPTION */ #ifdef LINEMODE /* * If we are operating in linemode, @@ -142,10 +136,6 @@ telrcv() } else #endif { -#ifdef ENCRYPTION - if (decrypt_input) - (void)(*decrypt_input)(-1); -#endif /* ENCRYPTION */ state = TS_CR; } } @@ -366,7 +356,7 @@ gotiac: switch (c) { char xbuf2[BUFSIZ]; register char *cp; int n = pfrontp - opfrontp, oc; - memmove(xptyobuf, opfrontp, n); + bcopy(opfrontp, xptyobuf, n); pfrontp = opfrontp; pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP, xbuf2, &oc, BUFSIZ); @@ -464,9 +454,6 @@ extern void auth_request(); #ifdef LINEMODE extern void doclientstat(); #endif -#ifdef ENCRYPTION -extern void encrypt_send_support(); -#endif /* ENCRYPTION */ void willoption(option) @@ -580,12 +567,6 @@ willoption(option) break; #endif -#ifdef ENCRYPTION - case TELOPT_ENCRYPT: - func = encrypt_send_support; - changeok++; - break; -#endif /* ENCRYPTION */ default: break; @@ -645,11 +626,6 @@ willoption(option) break; #endif -#ifdef ENCRYPTION - case TELOPT_ENCRYPT: - func = encrypt_send_support; - break; -#endif /* ENCRYPTION */ case TELOPT_LFLOW: func = flowstat; break; @@ -716,6 +692,7 @@ wontoption(option) */ if (lmodetype != REAL_LINEMODE) break; + lmodetype = KLUDGE_LINEMODE; # endif /* KLUDGELINEMODE */ clientstat(TELOPT_LINEMODE, WONT, 0); break; @@ -939,11 +916,6 @@ dooption(option) /* NOT REACHED */ break; -#ifdef ENCRYPTION - case TELOPT_ENCRYPT: - changeok++; - break; -#endif /* ENCRYPTION */ case TELOPT_LINEMODE: case TELOPT_TTYPE: case TELOPT_NAWS: @@ -1463,49 +1435,6 @@ suboption() } break; #endif -#ifdef ENCRYPTION - case TELOPT_ENCRYPT: - if (SB_EOF()) - break; - switch(SB_GET()) { - case ENCRYPT_SUPPORT: - encrypt_support(subpointer, SB_LEN()); - break; - case ENCRYPT_IS: - encrypt_is(subpointer, SB_LEN()); - break; - case ENCRYPT_REPLY: - encrypt_reply(subpointer, SB_LEN()); - break; - case ENCRYPT_START: - encrypt_start(subpointer, SB_LEN()); - break; - case ENCRYPT_END: - encrypt_end(); - break; - case ENCRYPT_REQSTART: - encrypt_request_start(subpointer, SB_LEN()); - break; - case ENCRYPT_REQEND: - /* - * We can always send an REQEND so that we cannot - * get stuck encrypting. We should only get this - * if we have been able to get in the correct mode - * anyhow. - */ - encrypt_request_end(); - break; - case ENCRYPT_ENC_KEYID: - encrypt_enc_keyid(subpointer, SB_LEN()); - break; - case ENCRYPT_DEC_KEYID: - encrypt_dec_keyid(subpointer, SB_LEN()); - break; - default: - break; - } - break; -#endif /* ENCRYPTION */ default: break; @@ -1519,8 +1448,8 @@ doclientstat() clientstat(TELOPT_LINEMODE, WILL, 0); } -#define ADD(c) *ncp++ = c -#define ADD_DATA(c) { *ncp++ = c; if (c == SE || c == IAC) *ncp++ = c; } +#define ADD(c) *ncp++ = c; +#define ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; } void send_status() { @@ -1549,10 +1478,14 @@ send_status() if (my_want_state_is_will(i)) { ADD(WILL); ADD_DATA(i); + if (i == IAC) + ADD(IAC); } if (his_want_state_is_will(i)) { ADD(DO); ADD_DATA(i); + if (i == IAC) + ADD(IAC); } } @@ -1567,14 +1500,15 @@ send_status() ADD(SE); if (restartany >= 0) { - ADD(SB); + ADD(SB) ADD(TELOPT_LFLOW); if (restartany) { ADD(LFLOW_RESTART_ANY); } else { ADD(LFLOW_RESTART_XON); } - ADD(SE); + ADD(SE) + ADD(SB); } } @@ -1587,6 +1521,8 @@ send_status() ADD(TELOPT_LINEMODE); ADD(LM_MODE); ADD_DATA(editmode); + if (editmode == IAC) + ADD(IAC); ADD(SE); ADD(SB); diff --git a/libexec/telnetd/sys_term.c b/libexec/telnetd/sys_term.c index 58d2e98..1881e32 100644 --- a/libexec/telnetd/sys_term.c +++ b/libexec/telnetd/sys_term.c @@ -29,10 +29,12 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id$ */ #ifndef lint -static char sccsid[] = "@(#)sys_term.c 8.4 (Berkeley) 5/30/95"; +static char sccsid[] = "@(#)sys_term.c 8.2 (Berkeley) 12/15/93"; #endif /* not lint */ #include "telnetd.h" @@ -42,13 +44,15 @@ static char sccsid[] = "@(#)sys_term.c 8.4 (Berkeley) 5/30/95"; #include <libtelnet/auth.h> #endif +extern char *altlogin; + #if defined(CRAY) || defined(__hpux) # define PARENT_DOES_UTMP #endif +int utmp_len = MAXHOSTNAMELEN; #ifdef NEWINIT #include <initreq.h> -int utmp_len = MAXHOSTNAMELEN; /* sizeof(init_request.host) */ #else /* NEWINIT*/ # ifdef UTMPX # include <utmpx.h> @@ -58,10 +62,17 @@ struct utmpx wtmp; struct utmp wtmp; # endif /* UTMPX */ -int utmp_len = sizeof(wtmp.ut_host); # ifndef PARENT_DOES_UTMP +#ifdef _PATH_WTMP +char wtmpf[] = _PATH_WTMP; +#else char wtmpf[] = "/usr/adm/wtmp"; +#endif +#ifdef _PATH_UTMP +char utmpf[] = _PATH_UTMP; +#else char utmpf[] = "/etc/utmp"; +#endif # else /* PARENT_DOES_UTMP */ char wtmpf[] = "/etc/wtmp"; # endif /* PARENT_DOES_UTMP */ @@ -69,16 +80,21 @@ char wtmpf[] = "/etc/wtmp"; # ifdef CRAY #include <tmpdir.h> #include <sys/wait.h> -# if (UNICOS_LVL == '7.0') || (UNICOS_LVL == '7.1') -# define UNICOS7x +# if defined(_SC_CRAY_SECURE_SYS) && !defined(SCM_SECURITY) + /* + * UNICOS 6.0/6.1 do not have SCM_SECURITY defined, so we can + * use it to tell us to turn off all the socket security code, + * since that is only used in UNICOS 7.0 and later. + */ +# undef _SC_CRAY_SECURE_SYS # endif -# ifdef UNICOS7x +# if defined(_SC_CRAY_SECURE_SYS) #include <sys/sysv.h> #include <sys/secstat.h> extern int secflag; extern struct sysv sysv; -# endif /* UNICOS7x */ +# endif /* _SC_CRAY_SECURE_SYS */ # endif /* CRAY */ #endif /* NEWINIT */ @@ -210,7 +226,7 @@ copy_termbuf(cp, len) { if (len > sizeof(termbuf)) len = sizeof(termbuf); - memmove((char *)&termbuf, cp, len); + bcopy(cp, (char *)&termbuf, len); termbuf2 = termbuf; } #endif /* defined(LINEMODE) && defined(TIOCPKT_IOCTL) */ @@ -222,19 +238,17 @@ set_termbuf() * Only make the necessary changes. */ #ifndef USE_TERMIO - if (memcmp((char *)&termbuf.sg, (char *)&termbuf2.sg, - sizeof(termbuf.sg))) + if (bcmp((char *)&termbuf.sg, (char *)&termbuf2.sg, sizeof(termbuf.sg))) (void) ioctl(pty, TIOCSETN, (char *)&termbuf.sg); - if (memcmp((char *)&termbuf.tc, (char *)&termbuf2.tc, - sizeof(termbuf.tc))) + if (bcmp((char *)&termbuf.tc, (char *)&termbuf2.tc, sizeof(termbuf.tc))) (void) ioctl(pty, TIOCSETC, (char *)&termbuf.tc); - if (memcmp((char *)&termbuf.ltc, (char *)&termbuf2.ltc, + if (bcmp((char *)&termbuf.ltc, (char *)&termbuf2.ltc, sizeof(termbuf.ltc))) (void) ioctl(pty, TIOCSLTC, (char *)&termbuf.ltc); if (termbuf.lflags != termbuf2.lflags) (void) ioctl(pty, TIOCLSET, (char *)&termbuf.lflags); #else /* USE_TERMIO */ - if (memcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf))) + if (bcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf))) # ifdef STREAMSPTY (void) tcsetattr(ttyfd, TCSANOW, &termbuf); # else @@ -504,7 +518,7 @@ int *ptynum; p2 = &line[14]; #endif - for (cp = "pqrstuvwxyzPQRST"; *cp; cp++) { + for (cp = "pqrsPQRS"; *cp; cp++) { struct stat stb; *p1 = *cp; @@ -516,8 +530,8 @@ int *ptynum; */ if (stat(line, &stb) < 0) break; - for (i = 0; i < 16; i++) { - *p2 = "0123456789abcdef"[i]; + for (i = 0; i < 32; i++) { + *p2 = "0123456789abcdefghijklmnopqrstuv"[i]; p = open(line, 2); if (p > 0) { #ifndef __hpux @@ -947,7 +961,6 @@ tty_iscrnl() #endif #ifdef DECODE_BAUD - /* * A table of available terminal speeds */ @@ -955,39 +968,14 @@ struct termspeeds { int speed; int value; } termspeeds[] = { - { 0, B0 }, { 50, B50 }, { 75, B75 }, - { 110, B110 }, { 134, B134 }, { 150, B150 }, - { 200, B200 }, { 300, B300 }, { 600, B600 }, - { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 }, - { 4800, B4800 }, -#ifdef B7200 - { 7200, B7200 }, -#endif - { 9600, B9600 }, -#ifdef B14400 - { 14400, B14400 }, -#endif -#ifdef B19200 - { 19200, B19200 }, -#endif -#ifdef B28800 - { 28800, B28800 }, -#endif -#ifdef B38400 - { 38400, B38400 }, -#endif -#ifdef B57600 - { 57600, B57600 }, -#endif -#ifdef B115200 - { 115200, B115200 }, -#endif -#ifdef B230400 - { 230400, B230400 }, -#endif - { -1, 0 } + { 0, B0 }, { 50, B50 }, { 75, B75 }, + { 110, B110 }, { 134, B134 }, { 150, B150 }, + { 200, B200 }, { 300, B300 }, { 600, B600 }, + { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 }, + { 4800, B4800 }, { 9600, B9600 }, { 19200, B9600 }, + { 38400, B9600 }, { -1, B9600 } }; -#endif /* DECODE_BUAD */ +#endif /* DECODE_BAUD */ void tty_tspeed(val) @@ -998,12 +986,10 @@ tty_tspeed(val) for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++) ; - if (tp->speed == -1) /* back up to last valid value */ - --tp; cfsetospeed(&termbuf, tp->value); -#else /* DECODE_BUAD */ +#else /* DECODE_BAUD */ cfsetospeed(&termbuf, val); -#endif /* DECODE_BUAD */ +#endif /* DECODE_BAUD */ } void @@ -1015,8 +1001,6 @@ tty_rspeed(val) for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++) ; - if (tp->speed == -1) /* back up to last valid value */ - --tp; cfsetispeed(&termbuf, tp->value); #else /* DECODE_BAUD */ cfsetispeed(&termbuf, val); @@ -1069,6 +1053,7 @@ extern void utmp_sig_notify P((int)); getptyslave() { register int t = -1; + char erase; #if !defined(CRAY) || !defined(NEWINIT) # ifdef LINEMODE @@ -1085,12 +1070,13 @@ getptyslave() * if linemode was turned on * terminal window size * terminal speed + * erase character * so that we can re-set them if we need to. */ # ifdef LINEMODE waslm = tty_linemode(); # endif - + erase = termbuf.c_cc[VERASE]; /* * Make sure that we don't have a controlling tty, and @@ -1136,7 +1122,7 @@ getptyslave() init_termbuf(); # ifdef TIOCGWINSZ if (def_row || def_col) { - memset((char *)&ws, 0, sizeof(ws)); + bzero((char *)&ws, sizeof(ws)); ws.ws_col = def_col; ws.ws_row = def_row; (void)ioctl(t, TIOCSWINSZ, (char *)&ws); @@ -1176,6 +1162,8 @@ getptyslave() # endif /* defined(USE_TERMIO) && !defined(CRAY) && (BSD <= 43) */ tty_rspeed((def_rspeed > 0) ? def_rspeed : 9600); tty_tspeed((def_tspeed > 0) ? def_tspeed : 9600); + if (erase) + termbuf.c_cc[VERASE] = erase; # ifdef LINEMODE if (waslm) tty_setlinemode(1); @@ -1216,9 +1204,9 @@ cleanopen(line) char *line; { register int t; -#ifdef UNICOS7x +#if defined(_SC_CRAY_SECURE_SYS) struct secstat secbuf; -#endif /* UNICOS7x */ +#endif /* _SC_CRAY_SECURE_SYS */ #ifndef STREAMSPTY /* @@ -1232,7 +1220,7 @@ cleanopen(line) # if !defined(CRAY) && (BSD > 43) (void) revoke(line); # endif -#ifdef UNICOS7x +#if defined(_SC_CRAY_SECURE_SYS) if (secflag) { if (secstat(line, &secbuf) < 0) return(-1); @@ -1241,18 +1229,18 @@ cleanopen(line) if (setucmp(secbuf.st_compart) < 0) return(-1); } -#endif /* UNICOS7x */ +#endif /* _SC_CRAY_SECURE_SYS */ t = open(line, O_RDWR|O_NOCTTY); -#ifdef UNICOS7x +#if defined(_SC_CRAY_SECURE_SYS) if (secflag) { if (setulvl(sysv.sy_minlvl) < 0) return(-1); if (setucmp(0) < 0) return(-1); } -#endif /* UNICOS7x */ +#endif /* _SC_CRAY_SECURE_SYS */ if (t < 0) return(-1); @@ -1275,8 +1263,9 @@ cleanopen(line) (void) signal(SIGHUP, SIG_IGN); (void) ioctl(t, TCVHUP, (char *)0); (void) signal(SIGHUP, SIG_DFL); + setpgrp(); -#ifdef UNICOS7x +#if defined(_SC_CRAY_SECURE_SYS) if (secflag) { if (secstat(line, &secbuf) < 0) return(-1); @@ -1285,18 +1274,18 @@ cleanopen(line) if (setucmp(secbuf.st_compart) < 0) return(-1); } -#endif /* UNICOS7x */ +#endif /* _SC_CRAY_SECURE_SYS */ i = open(line, O_RDWR); -#ifdef UNICOS7x +#if defined(_SC_CRAY_SECURE_SYS) if (secflag) { if (setulvl(sysv.sy_minlvl) < 0) return(-1); if (setucmp(0) < 0) return(-1); } -#endif /* UNICOS7x */ +#endif /* _SC_CRAY_SECURE_SYS */ if (i < 0) return(-1); @@ -1345,11 +1334,7 @@ login_tty(t) * setsid() call above may have set our pgrp, so clear * it out before opening the tty... */ -# ifndef SOLARIS (void) setpgrp(0, 0); -# else - (void) setpgrp(); -# endif close(open(line, O_RDWR)); # endif if (t != 0) @@ -1544,7 +1529,7 @@ start_login(host, autologin, name) { register char *cp; register char **argv; - char **addarg(); + char **addarg(), *user; extern char *getenv(); #ifdef UTMPX register int pid = getpid(); @@ -1560,7 +1545,7 @@ start_login(host, autologin, name) * Create utmp entry for child */ - memset(&utmpx, 0, sizeof(utmpx)); + bzero(&utmpx, sizeof(utmpx)); SCPYN(utmpx.ut_user, ".telnet"); SCPYN(utmpx.ut_line, line + sizeof("/dev/") - 1); utmpx.ut_pid = pid; @@ -1570,10 +1555,12 @@ start_login(host, autologin, name) utmpx.ut_id[3] = SC_WILDC; utmpx.ut_type = LOGIN_PROCESS; (void) time(&utmpx.ut_tv.tv_sec); - if (pututxline(&utmpx) == NULL) - fatal(net, "pututxline failed"); + if (makeutx(&utmpx) == NULL) + fatal(net, "makeutx failed"); #endif + scrub_env(); + /* * -h : pass on name of host. * WARNING: -h is accepted by login if and only if @@ -1615,19 +1602,6 @@ start_login(host, autologin, name) #if !defined(NO_LOGIN_P) argv = addarg(argv, "-p"); #endif -#ifdef LINEMODE - /* - * Set the environment variable "LINEMODE" to either - * "real" or "kludge" if we are operating in either - * real or kludge linemode. - */ - if (lmodetype == REAL_LINEMODE) - setenv("LINEMODE", "real", 1); -# ifdef KLUDGELINEMODE - else if (lmodetype == KLUDGE_LINEMODE || lmodetype == KLUDGE_OK) - setenv("LINEMODE", "kludge", 1); -# endif -#endif #ifdef BFTPDAEMON /* * Are we working as the bftp daemon? If so, then ask login @@ -1650,6 +1624,7 @@ start_login(host, autologin, name) if (auth_level >= 0 && autologin == AUTH_VALID) { # if !defined(NO_LOGIN_F) argv = addarg(argv, "-f"); + argv = addarg(argv, "--"); argv = addarg(argv, name); # else # if defined(LOGIN_R) @@ -1722,12 +1697,14 @@ start_login(host, autologin, name) pty = xpty; } # else + argv = addarg(argv, "--"); argv = addarg(argv, name); # endif # endif } else #endif if (getenv("USER")) { + argv = addarg(argv, "--"); argv = addarg(argv, getenv("USER")); #if defined(LOGIN_ARGS) && defined(NO_LOGIN_P) { @@ -1748,31 +1725,19 @@ start_login(host, autologin, name) */ unsetenv("USER"); } -#ifdef SOLARIS - else { - char **p; - - argv = addarg(argv, ""); /* no login name */ - for (p = environ; *p; p++) { - argv = addarg(argv, *p); - } - } -#endif /* SOLARIS */ #if defined(AUTHENTICATION) && defined(NO_LOGIN_F) && defined(LOGIN_R) if (pty > 2) close(pty); #endif closelog(); - /* - * This sleep(1) is in here so that telnetd can - * finish up with the tty. There's a race condition - * the login banner message gets lost... - */ - sleep(1); - execv(_PATH_LOGIN, argv); - syslog(LOG_ERR, "%s: %m\n", _PATH_LOGIN); - fatalperror(net, _PATH_LOGIN); + if (altlogin == NULL) { + altlogin = _PATH_LOGIN; + } + execv(altlogin, argv); + + syslog(LOG_ERR, "%s: %m\n", altlogin); + fatalperror(net, altlogin); /*NOTREACHED*/ } @@ -1798,7 +1763,7 @@ addarg(argv, val) if (cpp == &argv[(int)argv[-1]]) { --argv; *argv = (char *)((int)(*argv) + 10); - argv = (char **)realloc(argv, sizeof(*argv)*((int)(*argv) + 2)); + argv = (char **)realloc(argv, (int)(*argv) + 2); if (argv == NULL) return(NULL); argv++; @@ -1811,6 +1776,31 @@ addarg(argv, val) #endif /* NEWINIT */ /* + * scrub_env() + * + * Remove a few things from the environment that + * don't need to be there. + */ +scrub_env() +{ + register char **cpp, **cpp2; + + for (cpp2 = cpp = environ; *cpp; cpp++) { +#ifdef __FreeBSD__ + if (strncmp(*cpp, "LD_LIBRARY_PATH=", 16) && + strncmp(*cpp, "LD_PRELOAD=", 11) && +#else + if (strncmp(*cpp, "LD_", 3) && + strncmp(*cpp, "_RLD_", 5) && + strncmp(*cpp, "LIBPATH=", 8) && +#endif + strncmp(*cpp, "IFS=", 4)) + *cpp2++ = *cpp; + } + *cpp2 = 0; +} + +/* * cleanup() * * This is the routine to call when we are all through, to @@ -1851,8 +1841,6 @@ cleanup(sig) # ifdef CRAY static int incleanup = 0; register int t; - int child_status; /* status of child process as returned by waitpid */ - int flags = WNOHANG|WUNTRACED; /* * 1: Pick up the zombie, if we are being called @@ -1863,17 +1851,9 @@ cleanup(sig) * 5: Close down the network and pty connections. * 6: Finish up the TMPDIR cleanup, if needed. */ - if (sig == SIGCHLD) { - while (waitpid(-1, &child_status, flags) > 0) + if (sig == SIGCHLD) + while (waitpid(-1, 0, WNOHANG) > 0) ; /* VOID */ - /* Check if the child process was stopped - * rather than exited. We want cleanup only if - * the child has died. - */ - if (WIFSTOPPED(child_status)) { - return; - } - } t = sigblock(sigmask(SIGCHLD)); if (incleanup) { sigsetmask(t); @@ -1881,7 +1861,6 @@ cleanup(sig) } incleanup = 1; sigsetmask(t); -#ifdef UNICOS7x if (secflag) { /* * We need to set ourselves back to a null @@ -1891,7 +1870,6 @@ cleanup(sig) setulvl(sysv.sy_minlvl); setucmp((long)0); } -#endif /* UNICOS7x */ t = cleantmp(&wtmp); setutent(); /* just to make sure */ @@ -1992,28 +1970,6 @@ sigjob(sig) } /* - * jid_getutid: - * called by jobend() before calling cleantmp() - * to find the correct $TMPDIR to cleanup. - */ - - struct utmp * -jid_getutid(jid) - int jid; -{ - struct utmp *cur = NULL; - - setutent(); /* just to make sure */ - while (cur = getutent()) { - if ( (cur->ut_type != NULL) && (jid == cur->ut_jid) ) { - return(cur); - } - } - - return(0); -} - -/* * Clean up the TMPDIR that login created. * The first time this is called we pick up the info * from the utmp. If the job has already gone away, @@ -2069,27 +2025,9 @@ jobend(jid, path, user) register char *user; { static int saved_jid = 0; - static int pty_saved_jid = 0; static char saved_path[sizeof(wtmp.ut_tpath)+1]; static char saved_user[sizeof(wtmp.ut_user)+1]; - /* - * this little piece of code comes into play - * only when ptyreconnect is used to reconnect - * to an previous session. - * - * this is the only time when the - * "saved_jid != jid" code is executed. - */ - - if ( saved_jid && saved_jid != jid ) { - if (!path) { /* called from signal handler */ - pty_saved_jid = jid; - } else { - pty_saved_jid = saved_jid; - } - } - if (path) { strncpy(saved_path, path, sizeof(wtmp.ut_tpath)); strncpy(saved_user, user, sizeof(wtmp.ut_user)); @@ -2100,24 +2038,6 @@ jobend(jid, path, user) saved_jid = jid; return(0); } - - /* if the jid has changed, get the correct entry from the utmp file */ - - if ( saved_jid != jid ) { - struct utmp *utp = NULL; - struct utmp *jid_getutid(); - - utp = jid_getutid(pty_saved_jid); - - if (utp == 0) { - syslog(LOG_ERR, "Can't get /etc/utmp entry to clean TMPDIR"); - return(-1); - } - - cleantmpdir(jid, utp->ut_tpath, utp->ut_user); - return(1); - } - cleantmpdir(jid, saved_path, saved_user); return(1); } diff --git a/libexec/telnetd/telnetd.8 b/libexec/telnetd/telnetd.8 index f618385..cb3c743 100644 --- a/libexec/telnetd/telnetd.8 +++ b/libexec/telnetd/telnetd.8 @@ -29,9 +29,10 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)telnetd.8 8.4 (Berkeley) 6/1/94 +.\" @(#)telnetd.8 8.3 (Berkeley) 3/1/94 +.\" $Id$ .\" -.Dd June 1, 1994 +.Dd March 1, 1994 .Dt TELNETD 8 .Os BSD 4.2 .Sh NAME @@ -48,6 +49,7 @@ protocol server .Op Fl X Ar authtype .Op Fl a Ar authmode .Op Fl edebug +.Op Fl P Ar loginprog .Op Fl r Ns Ar lowpty-highpty .Op Fl u Ar len .Op Fl debug Op Ar port @@ -187,6 +189,11 @@ If has been compiled with support for data encryption, then the .Fl edebug option may be used to enable encryption debugging code. +.It Fl P Ar loginprog +Specifies an alternate +.Xr login 1 +command to run to complete the login. The alternate command must +understand the same command arguments as the standard login. .It Fl h Disables the printing of host-specific information before login has been completed. @@ -217,7 +224,7 @@ will operate in character at a time mode. It will still support kludge linemode, but will only go into kludge linemode if the remote client requests it. -(This is done by by the client sending +(This is done by the client sending .Dv DONT SUPPRESS-GO-AHEAD and .Dv DONT ECHO . ) @@ -308,7 +315,6 @@ indicates that only dotted decimal addresses should be put into the .Pa utmp file. -.ne 1i .It Fl U This option causes .Nm telnetd @@ -425,7 +431,6 @@ Whenever a command is received, it is always responded to with a .Dv WILL TIMING-MARK -.ne 1i .It "WILL LOGOUT" When a .Dv DO LOGOUT @@ -481,7 +486,7 @@ about the speed of the serial line to which the client is attached. .It "DO XDISPLOC" Indicates a desire to be able to request the name -of the X windows display that is associated with +of the X Window System display that is associated with the telnet client. .It "DO NEW-ENVIRON" Indicates a desire to be able to request environment @@ -532,9 +537,9 @@ the data stream. .Pa /usr/ucb/bftp (if supported) .Sh "SEE ALSO" -.Xr telnet 1 , +.Xr bftp 1 , .Xr login 1 , -.Xr bftp 1 +.Xr telnet 1 (if supported) .Sh STANDARDS .Bl -tag -compact -width RFC-1572 diff --git a/libexec/telnetd/telnetd.c b/libexec/telnetd/telnetd.c index 70c0fc0..8aafdbd 100644 --- a/libexec/telnetd/telnetd.c +++ b/libexec/telnetd/telnetd.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id: telnetd.c,v 1.10 1997/02/22 14:22:31 peter Exp $ */ #ifndef lint @@ -38,7 +40,7 @@ static char copyright[] = #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)telnetd.c 8.4 (Berkeley) 5/30/95"; +static char sccsid[] = "@(#)telnetd.c 8.2 (Berkeley) 12/15/93"; #endif /* not lint */ #include "telnetd.h" @@ -124,6 +126,7 @@ int lowpty = 0, highpty; /* low, high pty numbers */ int debug = 0; int keepalive = 1; char *progname; +char *altlogin; extern void usage P((void)); @@ -133,7 +136,7 @@ extern void usage P((void)); * passed off to getopt(). */ char valid_opts[] = { - 'd', ':', 'h', 'k', 'n', 'S', ':', 'u', ':', 'U', + 'd', ':', 'h', 'k', 'n', 'p', ':', 'S', ':', 'u', ':', 'U', #ifdef AUTHENTICATION 'a', ':', 'X', ':', #endif @@ -143,9 +146,6 @@ char valid_opts[] = { #ifdef DIAGNOSTICS 'D', ':', #endif -#ifdef ENCRYPTION - 'e', ':', -#endif #if defined(CRAY) && defined(NEWINIT) 'I', ':', #endif @@ -176,9 +176,6 @@ main(argc, argv) pfrontp = pbackp = ptyobuf; netip = netibuf; nfrontp = nbackp = netobuf; -#ifdef ENCRYPTION - nclearto = 0; -#endif /* ENCRYPTION */ progname = *argv; @@ -190,7 +187,7 @@ main(argc, argv) highpty = getnpty(); #endif /* CRAY */ - while ((ch = getopt(argc, argv, valid_opts)) != EOF) { + while ((ch = getopt(argc, argv, valid_opts)) != -1) { switch(ch) { #ifdef AUTHENTICATION @@ -258,17 +255,6 @@ main(argc, argv) break; #endif /* DIAGNOSTICS */ -#ifdef ENCRYPTION - case 'e': - if (strcmp(optarg, "debug") == 0) { - extern int encrypt_debug_mode; - encrypt_debug_mode = 1; - break; - } - usage(); - /* NOTREACHED */ - break; -#endif /* ENCRYPTION */ case 'h': hostinfo = 0; @@ -301,6 +287,10 @@ main(argc, argv) keepalive = 0; break; + case 'p': + altlogin = optarg; + break; + #ifdef CRAY case 'r': { @@ -451,7 +441,7 @@ main(argc, argv) int szi = sizeof(int); #endif /* SO_SEC_MULTI */ - memset((char *)&dv, 0, sizeof(dv)); + bzero((char *)&dv, sizeof(dv)); if (getsysv(&sysv, sizeof(struct sysv)) != 0) { perror("getsysv"); @@ -605,18 +595,12 @@ getterminaltype(name) } #endif -#ifdef ENCRYPTION - send_will(TELOPT_ENCRYPT, 1); -#endif /* ENCRYPTION */ send_do(TELOPT_TTYPE, 1); send_do(TELOPT_TSPEED, 1); send_do(TELOPT_XDISPLOC, 1); send_do(TELOPT_NEW_ENVIRON, 1); send_do(TELOPT_OLD_ENVIRON, 1); while ( -#ifdef ENCRYPTION - his_do_dont_is_changing(TELOPT_ENCRYPT) || -#endif /* ENCRYPTION */ his_will_wont_is_changing(TELOPT_TTYPE) || his_will_wont_is_changing(TELOPT_TSPEED) || his_will_wont_is_changing(TELOPT_XDISPLOC) || @@ -624,53 +608,38 @@ getterminaltype(name) his_will_wont_is_changing(TELOPT_OLD_ENVIRON)) { ttloop(); } -#ifdef ENCRYPTION - /* - * Wait for the negotiation of what type of encryption we can - * send with. If autoencrypt is not set, this will just return. - */ - if (his_state_is_will(TELOPT_ENCRYPT)) { - encrypt_wait(); - } -#endif /* ENCRYPTION */ if (his_state_is_will(TELOPT_TSPEED)) { static unsigned char sb[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; - memmove(nfrontp, sb, sizeof sb); + bcopy(sb, nfrontp, sizeof sb); nfrontp += sizeof sb; - DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); } if (his_state_is_will(TELOPT_XDISPLOC)) { static unsigned char sb[] = { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE }; - memmove(nfrontp, sb, sizeof sb); + bcopy(sb, nfrontp, sizeof sb); nfrontp += sizeof sb; - DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); } if (his_state_is_will(TELOPT_NEW_ENVIRON)) { static unsigned char sb[] = { IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND, IAC, SE }; - memmove(nfrontp, sb, sizeof sb); + bcopy(sb, nfrontp, sizeof sb); nfrontp += sizeof sb; - DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); } else if (his_state_is_will(TELOPT_OLD_ENVIRON)) { static unsigned char sb[] = { IAC, SB, TELOPT_OLD_ENVIRON, TELQUAL_SEND, IAC, SE }; - memmove(nfrontp, sb, sizeof sb); + bcopy(sb, nfrontp, sizeof sb); nfrontp += sizeof sb; - DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); } if (his_state_is_will(TELOPT_TTYPE)) { - memmove(nfrontp, ttytype_sbbuf, sizeof ttytype_sbbuf); + bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); nfrontp += sizeof ttytype_sbbuf; - DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2, - sizeof ttytype_sbbuf - 2);); } if (his_state_is_will(TELOPT_TSPEED)) { while (sequenceIs(tspeedsubopt, baseline)) @@ -699,12 +668,14 @@ getterminaltype(name) * we have to just go with what we (might) have already gotten. */ if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) { - (void) strncpy(first, terminaltype, sizeof(first)); + (void) strncpy(first, terminaltype, sizeof(first)-1); + first[sizeof(first)-1] = '\0'; for(;;) { /* * Save the unknown name, and request the next name. */ - (void) strncpy(last, terminaltype, sizeof(last)); + (void) strncpy(last, terminaltype, sizeof(last)-1); + last[sizeof(last)-1] = '\0'; _gettermname(); if (terminaltypeok(terminaltype)) break; @@ -722,8 +693,10 @@ getterminaltype(name) * the start of the list. */ _gettermname(); - if (strncmp(first, terminaltype, sizeof(first)) != 0) - (void) strncpy(terminaltype, first, sizeof(first)); + if (strncmp(first, terminaltype, sizeof(first)) != 0) { + (void) strncpy(terminaltype, first, sizeof(terminaltype)-1); + terminaltype[sizeof(terminaltype)-1] = '\0'; + } break; } } @@ -743,10 +716,8 @@ _gettermname() if (his_state_is_wont(TELOPT_TTYPE)) return; settimer(baseline); - memmove(nfrontp, ttytype_sbbuf, sizeof ttytype_sbbuf); + bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); nfrontp += sizeof ttytype_sbbuf; - DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2, - sizeof ttytype_sbbuf - 2);); while (sequenceIs(ttypesubopt, baseline)) ttloop(); } @@ -780,12 +751,10 @@ char *hostname; char host_name[MAXHOSTNAMELEN]; char remote_host_name[MAXHOSTNAMELEN]; -#ifndef convex -extern void telnet P((int, int)); -#else extern void telnet P((int, int, char *)); -#endif +int level; +char user_name[256]; /* * Get a pty, scan input lines. */ @@ -795,9 +764,7 @@ doit(who) char *host, *inet_ntoa(); int t; struct hostent *hp; - int level; int ptynum; - char user_name[256]; /* * Find an available pty to use. @@ -844,10 +811,9 @@ doit(who) if (hp == NULL && registerd_host_only) { fatal(net, "Couldn't resolve your address into a host name.\r\n\ - Please contact your net administrator"); + Please contact your net administrator"); } else if (hp && - (strlen(hp->h_name) <= (unsigned int)((utmp_len < 0) ? -utmp_len - : utmp_len))) { + (strlen(hp->h_name) <= ((utmp_len < 0) ? -utmp_len : utmp_len))) { host = hp->h_name; } else { host = inet_ntoa(who->sin_addr); @@ -863,7 +829,7 @@ doit(who) (void) gethostname(host_name, sizeof (host_name)); hostname = host_name; -#if defined(AUTHENTICATION) || defined(ENCRYPTION) +#if defined(AUTHENTICATION) auth_encrypt_init(hostname, host, "TELNETD", 1); #endif @@ -875,12 +841,6 @@ doit(who) level = getterminaltype(user_name); setenv("TERM", terminaltype ? terminaltype : "network", 1); - /* - * Start up the login process on the slave side of the terminal - */ -#ifndef convex - startslave(host, level, user_name); - #if defined(_SC_CRAY_SECURE_SYS) if (secflag) { if (setulvl(dv.dv_actlvl) < 0) @@ -890,10 +850,8 @@ doit(who) } #endif /* _SC_CRAY_SECURE_SYS */ - telnet(net, pty); /* begin server processing */ -#else - telnet(net, pty, host); -#endif + telnet(net, pty, host); /* begin server process */ + /*NOTREACHED*/ } /* end of doit */ @@ -917,15 +875,9 @@ Xterm_output(ibufp, obuf, icountp, ocount) * hand data to telnet receiver finite state machine. */ void -#ifndef convex -telnet(f, p) -#else telnet(f, p, host) -#endif int f, p; -#ifdef convex char *host; -#endif { int on = 1; #define TABBUFSIZ 512 @@ -936,7 +888,6 @@ telnet(f, p, host) char *HN; char *IM; void netflush(); - int nfd; /* * Initialize the slc mapping table. @@ -1162,11 +1113,13 @@ telnet(f, p, host) {sprintf(nfrontp, "td: Entering processing loop\r\n"); nfrontp += strlen(nfrontp);}); -#ifdef convex - startslave(host); -#endif + /* + * Startup the login process on the slave side of the terminal + * now. We delay this until here to insure option negotiation + * is complete. + */ + startslave(host, level, user_name); - nfd = ((f > p) ? f : p) + 1; for (;;) { fd_set ibits, obits, xbits; register int c; @@ -1198,7 +1151,7 @@ telnet(f, p, host) if (!SYNCHing) { FD_SET(f, &xbits); } - if ((c = select(nfd, &ibits, &obits, &xbits, + if ((c = select(16, &ibits, &obits, &xbits, (struct timeval *)0)) < 1) { if (c == -1) { if (errno == EINTR) { @@ -1337,9 +1290,6 @@ telnet(f, p, host) *nfrontp++ = IAC; *nfrontp++ = DM; neturg = nfrontp-1; /* off by one XXX */ - DIAG(TD_OPTIONS, - printoption("td: send IAC", DM)); - #endif } if (his_state_is_will(TELOPT_LFLOW) && @@ -1356,9 +1306,6 @@ telnet(f, p, host) : LFLOW_OFF, IAC, SE); nfrontp += 6; - DIAG(TD_OPTIONS, printsub('>', - (unsigned char *)nfrontp-4, - 4);); } } pcc--; @@ -1524,14 +1471,6 @@ interrupt() { ptyflush(); /* half-hearted */ -#if defined(STREAMSPTY) && defined(TIOCSIGNAL) - /* Streams PTY style ioctl to post a signal */ - { - int sig = SIGINT; - (void) ioctl(pty, TIOCSIGNAL, &sig); - (void) ioctl(pty, I_FLUSH, FLUSHR); - } -#else #ifdef TCSIG (void) ioctl(pty, TCSIG, (char *)SIGINT); #else /* TCSIG */ @@ -1539,7 +1478,6 @@ interrupt() *pfrontp++ = slctab[SLC_IP].sptr ? (unsigned char)*slctab[SLC_IP].sptr : '\177'; #endif /* TCSIG */ -#endif } /* diff --git a/libexec/telnetd/termstat.c b/libexec/telnetd/termstat.c index ebc843a..9cb2b63 100644 --- a/libexec/telnetd/termstat.c +++ b/libexec/telnetd/termstat.c @@ -29,10 +29,12 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id$ */ #ifndef lint -static char sccsid[] = "@(#)termstat.c 8.2 (Berkeley) 5/30/95"; +static char sccsid[] = "@(#)termstat.c 8.1 (Berkeley) 6/4/93"; #endif /* not lint */ #include "telnetd.h" @@ -144,25 +146,6 @@ localstat() #endif /* defined(CRAY2) && defined(UNICOS5) */ /* - * Check for state of BINARY options. - */ - if (tty_isbinaryin()) { - if (his_want_state_is_wont(TELOPT_BINARY)) - send_do(TELOPT_BINARY, 1); - } else { - if (his_want_state_is_will(TELOPT_BINARY)) - send_dont(TELOPT_BINARY, 1); - } - - if (tty_isbinaryout()) { - if (my_want_state_is_wont(TELOPT_BINARY)) - send_will(TELOPT_BINARY, 1); - } else { - if (my_want_state_is_will(TELOPT_BINARY)) - send_wont(TELOPT_BINARY, 1); - } - - /* * Check for changes to flow control if client supports it. */ flowstat(); @@ -181,25 +164,34 @@ localstat() tty_setlinemode(uselinemode); } -#ifdef ENCRYPTION - /* - * If the terminal is not echoing, but editing is enabled, - * something like password input is going to happen, so - * if we the other side is not currently sending encrypted - * data, ask the other side to start encrypting. - */ - if (his_state_is_will(TELOPT_ENCRYPT)) { - static int enc_passwd = 0; - if (uselinemode && !tty_isecho() && tty_isediting() - && (enc_passwd == 0) && !decrypt_input) { - encrypt_send_request_start(); - enc_passwd = 1; - } else if (enc_passwd) { - encrypt_send_request_end(); - enc_passwd = 0; - } - } -#endif /* ENCRYPTION */ + if (uselinemode) { + + /* + * Check for state of BINARY options. + * + * We only need to do the binary dance if we are actually going + * to use linemode. As this confuses some telnet clients that dont + * support linemode, and doesnt gain us anything, we dont do it + * unless we're doing linemode. -Crh (henrich@msu.edu) + */ + + if (tty_isbinaryin()) { + if (his_want_state_is_wont(TELOPT_BINARY)) + send_do(TELOPT_BINARY, 1); + } else { + if (his_want_state_is_will(TELOPT_BINARY)) + send_dont(TELOPT_BINARY, 1); + } + + if (tty_isbinaryout()) { + if (my_want_state_is_wont(TELOPT_BINARY)) + send_will(TELOPT_BINARY, 1); + } else { + if (my_want_state_is_will(TELOPT_BINARY)) + send_wont(TELOPT_BINARY, 1); + } + + } /* * Do echo mode handling as soon as we know what the @@ -632,7 +624,7 @@ defer_terminit() if (def_col || def_row) { struct winsize ws; - memset((char *)&ws, 0, sizeof(ws)); + bzero((char *)&ws, sizeof(ws)); ws.ws_col = def_col; ws.ws_row = def_row; (void) ioctl(pty, TIOCSWINSZ, (char *)&ws); diff --git a/libexec/telnetd/utility.c b/libexec/telnetd/utility.c index 9553d58..2ee0d21 100644 --- a/libexec/telnetd/utility.c +++ b/libexec/telnetd/utility.c @@ -29,12 +29,18 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id$ */ #ifndef lint -static char sccsid[] = "@(#)utility.c 8.4 (Berkeley) 5/30/95"; +static char sccsid[] = "@(#)utility.c 8.2 (Berkeley) 12/15/93"; #endif /* not lint */ +#ifdef __FreeBSD__ +#include <locale.h> +#include <sys/utsname.h> +#endif #define PRINTOPTIONS #include "telnetd.h" @@ -93,6 +99,7 @@ stilloob(s) do { FD_ZERO(&excepts); FD_SET(s, &excepts); + memset((char *)&timeout, 0, sizeof timeout); value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); } while ((value == -1) && (errno == EINTR)); @@ -193,11 +200,7 @@ netclear() #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) -#ifdef ENCRYPTION - thisitem = nclearto > netobuf ? nclearto : netobuf; -#else /* ENCRYPTION */ thisitem = netobuf; -#endif /* ENCRYPTION */ while ((next = nextitem(thisitem)) <= nbackp) { thisitem = next; @@ -205,11 +208,7 @@ netclear() /* Now, thisitem is first before/at boundary. */ -#ifdef ENCRYPTION - good = nclearto > netobuf ? nclearto : netobuf; -#else /* ENCRYPTION */ good = netobuf; /* where the good bytes go */ -#endif /* ENCRYPTION */ while (nfrontp > thisitem) { if (wewant(thisitem)) { @@ -220,7 +219,7 @@ netclear() next = nextitem(next); } while (wewant(next) && (nfrontp > next)); length = next-thisitem; - memmove(good, thisitem, length); + bcopy(thisitem, good, length); good += length; thisitem = next; } else { @@ -250,15 +249,6 @@ netflush() n += strlen(nfrontp); /* get count first */ nfrontp += strlen(nfrontp); /* then move pointer */ }); -#ifdef ENCRYPTION - if (encrypt_output) { - char *s = nclearto ? nclearto : nbackp; - if (nfrontp - s > 0) { - (*encrypt_output)((unsigned char *)s, nfrontp-s); - nclearto = nfrontp; - } - } -#endif /* ENCRYPTION */ /* * if no urgent data, or if the other side appears to be an * old 4.2 client (and thus unable to survive TCP urgent data), @@ -289,18 +279,11 @@ netflush() cleanup(0); } nbackp += n; -#ifdef ENCRYPTION - if (nbackp > nclearto) - nclearto = 0; -#endif /* ENCRYPTION */ if (nbackp >= neturg) { neturg = 0; } if (nbackp == nfrontp) { nbackp = nfrontp = netobuf; -#ifdef ENCRYPTION - nclearto = 0; -#endif /* ENCRYPTION */ } return; } /* end of netflush */ @@ -327,7 +310,7 @@ writenet(ptr, len) netflush(); } - memmove(nfrontp, ptr, len); + bcopy(ptr, nfrontp, len); nfrontp += len; } /* end of writenet */ @@ -346,16 +329,6 @@ fatal(f, msg) char buf[BUFSIZ]; (void) sprintf(buf, "telnetd: %s.\r\n", msg); -#ifdef ENCRYPTION - if (encrypt_output) { - /* - * Better turn off encryption first.... - * Hope it flushes... - */ - encrypt_send_end(); - netflush(); - } -#endif /* ENCRYPTION */ (void) write(f, buf, (int)strlen(buf)); sleep(1); /*XXX*/ exit(1); @@ -368,7 +341,7 @@ fatalperror(f, msg) { char buf[BUFSIZ], *strerror(); - (void) sprintf(buf, "%s: %s", msg, strerror(errno)); + (void) sprintf(buf, "%s: %s\r\n", msg, strerror(errno)); fatal(f, buf); } @@ -433,12 +406,16 @@ putchr(cc) *putlocation++ = cc; } +#ifdef __FreeBSD__ +static char fmtstr[] = { "%+" }; +#else /* * This is split on two lines so that SCCS will not see the M * between two % signs and expand it... */ static char fmtstr[] = { "%l:%M\ %P on %A, %d %B %Y" }; +#endif void putf(cp, where) @@ -449,9 +426,15 @@ putf(cp, where) time_t t; char db[100]; #ifdef STREAMSPTY - extern char *strchr(); + extern char *index(); #else - extern char *strrchr(); + extern char *rindex(); +#endif +#ifdef __FreeBSD__ + static struct utsname kerninfo; + + if (!*kerninfo.sysname) + uname(&kerninfo); #endif putlocation = where; @@ -466,9 +449,9 @@ putf(cp, where) case 't': #ifdef STREAMSPTY /* names are like /dev/pts/2 -- we want pts/2 */ - slash = strchr(line+1, '/'); + slash = index(line+1, '/'); #else - slash = strrchr(line, '/'); + slash = rindex(line, '/'); #endif if (slash == (char *) 0) putstr(line); @@ -481,11 +464,32 @@ putf(cp, where) break; case 'd': +#ifdef __FreeBSD__ + setlocale(LC_TIME, ""); +#endif (void)time(&t); (void)strftime(db, sizeof(db), fmtstr, localtime(&t)); putstr(db); break; +#ifdef __FreeBSD__ + case 's': + putstr(kerninfo.sysname); + break; + + case 'm': + putstr(kerninfo.machine); + break; + + case 'r': + putstr(kerninfo.release); + break; + + case 'v': + putstr(kerninfo.version); + break; +#endif + case '%': putchr('%'); break; @@ -522,7 +526,7 @@ printsub(direction, pointer, length) register int i; char buf[512]; - if (!(diagnostic & TD_OPTIONS)) + if (!(diagnostic & TD_OPTIONS)) return; if (direction) { @@ -1044,99 +1048,12 @@ printsub(direction, pointer, length) break; #endif -#ifdef ENCRYPTION - case TELOPT_ENCRYPT: - sprintf(nfrontp, "ENCRYPT"); - nfrontp += strlen(nfrontp); - if (length < 2) { - sprintf(nfrontp, " (empty suboption??\?)"); - nfrontp += strlen(nfrontp); - break; - } - switch (pointer[1]) { - case ENCRYPT_START: - sprintf(nfrontp, " START"); - nfrontp += strlen(nfrontp); - break; - - case ENCRYPT_END: - sprintf(nfrontp, " END"); - nfrontp += strlen(nfrontp); - break; - - case ENCRYPT_REQSTART: - sprintf(nfrontp, " REQUEST-START"); - nfrontp += strlen(nfrontp); - break; - - case ENCRYPT_REQEND: - sprintf(nfrontp, " REQUEST-END"); - nfrontp += strlen(nfrontp); - break; - - case ENCRYPT_IS: - case ENCRYPT_REPLY: - sprintf(nfrontp, " %s ", (pointer[1] == ENCRYPT_IS) ? - "IS" : "REPLY"); - nfrontp += strlen(nfrontp); - if (length < 3) { - sprintf(nfrontp, " (partial suboption??\?)"); - nfrontp += strlen(nfrontp); - break; - } - if (ENCTYPE_NAME_OK(pointer[2])) - sprintf(nfrontp, "%s ", ENCTYPE_NAME(pointer[2])); - else - sprintf(nfrontp, " %d (unknown)", pointer[2]); - nfrontp += strlen(nfrontp); - - encrypt_printsub(&pointer[1], length - 1, buf, sizeof(buf)); - sprintf(nfrontp, "%s", buf); - nfrontp += strlen(nfrontp); - break; - - case ENCRYPT_SUPPORT: - i = 2; - sprintf(nfrontp, " SUPPORT "); - nfrontp += strlen(nfrontp); - while (i < length) { - if (ENCTYPE_NAME_OK(pointer[i])) - sprintf(nfrontp, "%s ", ENCTYPE_NAME(pointer[i])); - else - sprintf(nfrontp, "%d ", pointer[i]); - nfrontp += strlen(nfrontp); - i++; - } - break; - - case ENCRYPT_ENC_KEYID: - sprintf(nfrontp, " ENC_KEYID", pointer[1]); - nfrontp += strlen(nfrontp); - goto encommon; - - case ENCRYPT_DEC_KEYID: - sprintf(nfrontp, " DEC_KEYID", pointer[1]); - nfrontp += strlen(nfrontp); - goto encommon; - - default: - sprintf(nfrontp, " %d (unknown)", pointer[1]); - nfrontp += strlen(nfrontp); - encommon: - for (i = 2; i < length; i++) { - sprintf(nfrontp, " %d", pointer[i]); - nfrontp += strlen(nfrontp); - } - break; - } - break; -#endif /* ENCRYPTION */ default: if (TELOPT_OK(pointer[0])) - sprintf(nfrontp, "%s (unknown)", TELOPT(pointer[0])); + sprintf(nfrontp, "%s (unknown)", TELOPT(pointer[0])); else - sprintf(nfrontp, "%d (unknown)", pointer[i]); + sprintf(nfrontp, "%d (unknown)", pointer[i]); nfrontp += strlen(nfrontp); for (i = 1; i < length; i++) { sprintf(nfrontp, " %d", pointer[i]); diff --git a/libexec/tftpd/Makefile b/libexec/tftpd/Makefile index f94acc0..b7c600c 100644 --- a/libexec/tftpd/Makefile +++ b/libexec/tftpd/Makefile @@ -1,8 +1,9 @@ # @(#)Makefile 8.1 (Berkeley) 6/4/93 +# $Id$ PROG= tftpd SRCS= tftpd.c tftpsubs.c -MAN8= tftpd.0 +MAN8= tftpd.8 CFLAGS+=-I${.CURDIR}/../../usr.bin/tftp .PATH: ${.CURDIR}/../../usr.bin/tftp diff --git a/libexec/tftpd/tftpd.8 b/libexec/tftpd/tftpd.8 index 430c1c4..4a9004d 100644 --- a/libexec/tftpd/tftpd.8 +++ b/libexec/tftpd/tftpd.8 @@ -42,6 +42,7 @@ Internet Trivial File Transfer Protocol server .Nm tftpd .Op Fl l .Op Fl n +.Op Fl s Ar directory .Op Ar directory ... .Sh DESCRIPTION .Nm Tftpd @@ -87,6 +88,15 @@ 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 chroot option provides additional security by restricting access +of tftpd to only a chroot'd file system. This is useful when moving +from an OS that supported +.Nm -s +as a boot server. Because chroot is restricted to root, you must run +tftpd as root. However, if you chroot, then +.Nm tftpd +will set its user id to nobody. +.Pp The options are: .Bl -tag -width Ds .It Fl l @@ -95,6 +105,11 @@ Logs all requests using .It Fl n Suppresses negative acknowledgement of requests for nonexistent relative filenames. +.It Fl s Ar directory +Causes tftpd to chroot to +.Pa directory +before accepting commands. In addition, the user id is set to +nobody. .El .Sh SEE ALSO .Xr tftp 1 , diff --git a/libexec/tftpd/tftpd.c b/libexec/tftpd/tftpd.c index 2c74e3d..199ec2b 100644 --- a/libexec/tftpd/tftpd.c +++ b/libexec/tftpd/tftpd.c @@ -29,6 +29,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id: tftpd.c,v 1.8 1997/03/24 06:04:08 imp Exp $ */ #ifndef lint @@ -52,6 +54,7 @@ static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93"; #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/socket.h> +#include <sys/types.h> #include <netinet/in.h> #include <arpa/tftp.h> @@ -68,6 +71,7 @@ static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93"; #include <string.h> #include <syslog.h> #include <unistd.h> +#include <pwd.h> #include "tftpsubs.h" @@ -113,9 +117,11 @@ main(argc, argv) register int n; int ch, on; struct sockaddr_in sin; + char *chroot_dir = NULL; + struct passwd *nobody; openlog("tftpd", LOG_PID, LOG_FTP); - while ((ch = getopt(argc, argv, "ln")) != EOF) { + while ((ch = getopt(argc, argv, "lns:")) != -1) { switch (ch) { case 'l': logging = 1; @@ -123,6 +129,9 @@ main(argc, argv) case 'n': suppress_naks = 1; break; + case 's': + chroot_dir = optarg; + break; default: syslog(LOG_WARNING, "ignoring unknown option -%c", ch); } @@ -140,6 +149,10 @@ main(argc, argv) } } } + else if (chroot_dir) { + dirs->name = "/"; + dirs->len = 1; + } on = 1; if (ioctl(0, FIONBIO, &on) < 0) { @@ -203,6 +216,26 @@ main(argc, argv) exit(0); } } + + /* + * 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) { + /* Must get this before chroot because /etc might go away */ + if ((nobody = getpwnam("nobody")) == NULL) { + syslog(LOG_ERR, "nobody: no such user"); + exit(1); + } + if (chroot(chroot_dir)) { + syslog(LOG_ERR, "chroot: %s: %m", chroot_dir); + exit(1); + } + chdir( "/" ); + setuid(nobody->pw_uid); + } + from.sin_family = AF_INET; alarm(0); close(0); @@ -375,11 +408,11 @@ validate_access(filep, mode) /* * Relative file name: search the approved locations for it. - * Don't allow write requests or ones that avoid directory + * Don't allow write requests that avoid directory * restrictions. */ - if (mode != RRQ || !strncmp(filename, "../", 3)) + if (!strncmp(filename, "../", 3)) return (EACCESS); /* @@ -389,7 +422,8 @@ validate_access(filep, mode) */ err = ENOTFOUND; for (dirp = dirs; dirp->name != NULL; dirp++) { - sprintf(pathname, "%s/%s", dirp->name, filename); + 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) { @@ -402,7 +436,7 @@ validate_access(filep, mode) return (err); *filep = filename = pathname; } - fd = open(filename, mode == RRQ ? 0 : 1); + fd = open(filename, mode == RRQ ? O_RDONLY : O_WRONLY|O_TRUNC); if (fd < 0) return (errno + 100); file = fdopen(fd, (mode == RRQ)? "r":"w"); @@ -599,7 +633,7 @@ errtomsg(error) for (pe = errmsgs; pe->e_code >= 0; pe++) if (pe->e_code == error) return pe->e_msg; - sprintf(buf, "error %d", error); + snprintf(buf, sizeof(buf), "error %d", error); return buf; } diff --git a/libexec/uucpd/Makefile b/libexec/uucpd/Makefile index 45df1cd..9a296c9 100644 --- a/libexec/uucpd/Makefile +++ b/libexec/uucpd/Makefile @@ -1,6 +1,8 @@ # @(#)Makefile 8.1 (Berkeley) 6/4/93 +# $Id$ PROG= uucpd -NOMAN= noman - +MAN8= uucpd.8 +LDADD= -lcrypt -lutil +DPADD= ${LIBCRYPT} ${LIBUTIL} .include <bsd.prog.mk> diff --git a/libexec/uucpd/pathnames.h b/libexec/uucpd/pathnames.h index e9823ee..782f23e 100644 --- a/libexec/uucpd/pathnames.h +++ b/libexec/uucpd/pathnames.h @@ -31,8 +31,10 @@ * SUCH DAMAGE. * * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + * + * $Id$ */ #include <paths.h> -#define _PATH_UUCICO "/usr/lib/uucp/uucico" +#define _PATH_UUCICO "/usr/libexec/uucp/uucico" diff --git a/libexec/bugfiler/sendbug.1 b/libexec/uucpd/uucpd.8 index 31ca802..654f65e 100644 --- a/libexec/bugfiler/sendbug.1 +++ b/libexec/uucpd/uucpd.8 @@ -1,4 +1,4 @@ -.\" Copyright (c) 1983, 1990, 1993 +.\" 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 @@ -29,57 +29,40 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" @(#)sendbug.1 8.1 (Berkeley) 6/4/93 +.\" @(#)rshd.8 8.1 (Berkeley) 6/4/93 +.\" $Id$ .\" -.Dd June 4, 1993 -.Dt SENDBUG 1 -.Os BSD 4.2 +.Dd Feb 18, 1996 +.Dt UUCPD 8 +.Os BSD 4.4 .Sh NAME -.Nm sendbug -.Nd mail a system bug report to 4bsd-bugs +.Nm uucpd +.Nd uucp network server .Sh SYNOPSIS -.Nm sendbug -.Op Ar address +.Nm uucpd .Sh DESCRIPTION -Bug reports sent to `4bsd-bugs@Berkeley.EDU' are intercepted -by a program which expects bug reports to conform to a standard format. -.Nm Sendbug -is a shell script to help the user compose and mail bug reports -in the correct format. -.Nm Sendbug -works by invoking the editor specified by the environment variable -.Ev EDITOR -on a temporary copy of the bug report format outline. The user must fill in the -appropriate fields and exit the editor. -.Nm Sendbug -then mails the completed report to `4bsd-bugs@Berkeley.EDU' or the -.Ar address -specified on the command line. -.Sh ENVIRONMENT -.Nm Sendbug -will utilize the following environment variable if it exists: -.Bl -tag -width EDITOR -.It Ev EDITOR -Specifies the preferred editor. If -.Ev EDITOR -is not set, -.Nm -defaults to -.Xr vi 1 . -.El +.Nm Uucpd +is the server for supporting uucp connections over networks. +.Nm Uucpd +listens for service requests at the port indicated in the ``uucp'' service +specification; see +.Xr services 5 . +The server provides login name and password authentication before starting +up +.Nm uucico +for the rest of the transaction. +.Sh SEE ALSO +.Xr inetd.conf 5 , +.Xr services 5 , +.Xr inetd 8 , +.Xr uucico 8 . .Sh FILES -.Bl -tag -width /usr/share/misc/bugformat -compact -.It Pa /usr/share/misc/bugformat -Contains the bug report outline. +.Bl -tag -width /etc/hosts -compact +.It Pa /etc/services +.It Pa /etc/inetd.conf .El -.Sh SEE ALSO -.Xr vi 1 , -.Xr environ 7 , -.Xr bugfiler 8 , -.Xr sendmail 8 .Sh HISTORY The -.Nm sendbug -command -appeared in -.Bx 4.2 . +.Nm uucpd +utility first appeared in +.Bx 4.3 . diff --git a/libexec/uucpd/uucpd.c b/libexec/uucpd/uucpd.c index 5b958ce..f95b388 100644 --- a/libexec/uucpd/uucpd.c +++ b/libexec/uucpd/uucpd.c @@ -32,6 +32,8 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + * + * $Id: uucpd.c,v 1.12 1997/04/08 12:32:17 davidn Exp $ */ #ifndef lint @@ -53,6 +55,7 @@ static char sccsid[] = "@(#)uucpd.c 8.1 (Berkeley) 6/4/93"; #include <sys/wait.h> #include <sys/ioctl.h> #include <sys/socket.h> +#include <sys/param.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> @@ -65,207 +68,186 @@ static char sccsid[] = "@(#)uucpd.c 8.1 (Berkeley) 6/4/93"; #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <utmp.h> +#include <syslog.h> +#include <sys/param.h> #include "pathnames.h" +#if (MAXLOGNAME-1) > UT_NAMESIZE +#define LOGNAMESIZE UT_NAMESIZE +#else +#define LOGNAMESIZE (MAXLOGNAME-1) +#endif + +#define SCPYN(a, b) strncpy(a, b, sizeof (a)) + struct sockaddr_in hisctladdr; int hisaddrlen = sizeof hisctladdr; struct sockaddr_in myctladdr; int mypid; -char Username[64]; +char Username[64], Logname[64]; char *nenv[] = { Username, + Logname, NULL, }; extern char **environ; +extern void logwtmp(char *line, char *name, char *host); -main(argc, argv) -int argc; -char **argv; -{ -#ifndef BSDINETD - register int s, tcp_socket; - struct servent *sp; -#endif !BSDINETD - extern int errno; - int dologout(); +void doit(struct sockaddr_in *sinp); +void dologout(void); +int readline(char start[], int num, int passw); +void dologin(struct passwd *pw, struct sockaddr_in *sin); +void main(int argc, char **argv) +{ environ = nenv; -#ifdef BSDINETD close(1); close(2); dup(0); dup(0); hisaddrlen = sizeof (hisctladdr); - if (getpeername(0, &hisctladdr, &hisaddrlen) < 0) { - fprintf(stderr, "%s: ", argv[0]); - perror("getpeername"); + openlog("uucpd", LOG_PID, LOG_DAEMON); + if (getpeername(0, (struct sockaddr *)&hisctladdr, &hisaddrlen) < 0) { + syslog(LOG_ERR, "getpeername: %m"); _exit(1); } - if (fork() == 0) - doit(&hisctladdr); + doit(&hisctladdr); dologout(); - exit(1); -#else !BSDINETD - sp = getservbyname("uucp", "tcp"); - if (sp == NULL){ - perror("uucpd: getservbyname"); - exit(1); - } - if (fork()) - exit(0); - if ((s=open(_PATH_TTY, 2)) >= 0){ - ioctl(s, TIOCNOTTY, (char *)0); - close(s); - } + exit(0); +} - bzero((char *)&myctladdr, sizeof (myctladdr)); - myctladdr.sin_family = AF_INET; - myctladdr.sin_port = sp->s_port; -#ifdef BSD4_2 - tcp_socket = socket(AF_INET, SOCK_STREAM, 0); - if (tcp_socket < 0) { - perror("uucpd: socket"); - exit(1); - } - if (bind(tcp_socket, (char *)&myctladdr, sizeof (myctladdr)) < 0) { - perror("uucpd: bind"); - exit(1); - } - listen(tcp_socket, 3); /* at most 3 simultaneuos uucp connections */ - signal(SIGCHLD, dologout); +void badlogin(char *name, struct sockaddr_in *sin) +{ + char remotehost[MAXHOSTNAMELEN]; + struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, + sizeof (struct in_addr), AF_INET); - for(;;) { - s = accept(tcp_socket, &hisctladdr, &hisaddrlen); - if (s < 0){ - if (errno == EINTR) - continue; - perror("uucpd: accept"); - exit(1); - } - if (fork() == 0) { - close(0); close(1); close(2); - dup(s); dup(s); dup(s); - close(tcp_socket); close(s); - doit(&hisctladdr); - exit(1); - } - close(s); - } -#endif BSD4_2 + if (hp) { + strncpy(remotehost, hp->h_name, sizeof (remotehost)); + endhostent(); + } else + strncpy(remotehost, inet_ntoa(sin->sin_addr), + sizeof (remotehost)); -#endif !BSDINETD + remotehost[sizeof remotehost - 1] = '\0'; + + syslog(LOG_NOTICE, "LOGIN FAILURE FROM %s", remotehost); + syslog(LOG_AUTHPRIV|LOG_NOTICE, + "LOGIN FAILURE FROM %s, %s", remotehost, name); + + fprintf(stderr, "Login incorrect.\n"); + exit(1); } -doit(sinp) -struct sockaddr_in *sinp; +void doit(struct sockaddr_in *sinp) { char user[64], passwd[64]; char *xpasswd, *crypt(); - struct passwd *pw, *getpwnam(); + struct passwd *pw; + pid_t s; + int pwdok =0; alarm(60); printf("login: "); fflush(stdout); - if (readline(user, sizeof user) < 0) { - fprintf(stderr, "user read\n"); - return; + if (readline(user, sizeof user, 0) < 0) { + syslog(LOG_WARNING, "login read: %m"); + _exit(1); } - /* truncate username to 8 characters */ - user[8] = '\0'; + /* truncate username to LOGNAMESIZE characters */ + user[LOGNAMESIZE] = '\0'; pw = getpwnam(user); - if (pw == NULL) { - fprintf(stderr, "user unknown\n"); - return; - } - if (strcmp(pw->pw_shell, _PATH_UUCICO)) { - fprintf(stderr, "Login incorrect."); - return; - } - if (pw->pw_passwd && *pw->pw_passwd != '\0') { + /* + * Fail after password if: + * 1. Invalid user + * 2. Shell is not uucico + * 3. Account has expired + * 4. Password is incorrect + */ + if (pw != NULL && strcmp(pw->pw_shell, _PATH_UUCICO) == 0 && + (!pw->pw_expire || time(NULL) >= pw->pw_expire)) + pwdok = 1; + /* always ask for passwords to deter account guessing */ + if (!pwdok || (pw->pw_passwd && *pw->pw_passwd != '\0')) { printf("Password: "); fflush(stdout); - if (readline(passwd, sizeof passwd) < 0) { - fprintf(stderr, "passwd read\n"); - return; + if (readline(passwd, sizeof passwd, 1) < 0) { + syslog(LOG_WARNING, "passwd read: %m"); + _exit(1); } - xpasswd = crypt(passwd, pw->pw_passwd); - if (strcmp(xpasswd, pw->pw_passwd)) { - fprintf(stderr, "Login incorrect."); - return; + if (pwdok) { + xpasswd = crypt(passwd, pw->pw_passwd); + if (strcmp(xpasswd, pw->pw_passwd)) + pwdok = 0; } + if (!pwdok) + badlogin(user, sinp); } alarm(0); - sprintf(Username, "USER=%s", user); - dologin(pw, sinp); - setgid(pw->pw_gid); -#ifdef BSD4_2 - initgroups(pw->pw_name, pw->pw_gid); -#endif BSD4_2 - chdir(pw->pw_dir); - setuid(pw->pw_uid); -#ifdef BSD4_2 - execl(UUCICO, "uucico", (char *)0); -#endif BSD4_2 - perror("uucico server: execl"); + sprintf(Username, "USER=%s", pw->pw_name); + sprintf(Logname, "LOGNAME=%s", pw->pw_name); + if ((s = fork()) < 0) { + syslog(LOG_ERR, "fork: %m"); + _exit(1); + } else if (s == 0) { + dologin(pw, sinp); + setgid(pw->pw_gid); + initgroups(pw->pw_name, pw->pw_gid); + chdir(pw->pw_dir); + setuid(pw->pw_uid); + execl(pw->pw_shell, "uucico", NULL); + syslog(LOG_ERR, "execl: %m"); + _exit(1); + } } -readline(p, n) -register char *p; -register int n; +int readline(char start[], int num, int passw) { char c; + register char *p = start; + register int n = num; while (n-- > 0) { if (read(0, &c, 1) <= 0) return(-1); c &= 0177; - if (c == '\n' || c == '\r') { + if (c == '\n' || c == '\r' || c == '\0') { + if (p == start && passw) { + n++; + continue; + } *p = '\0'; return(0); } + if (c == 025) { + n = num; + p = start; + continue; + } *p++ = c; } return(-1); } -#include <utmp.h> -#ifdef BSD4_2 -#include <fcntl.h> -#endif BSD4_2 - -#define SCPYN(a, b) strncpy(a, b, sizeof (a)) - -struct utmp utmp; - -dologout() +void dologout(void) { union wait status; - int pid, wtmp; + pid_t pid; + char line[32]; -#ifdef BSDINETD while ((pid=wait((int *)&status)) > 0) { -#else !BSDINETD - while ((pid=wait3((int *)&status,WNOHANG,0)) > 0) { -#endif !BSDINETD - wtmp = open(_PATH_WTMP, O_WRONLY|O_APPEND); - if (wtmp >= 0) { - sprintf(utmp.ut_line, "uucp%.4d", pid); - SCPYN(utmp.ut_name, ""); - SCPYN(utmp.ut_host, ""); - (void) time(&utmp.ut_time); - (void) write(wtmp, (char *)&utmp, sizeof (utmp)); - (void) close(wtmp); - } + sprintf(line, "uucp%ld", pid); + logwtmp(line, "", ""); } } /* * Record login in wtmp file. */ -dologin(pw, sin) -struct passwd *pw; -struct sockaddr_in *sin; +void dologin(struct passwd *pw, struct sockaddr_in *sin) { char line[32]; - char remotehost[32]; - int wtmp, f; + char remotehost[MAXHOSTNAMELEN]; + int f; + time_t cur_time; struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, sizeof (struct in_addr), AF_INET); @@ -275,26 +257,19 @@ struct sockaddr_in *sin; } else strncpy(remotehost, inet_ntoa(sin->sin_addr), sizeof (remotehost)); - wtmp = open(_PATH_WTMP, O_WRONLY|O_APPEND); - if (wtmp >= 0) { - /* hack, but must be unique and no tty line */ - sprintf(line, "uucp%.4d", getpid()); - SCPYN(utmp.ut_line, line); - SCPYN(utmp.ut_name, pw->pw_name); - SCPYN(utmp.ut_host, remotehost); - time(&utmp.ut_time); - (void) write(wtmp, (char *)&utmp, sizeof (utmp)); - (void) close(wtmp); - } + remotehost[sizeof remotehost - 1] = '\0'; + /* hack, but must be unique and no tty line */ + sprintf(line, "uucp%ld", getpid()); + time(&cur_time); if ((f = open(_PATH_LASTLOG, O_RDWR)) >= 0) { struct lastlog ll; - time(&ll.ll_time); - lseek(f, (long)pw->pw_uid * sizeof(struct lastlog), 0); - strcpy(line, remotehost); + ll.ll_time = cur_time; + lseek(f, (off_t)pw->pw_uid * sizeof(struct lastlog), L_SET); SCPYN(ll.ll_line, line); SCPYN(ll.ll_host, remotehost); (void) write(f, (char *) &ll, sizeof ll); (void) close(f); } + logwtmp(line, pw->pw_name, remotehost); } diff --git a/libexec/xtend/Makefile b/libexec/xtend/Makefile new file mode 100644 index 0000000..d89e079 --- /dev/null +++ b/libexec/xtend/Makefile @@ -0,0 +1,14 @@ +# Makefile for xtend (Stark) 10/30/93 +# $Id$ + +BINMODE= 555 + +PROG= xtend +SRCS= xtend.c status.c packet.c user.c +CFLAGS+= -DXTENUNAME=\"xten\" -DXTENGNAME=\"xten\" +DPADD= ${LIBUTIL} +LDADD= -lutil + +MAN8= xtend.8 + +.include <bsd.prog.mk> diff --git a/libexec/xtend/packet.c b/libexec/xtend/packet.c new file mode 100644 index 0000000..ec67c94 --- /dev/null +++ b/libexec/xtend/packet.c @@ -0,0 +1,319 @@ +/*- + * Copyright (c) 1992, 1993, 1995 Eugene W. Stark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Eugene W. Stark. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#include <stdio.h> +#include <sys/time.h> +#include "xtend.h" +#include "xten.h" + +char *X10housenames[] = { + "A", "B", "C", "D", "E", "F", "G", "H", + "I", "J", "K", "L", "M", "N", "O", "P", + NULL +}; + +char *X10cmdnames[] = { + "1", "2", "3", "4", "5", "6", "7", "8", + "9", "10", "11", "12", "13", "14", "15", "16", + "AllUnitsOff", "AllLightsOn", "On", "Off", "Dim", "Bright", "AllLightsOff", + "ExtendedCode", "HailRequest", "HailAcknowledge", "PreSetDim0", "PreSetDim1", + "ExtendedData", "StatusOn", "StatusOff", "StatusRequest", + NULL +}; + +/* + * Log a packet and update device status accordingly + */ + +logpacket(p) +unsigned char *p; +{ + fprintf(Log, "%s: %s %s ", thedate(), + X10housenames[p[1]], X10cmdnames[p[2]]); + if(p[0] & TW_RCV_LOCAL) fprintf(Log, "(loc,"); + else fprintf(Log, "(rem,"); + if(p[0] & TW_RCV_ERROR) fprintf(Log, "err)"); + else fprintf(Log, " ok)"); + fprintf(Log, "\n"); +} + +/* + * Process a received packet p, updating device status information both + * in core and on disk. + */ + +processpacket(p) +unsigned char *p; +{ + int i, j, h, k; + STATUS *s; + + /* + * If the packet had the error flag set, there is no other useful info. + */ + if(p[0] & TW_RCV_ERROR) return; + /* + * First update in-core status information for the device. + */ + h = p[1]; k = p[2]; + if(k < 16) { /* We received a unit code, to select a particular device */ + s = &Status[h][k]; + s->selected = SELECTED; + s->lastchange = time(NULL); + s->changed = 1; + } else { /* We received a key code, to execute some function */ + /* + * Change in status depends on the key code received + */ + if(k == DIM) { + /* + * We can't really track DIM/BRIGHT properly the way things are right + * now. The TW523 reports the first, fourth, seventh, etc. Dim packet. + * We don't really have any way to tell when gaps occur, to cancel + * selection. For now, we'll assume that successive sequences of + * Dim/Bright commands are never transmitted without some other + * intervening command, and we make a good guess about how many units of + * dim/bright are represented by each packet actually reported by the + * TW523. + */ + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + switch(s->selected) { + case SELECTED: /* Selected, but not being dimmed or brightened */ + if(s->onoff == 0) { + s->onoff = 1; + s->brightness = 15; + } + s->brightness -= 2; + if(s->brightness < 0) s->brightness = 0; + s->selected = DIMMING; + s->lastchange = time(NULL); + s->changed = 1; + break; + case DIMMING: /* Selected and being dimmed */ + s->brightness -=3; + if(s->brightness < 0) s->brightness = 0; + s->lastchange = time(NULL); + s->changed = 1; + break; + case BRIGHTENING: /* Selected and being brightened (an error) */ + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + break; + default: + break; + } + } + } else if(k == BRIGHT) { + /* + * Same problem here... + */ + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + switch(s->selected) { + case SELECTED: /* Selected, but not being dimmed or brightened */ + if(s->onoff == 0) { + s->onoff = 1; + s->brightness = 15; + } + s->brightness += 2; + if(s->brightness > 15) s->brightness = 15; + s->selected = BRIGHTENING; + s->lastchange = time(NULL); + s->changed = 1; + break; + case DIMMING: /* Selected and being dimmed (an error) */ + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + break; + case BRIGHTENING: /* Selected and being brightened */ + s->brightness +=3; + if(s->brightness > 15) s->brightness = 15; + s->lastchange = time(NULL); + s->changed = 1; + break; + default: + break; + } + } + } else { /* Other key codes besides Bright and Dim */ + /* + * We cancel brightening and dimming on ALL units on ALL house codes, + * because the arrival of a different packet indicates a gap that + * terminates any prior sequence of brightening and dimming + */ + for(j = 0; j < 16; j++) { + for(i = 0; i < 16; i++) { + s = &Status[j][i]; + if(s->selected == BRIGHTENING || s->selected == DIMMING) { + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + } + switch(k) { + case ALLUNITSOFF: + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + s->onoff = 0; + s->selected = IDLE; + s->brightness = 0; + s->lastchange = time(NULL); + s->changed = 1; + } + break; + case ALLLIGHTSON: + /* Does AllLightsOn cancel selectedness of non-lights? */ + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->devcap & ISLIGHT) { + s->onoff = 1; + s->selected = IDLE; + s->brightness = 15; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case UNITON: + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected == SELECTED) { + s->onoff = 1; + s->selected = IDLE; + s->brightness = 15; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case UNITOFF: + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected == SELECTED) { + s->onoff = 0; + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case ALLLIGHTSOFF: + /* Does AllLightsOff cancel selectedness of non-lights? */ + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->devcap & ISLIGHT) { + s->onoff = 0; + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case EXTENDEDCODE: + break; + case HAILREQUEST: + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected == SELECTED) { + s->selected = HAILED; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case HAILACKNOWLEDGE: + /* Do these commands cancel selection of devices not affected? */ + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected == HAILED) { + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case PRESETDIM0: + case PRESETDIM1: + /* I don't really understand these */ + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected == SELECTED) { + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case EXTENDEDDATA: + /* Who knows? The TW523 can't receive these anyway. */ + break; + case STATUSON: + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected == REQUESTED) { + s->onoff = 1; + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case STATUSOFF: + for(i = 0; i < 16; i++) { + if(s->selected == REQUESTED) { + s = &Status[h][i]; + s->onoff = 0; + s->selected = IDLE; + s->brightness = 0; + s->lastchange = time(NULL); + s->changed = 1; + } + } + case STATUSREQUEST: + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected) { + s->selected = REQUESTED; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + } + } + } +} diff --git a/libexec/xtend/paths.h b/libexec/xtend/paths.h new file mode 100644 index 0000000..e41d214 --- /dev/null +++ b/libexec/xtend/paths.h @@ -0,0 +1,13 @@ +/* + * Pathnames for files used by xtend + * + * $Id$ + */ + +#define X10DIR "/var/spool/xten" +#define X10LOGNAME "Log" +#define X10STATNAME "Status" +#define X10DUMPNAME "status.out" +#define TWPATH "/dev/tw0" +#define SOCKPATH "/var/run/tw523" +#define PIDPATH "/var/run/xtend.pid" diff --git a/libexec/xtend/status.c b/libexec/xtend/status.c new file mode 100644 index 0000000..e527f55 --- /dev/null +++ b/libexec/xtend/status.c @@ -0,0 +1,111 @@ +/*- + * Copyright (c) 1992, 1993, 1995 Eugene W. Stark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Eugene W. Stark. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#include <stdio.h> +#include <unistd.h> +#include <sys/param.h> +#include <sys/stat.h> +#include "xtend.h" +#include "xten.h" +#include "paths.h" + +/* + * Initialize the status table from the status files + */ + +initstatus() +{ + int h, i; + + if(lseek(status, 0, SEEK_SET) != 0) { + fprintf(Log, "%s: Seek error on status file\n", thedate()); + return; + } + if(read(status, Status, 16*16*sizeof(STATUS)) != 16*16*sizeof(STATUS)) { + fprintf(Log, "%s: Read error on status file\n", thedate()); + return; + } +} + +/* + * Checkpoint status of any devices whose status has changed + * and notify anyone monitoring those devices. + */ + +checkpoint_status() +{ + int h, i, k, offset; + + offset = 0; + for(h = 0; h < 16; h++) { + for(i = 0; i < 16; i++) { + if(Status[h][i].changed) { + if(lseek(status, offset, SEEK_SET) != offset) { + fprintf(Log, "%s: Seek error on status file\n", thedate()); + } else { + if(write(status, &Status[h][i], sizeof(STATUS)) != sizeof(STATUS)) { + fprintf(Log, "%s: Write error on status file\n", thedate()); + } + } + Status[h][i].changed = 0; + for(k = 0; k < MAXMON; k++) { + if(Monitor[k].inuse + && Monitor[k].house == h && Monitor[k].unit == i) { + /* + * Arrange to catch SIGPIPE in case client has gone away. + */ + extern int client; + extern void clientgone(); + void (*prev)(); + + client = k; + prev = signal(SIGPIPE, clientgone); + printstatus(Monitor[k].user, &Status[h][i]); + fflush(Monitor[k].user); + signal(SIGPIPE, prev); + } + } + } + offset += sizeof(STATUS); + } + } +} + +int client; + +void clientgone() +{ + fprintf(Log, "%s: Deleting monitor table entry %d, client gone\n", thedate(), client); + fclose(Monitor[client].user); + Monitor[client].inuse = 0; +} diff --git a/libexec/xtend/user.c b/libexec/xtend/user.c new file mode 100644 index 0000000..1a863af --- /dev/null +++ b/libexec/xtend/user.c @@ -0,0 +1,175 @@ +/*- + * Copyright (c) 1992, 1993, 1995 Eugene W. Stark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Eugene W. Stark. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: user.c,v 1.5 1997/02/22 14:22:42 peter Exp $ + */ + +#include <stdio.h> +#include <sys/param.h> +#include <sys/time.h> +#include "xtend.h" +#include "xten.h" +#include "paths.h" + +MONENTRY Monitor[MAXMON]; + +/* + * Process a user command + */ + +user_command() +{ + char h; + char *m; + int i, k, c, n, error; + char cmd[512], dumppath[MAXPATHLEN+1], pkt[3]; + FILE *dumpf; + + error = 0; + if(fgets(cmd, 512, User) != NULL) { + m = cmd; + while ( *m != '\0' ) { + if(isupper(*m)) + *m = tolower(*m); + m++; + } + if(sscanf(cmd, "status %c %d", &h, &i) == 2 + && h >= 'a' && h <= 'p' && i >= 1 && i <= 16) { + h -= 'a'; + i--; + printstatus(User, &Status[h][i]); + } else if(sscanf(cmd, "send %c %s %d", &h, cmd, &n) == 3 + && h >= 'a' && h <= 'p' && (i = find(cmd, X10cmdnames)) >= 0) { + h -= 'a'; + pkt[0] = h; + pkt[1] = i; + pkt[2] = n; + if(write(tw523, pkt, 3) != 3) { + fprintf(Log, "%s: Transmission error (packet [%s %s]:%d).\n", + thedate(), X10housenames[h], X10cmdnames[i], n); + error++; + } else { + fprintf(User, "OK\n"); + } + } else if(!strcmp("dump\n", cmd)) { + strcpy(dumppath, X10DIR); + strcat(dumppath, X10DUMPNAME); + if((dumpf = fopen(dumppath, "w")) != NULL) { + for(h = 0; h < 16; h++) { + for(i = 0; i < 16; i++) { + if(Status[h][i].lastchange) { + fprintf(dumpf, "%s%d\t", X10housenames[h], i+1); + printstatus(dumpf, &Status[h][i]); + } + } + } + fclose(dumpf); + fprintf(User, "OK\n"); + } else { + error++; + } + } else if(sscanf(cmd, "monitor %c %d", &h, &i) == 2 + && h >= 'a' && h <= 'p' && i >= 1 && i <= 16) { + h -= 'a'; + i--; + for(k = 0; k < MAXMON; k++) { + if(!Monitor[k].inuse) break; + } + if(k == MAXMON) { + error++; + } else { + Monitor[k].house = h; + Monitor[k].unit = i; + Monitor[k].user = User; + Monitor[k].inuse = 1; + fprintf(Log, "%s: Adding %c %d to monitor list (entry %d)\n", + thedate(), h+'A', i+1, k); + fprintf(User, "OK\n"); + fflush(User); + User = NULL; + return(0); /* We don't want caller to close stream */ + } + } else if(!strcmp("done\n", cmd)) { + fprintf(User, "OK\n"); + fflush(User); + return(1); + } else { + if(feof(User)) { + return(1); + } else { + error++; + } + } + } else { + error++; + } + if(error) { + fprintf(User, "ERROR\n"); + } + fflush(User); + return(0); +} + +find(s, tab) +char *s; +char *tab[]; +{ + int i; + + for(i = 0; tab[i] != NULL; i++) { + if(strcasecmp(s, tab[i]) == 0) return(i); + } + return(-1); +} + +printstatus(f, s) +FILE *f; +STATUS *s; +{ + fprintf(f, "%s:%d", s->onoff ? "On" : "Off", s->brightness); + switch(s->selected) { + case IDLE: + fprintf(f, " (normal) "); break; + case SELECTED: + fprintf(f, " (selected) "); break; + case DIMMING: + fprintf(f, " (dimming) "); break; + case BRIGHTENING: + fprintf(f, " (brightening) "); break; + case REQUESTED: + fprintf(f, " (requested) "); break; + case HAILED: + fprintf(f, " (hailed) "); break; + default: + fprintf(f, " (bogus) "); break; + } + fprintf(f, "%s", ctime(&s->lastchange)); +} + diff --git a/libexec/xtend/xten.h b/libexec/xtend/xten.h new file mode 100644 index 0000000..23e8c6b --- /dev/null +++ b/libexec/xtend/xten.h @@ -0,0 +1,60 @@ +/*- + * Copyright (c) 1992, 1993, 1995 Eugene W. Stark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Eugene W. Stark. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +extern char *X10housenames[]; +extern char *X10cmdnames[]; + +#define ALLUNITSOFF 16 +#define ALLLIGHTSON 17 +#define UNITON 18 +#define UNITOFF 19 +#define DIM 20 +#define BRIGHT 21 +#define ALLLIGHTSOFF 22 +#define EXTENDEDCODE 23 +#define HAILREQUEST 24 +#define HAILACKNOWLEDGE 25 +#define PRESETDIM0 26 +#define PRESETDIM1 27 +#define EXTENDEDDATA 28 +#define STATUSON 29 +#define STATUSOFF 30 +#define STATUSREQUEST 31 + +/* + * Flags for first byte of received packet + */ + +#define TW_RCV_LOCAL 1 /* The packet arrived during a local transmission */ +#define TW_RCV_ERROR 2 /* An invalid/corrupted packet was received */ + diff --git a/libexec/xtend/xtend.8 b/libexec/xtend/xtend.8 new file mode 100644 index 0000000..fe6209b --- /dev/null +++ b/libexec/xtend/xtend.8 @@ -0,0 +1,176 @@ +.\" Copyright (c) 1992, 1993 Eugene W. Stark +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Eugene W. Stark. +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: xtend.8,v 1.6 1997/02/22 14:22:43 peter Exp $ +.\" +.Th XTEND 8 "30 Oct 1993" +.Dd Oct 30, 1993 +.Dt XTEND 8 +.Os BSD FreeBSD +.Sh NAME +xtend \- X-10 daemon +.Sh SYNOPSIS +.Nm xtend +.Sh DESCRIPTION +.Nm Xtend +interfaces between user-level programs and the TW523 X-10 controller. +It logs all packets received from the TW523, attempts to track the +status of all X-10 devices, and accepts socket connections from user-level +client programs that need to manipulate X-10 devices. +.Pp +When +.Nm xtend +is started, it forks, releases the controlling terminal, then opens +its log file, where it subsequently records all X-10 activity and +diagnostic messages. It then begins processing packets received from +the TW523 and accepting connections one at a time from clients +wishing to issue X-10 commands. The usual place to start xtend would +be from the +.Pa /etc/rc.local +startup script. +.Pp +Sending +.Nm xtend +a SIGHUP causes it to close and reopen its log file. This is useful +in shell scripts that rotate the log files to keep them from growing +indefinitely. +If +.Nm xtend +receives a SIGTERM, it shuts down gracefully and exits. +A SIGPIPE causes +.Nm xtend +to abort the current client connection. +.Pp +.Nm Xtend +communicates with client processes by a simple protocol in which a one-line +command is sent by the client, and is acknowledged by a one-line response +from the daemon. +.Pp +.Nm Xtend +understands four types of commands. The command +.Bl -tag +.It status H U +.El +.Pp +where H is a single letter house code, and U is a numeric unit code, +causes +.Nm xtend +to respond with one line of status information about the specified device. +The command +.Bl -tag +.It send H U N +.El +.Pp +where H is a single-letter house code, U is either a numeric unit code +or a function code (see source file +.Pa xtend/packet.c +) for a list, and N is a number indicating the number of times (usually 2) +the packet is to be transmitted without gaps, +causes +.Nm xtend +to perform the specified X-10 transmission. If the transmission was apparently +successful, a single-line response containing +.B +OK +is issued, otherwise a single-line response containing +.B +ERROR +is produced. +The command +.Bl -tag +.It dump +.El +.Pp +causes +.Nm xtend +to dump the current status of all devices to an ASCII file in the spool +directory. The response +.B +OK +is issued, regardless of whether the status dump was successful. +The command +.Bl -tag +.It monitor H U +.El +.Pp +causes +.Nm xtend +to add the current client socket connection to a list of clients that are to +be notified about activity concerning the specified X-10 device. +The single-line acknowledgement +.B +OK +is returned if the maximum (currently 5) number of such clients was not +exceeded, otherwise +.B +ERROR +is returned. +.Nm Xtend +then returns to its normal mode of accepting connections from clients. +However, each subsequent change in the status of the specified device will +cause +.Nm xtend +to write one line of status information for the device (in the same +format as produced by the +.B +status +command) to the saved socket. This feature is useful for writing programs +that need to monitor the activity of devices, like motion detectors, that can +perform X-10 transmissions. +.Sh OPTIONS +None. +.Sh SEE ALSO +.Xr xten 1 , +.Xr tw 4 +.Sh FILES +.Bl -tag -width /var/spool/xten/Status -compact +.It Pa /dev/tw0 +the TW523 special file +.It Pa /var/run/tw523 +socket for client connections +.It Pa /var/run/xtend.pid +pid file +.It Pa /var/spool/xten/Log +log file +.It Pa /var/spool/xten/Status +device status file (binary) +.It Pa /var/spool/xten/status.out +ASCII dump of device status +.El +.Sh BUGS +There is currently no timeout on client socket connections, so a hung +client program can prevent other clients from accessing the daemon. +.Pp +.Nm Xtend +does the best it can at trying to track device status, but there is +usually no way it can tell when a device has been operated manually. +This is due to the fact that most X-10 devices are not able to +respond to queries about their status. +.Sh AUTHOR +Eugene W. Stark (stark@cs.sunysb.edu) diff --git a/libexec/xtend/xtend.c b/libexec/xtend/xtend.c new file mode 100644 index 0000000..dd32129 --- /dev/null +++ b/libexec/xtend/xtend.c @@ -0,0 +1,370 @@ +/*- + * Copyright (c) 1992, 1993, 1995 Eugene W. Stark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Eugene W. Stark. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +/* + * xtend - X-10 daemon + * Eugene W. Stark (stark@cs.sunysb.edu) + * January 14, 1993 + */ + +#include <stdio.h> +#include <time.h> +#include <setjmp.h> +#include <signal.h> +#include <pwd.h> +#include <grp.h> +#include <sys/param.h> +#include <sys/file.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include "xtend.h" +#include "xten.h" +#include "paths.h" + +FILE *Log; /* Log file */ +FILE *User; /* User connection */ +STATUS Status[16][16]; /* Device status table */ +int status; /* Status file descriptor */ +int tw523; /* tw523 controller */ +int sock; /* socket for user */ +jmp_buf mainloop; /* longjmp point after SIGHUP */ +void onhup(); /* SIGHUP handler */ +void onterm(); /* SIGTERM handler */ +void onpipe(); /* SIGPIPE handler */ + +main(argc, argv) +int argc; +char *argv[]; +{ + char *twpath = TWPATH; + char *sockpath = SOCKPATH; + char logpath[MAXPATHLEN+1]; + char statpath[MAXPATHLEN+1]; + struct sockaddr_un sa; + struct timeval tv; + struct passwd *pw; + struct group *gr; + struct stat sb; + int user; + int fd; + FILE *pidf; + + /* + * Make sure we start out running as root + */ + if(geteuid() != 0) { + fprintf(stderr, "You must be root to run %s\n", argv[0]); + exit(1); + } + + /* + * Find out what UID/GID we are to run as + */ + if((pw = getpwnam(XTENUNAME)) == NULL) { + fprintf(stderr, "%s: No such user '%s'\n", argv[0], XTENUNAME); + exit(1); + } + if((gr = getgrnam(XTENGNAME)) == NULL) { + fprintf(stderr, "%s: No such group '%s'\n", argv[0], XTENGNAME); + exit(1); + } + + /* + * Open the log file before doing anything else + */ + strcpy(logpath, X10DIR); + if(stat(logpath, &sb) == -1 && errno == ENOENT) { + if(mkdir(logpath, 0755) != -1) { + chown(logpath, pw->pw_uid, gr->gr_gid); + } else { + fprintf(stderr, "%s: Can't create directory '%s'\n", argv[0], logpath); + exit(1); + } + } + strcat(logpath, "/"); + strcat(logpath, X10LOGNAME); + if((Log = fopen(logpath, "a")) == NULL) { + fprintf(stderr, "Can't open log file %s\n", logpath); + exit(1); + } + chown(logpath, pw->pw_uid, gr->gr_gid); + + /* + * Become a daemon + */ + if(daemon(0, 0) == -1) { + fprintf(Log, "%s: Unable to become a daemon\n", thedate()); + fclose(Log); + exit(1); + } + fprintf(Log, "%s: %s [%d] started\n", thedate(), argv[0], getpid()); + + /* + * Get ahold of the TW523 device + */ + if((tw523 = open(twpath, O_RDWR)) < 0) { + fprintf(Log, "%s: Can't open %s\n", thedate(), twpath); + fclose(Log); + exit(1); + } + fprintf(Log, "%s: %s successfully opened\n", thedate(), twpath); + + /* + * Put our pid in a file so we can be signalled by shell scripts + */ + if((pidf = fopen(PIDPATH, "w")) == NULL) { + fprintf(Log, "%s: Error writing pid file: %s\n", thedate(), PIDPATH); + fclose(Log); + exit(1); + } + fprintf(pidf, "%d\n", getpid()); + fclose(pidf); + + /* + * Set up socket to accept user commands + */ + if((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + fprintf(Log, "%s: Can't create socket\n", thedate()); + fclose(Log); + exit(1); + } + strcpy(sa.sun_path, sockpath); + sa.sun_family = AF_UNIX; + unlink(sockpath); + if(bind(sock, (struct sockaddr *)(&sa), strlen(sa.sun_path) + 2) < 0) { + fprintf(Log, "%s: Can't bind socket to %s\n", thedate(), sockpath); + fclose(Log); + exit(1); + } + if(listen(sock, 5) < 0) { + fprintf(Log, "%s: Can't listen on socket\n", thedate()); + fclose(Log); + exit(1); + } + + /* + * Set proper ownership and permissions on the socket + */ + if(chown(sockpath, pw->pw_uid, gr->gr_gid) == -1 || + chmod(sockpath, 0660) == -1) { + fprintf(Log, "%s: Can't set owner/permissions on socket\n", thedate()); + fclose(Log); + exit(1); + } + + /* + * Give up root privileges + */ + setgid(pw->pw_gid); + setuid(pw->pw_uid); + + /* + * Initialize the status table + */ + strcpy(statpath, X10DIR); + strcat(statpath, "/"); + strcat(statpath, X10STATNAME); + if((status = open(statpath, O_RDWR)) < 0) { + if((status = open(statpath, O_RDWR | O_CREAT, 0666)) < 0) { + fprintf(Log, "%s: Can't open %s\n", thedate(), statpath); + fclose(Log); + exit(1); + } + if(write(status, Status, 16 * 16 * sizeof(STATUS)) + != 16 * 16 * sizeof(STATUS)) { + fprintf(Log, "%s: Error initializing status file\n", thedate()); + fclose(Log); + exit(1); + } + } + initstatus(); + + /* + * Return here on SIGHUP after closing and reopening log file. + * Also on SIGPIPE after closing user connection. + */ + signal(SIGTERM, onterm); + signal(SIGHUP, onhup); + signal(SIGPIPE, onpipe); + setjmp(mainloop); + + /* + * Now start the main processing loop. + */ + tv.tv_sec = 0; + tv.tv_usec = 250000; + while(1) { + fd_set fs; + unsigned char rpkt[3]; + int sel, h, k; + STATUS *s; + + FD_ZERO(&fs); + FD_SET(tw523, &fs); + if(User != NULL) FD_SET(user, &fs); + else FD_SET(sock, &fs); + sel = select(FD_SETSIZE, &fs, 0, 0, &tv); + if(sel == 0) { + /* + * Cancel brightening and dimming on ALL units on ALL house codes, + * because the fact that we haven't gotten a packet for awhile means + * that there was a gap in transmission. + */ + for(h = 0; h < 16; h++) { + for(k = 0; k < 16; k++) { + s = &Status[h][k]; + if(s->selected == BRIGHTENING || s->selected == DIMMING) { + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + } + fflush(Log); + checkpoint_status(); + /* + * Now that we've done this stuff, we'll set the timeout a little + * longer, so we don't keep looping too frequently. + */ + tv.tv_sec = 60; + tv.tv_usec = 0; + continue; + } + /* + * While there is stuff happening, we keep a short timeout, so we + * don't get stuck for some unknown reason, and so we can keep the + * brightening and dimming data up-to-date. + */ + tv.tv_sec = 0; + tv.tv_usec = 250000; + if(FD_ISSET(tw523, &fs)) { /* X10 data arriving from TW523 */ + if(read(tw523, rpkt, 3) < 3) { + fprintf(Log, "%s: Error reading from TW523\n", thedate()); + } else { + logpacket(rpkt); + processpacket(rpkt); + } + } else if(FD_ISSET(user, &fs)) { + if(User != NULL) { + if(user_command()) { + fprintf(Log, "%s: Closing user connection\n", thedate()); + fclose(User); + User = NULL; + } + } else { + /* "Can't" happen */ + } + } else if(FD_ISSET(sock, &fs)) { /* Accept a connection */ + if (User == NULL) { + int len = sizeof(struct sockaddr_un); + if((user = accept(sock, (struct sockaddr *)(&sa), &len)) >= 0) { + fprintf(Log, "%s: Accepting user connection\n", thedate()); + if((User = fdopen(user, "w+")) == NULL) { + fprintf(Log, "%s: Can't attach socket to stream\n", thedate()); + } + } else { + fprintf(Log, "%s: Failure in attempt to accept connection\n", thedate()); + } + } else { + /* "Can't happen */ + } + } + } + /* Not reached */ +} + +char *thedate() +{ + char *cp, *cp1; + time_t tod; + + tod = time(NULL); + cp = cp1 = ctime(&tod); + while(*cp1 != '\n') cp1++; + *cp1 = '\0'; + return(cp); +} + +/* + * When SIGHUP received, close and reopen the Log file + */ + +void onhup() +{ + char logpath[MAXPATHLEN+1]; + + fprintf(Log, "%s: SIGHUP received, reopening Log\n", thedate()); + fclose(Log); + strcpy(logpath, X10DIR); + strcat(logpath, "/"); + strcat(logpath, X10LOGNAME); + if((Log = fopen(logpath, "a")) == NULL) { + fprintf(stderr, "Can't open log file %s\n", logpath); + exit(1); + } + longjmp(mainloop, 1); + /* No return */ +} + +/* + * When SIGTERM received, just exit normally + */ + +void onterm() +{ + fprintf(Log, "%s: SIGTERM received, shutting down\n", thedate()); + fclose(Log); + exit(0); +} + +/* + * When SIGPIPE received, reset user connection + */ + +void onpipe() +{ + fprintf(Log, "%s: SIGPIPE received, resetting user connection\n", + thedate()); + if(User != NULL) { + fclose(User); + User = NULL; + } + longjmp(mainloop, 1); + /* No return */ +} diff --git a/libexec/xtend/xtend.h b/libexec/xtend/xtend.h new file mode 100644 index 0000000..9d70444 --- /dev/null +++ b/libexec/xtend/xtend.h @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 1992, 1993, 1995 Eugene W. Stark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Eugene W. Stark. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +/* + * Device capabilities + */ + +#define ISLIGHT 1 /* Is device a light? */ +#define CANQUERY 2 /* Responds to status query */ + +/* + * Device status + */ + +typedef enum { + IDLE, + SELECTED, + DIMMING, + BRIGHTENING, + REQUESTED, + HAILED + } SELECT; + +typedef struct { + unsigned int devcap; /* device capabilities */ + unsigned int changed; /* status changed since last checkpoint? */ + time_t lastchange; /* time status last changed */ + SELECT selected; /* select status of device */ + unsigned int onoff; /* nonzero if on */ + unsigned int brightness; /* value in range 0-15 */ +} STATUS; + +typedef struct { + int inuse; /* Is entry in use? */ + FILE *user; /* Socket to notify user */ + int house; /* House code of device to monitor */ + int unit; /* Unit code of device to monitor */ +} MONENTRY; + +#define MAXMON 5 /* Maximum number of monitor entries */ + +extern FILE *Log; /* Log file */ +extern FILE *User; /* User connection */ +extern STATUS Status[16][16]; /* Device status table */ +extern int status; /* Status file descriptor */ +extern int tw523; /* tw523 controller */ +extern MONENTRY Monitor[MAXMON];/* Monitor table */ + +extern char *thedate(); diff --git a/libexec/ypxfr/Makefile b/libexec/ypxfr/Makefile new file mode 100644 index 0000000..61a59c7 --- /dev/null +++ b/libexec/ypxfr/Makefile @@ -0,0 +1,41 @@ +# $Id$ + +PROG= ypxfr +SRCS= ypxfr_clnt.c yp_clnt.c ypxfr_getmap.c yp_dblookup.c \ + yp_error.c ypxfr_misc.c ypxfr_main.c yp_dbwrite.c \ + ypxfrd_getmap.c + +.PATH: ${.CURDIR}/../../usr.sbin/ypserv + +MAN8= ypxfr.8 + +CFLAGS+= -I. +DPADD= ${LIBRPCSVC} +LDADD= -lrpcsvc + +CLEANFILES= yp.h yp_clnt.c ypxfr_clnt.c + +RPCDIR= ${.CURDIR}/../../include/rpcsvc +RPCGEN= rpcgen -I -C + +ypxfr_clnt.c: ${RPCDIR}/yp.x yp.h + rm -f ${.TARGET} + ${RPCGEN} -DYPPUSH_ONLY -l -o ${.TARGET} ${RPCDIR}/yp.x + +yp_clnt.c: ${RPCDIR}/yp.x yp.h + 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 ypxfrd.h +# 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..e5ad7fd --- /dev/null +++ b/libexec/ypxfr/yp_dbwrite.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <limits.h> +#include <unistd.h> +#include <db.h> +#include <sys/stat.h> +#include <errno.h> +#include <paths.h> +#include <rpcsvc/yp.h> +#include "ypxfr_extern.h" + +#ifndef lint +static const char rcsid[] = "$Id$"; +#endif + +#define PERM_SECURE (S_IRUSR|S_IWUSR) + +/* + * Open a DB database read/write + */ +DB *yp_open_db_rw(domain, map, flags) + 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(dbp,key,data,allow_overwrite) + 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..3dd4a0f --- /dev/null +++ b/libexec/ypxfr/ypxfr.8 @@ -0,0 +1,231 @@ +.\" Copyright (c) 1995 +.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Bill Paul. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id$ +.\" +.Dd February 5, 1995 +.Dt YPXFR 8 +.Os +.Sh NAME +.Nm ypxfr +.Nd "transfer NIS database from remote server to local host" +.Sh SYNOPSIS +.Nm ypxfr +.Op Fl f +.Op Fl c +.Op Fl d Ar target domain +.Op Fl h Ar source host +.Op Fl s Ar source domain +.Op Fl p Ar path +.Op Fl C Ar taskid program-number ipaddr port +.Ar mapname +.Sh DESCRIPTION +.Nm ypxfr +copies an NIS database (or +.Pa map ) +from one NIS server to another using NIS services. In FreeBSD, +.Nm ypxfr +is generally invoked by +.Xr ypserv 8 +when it receives a map transfer request from +.Xr yppush 8 . +.Nm ypxfr +is used primarily in environments where several NIS servers +are in use in a single domain. One server, the NIS master, maintains +the canonical copies of all NIS maps, and all the other servers, +the NIS slaves, copy new versions of the maps from the master whenever +any updates are made (i.e. when a user updates their password via +.Xr yppasswd 1 +). +.Pp +When run, +.Nm ypxfr +creates a temporary database file in +.Pa /var/yp/[domainmame] , +and fills it with the contents of +.Ar mapname +as supplied by the specified +.Ar source host . +When the entire map has been transfered, +.Nm ypxfr +deletes the original copy of +.Ar mapname +and moves the temporary copy into its place. When the transfer is +complete, +.Nm ypxfr +will attempt to send a 'clear current map' request to the local +.Xr ypserv 8 +process to clear any possible references it may still have to the +stale map. +.Pp +Note that all files created by +.Nm ypxfr +are owner readable and writable only for security reasons. Since the +NIS maps and the directory in which they reside are normally owned by +root, this prevents non-privleged users from making unauthorized +modifications. +.Pp +In order to maintain consistency across all NIS servers, +.Nm ypxfr +can be run periodically in a +.Xr cron 8 +job. Maps which change infrequently +need only be updated once a day (preferably late at night when system +usage is lowest), whereas those that are subject to frequent changes +(such a +.Pa passwd.byname +and +.Pa passwd.byuid ) +should be updated perhaps once every hour. Using +.Xr cron 8 +to automatically +update the NIS maps is not strictly mandatory since all updates should +be propagated by +.Xr yppush 8 +when +.Pa /var/yp/Makefile +is run on the NIS master server, however it is good practice +on large networks where possible outages could cause NIS servers to +fall out of sync with each other. +.Pp +When +.Nm ypxfr +is invoked without a controlling terminal, e.g. from inside +.Xr ypserv 8 , +it logs all its output using the +.Xr syslog 3 +facility. +.Sh NOTES +The FreeBSD version of +.Nm ypxfr +has support for a special map transfer protocol which works in +conjunction with the FreeBSD +.Xr rpc.ypxfrd 8 +server. This protocol allows it to transfer raw map database files from +the NIS master server and can be many times faster than the standard +transfer method, particularly for very large NIS maps. The +.Nm ypxfr +command will check to see if the +.Xr rpc.ypxfrd 8 +server is registered on the NIS master server and attempt to use +it if it is present. If it isn't 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 FreeBSD ypxfrd protocol is conceptually similar +to the SunOS ypxfrd protocol, FreeBSD's protocol is not compatible with +Sun's, therefore it will not work with Sun's ypxfrd server. FreeBSD +slave systems can still transfer maps from any non-FreeBSD NIS server, +however they will only be able to take advantage of the faster protocol +if the master server is also running FreeBSD. +.Sh OPTIONS +The following options and flags are supported by +.Nm ypxfr : +.Bl -tag -width flag +.It Fl f +Force a map transfer. Normally, +.Nm ypxfr +will not transfer a map if it determines that the NIS master's copy +is not newer than the existing copy already on the local host: the +.Fl f +flag forces a transfer regardless of which server's version is more recent. +.It Fl c +Do not send a 'clear current map' request to the +.Xr ypserv 8 +process running on the local host. This flag is normally used when +invoking +.Nm ypxfr +manually on a machine that is not yet running +.Xr ypserv 8 . +Without this flag, failure to contact the local NIS server will cause +.Nm ypxfr +to abort the transfer. +.It Fl d Ar target domain +Specify a target domain other than the current NIS domain. +.It Fl h Ar source host +Specify the name of the host from which to copy the NIS maps. This option +is used to insure that +.Nm ypxfr +only copies maps from the NIS master server. +.It Fl s Ar source domain +Specify the domain from which to transfer a map, in the event that +the transfer is being done across two different NIS domains. +.It Fl p Ar path +Specify the top level directory containing the NIS maps. By +default, this path is +.Pa /var/yp . +The +.Fl p +flag allows you to specify an alternate path should you wish to +store your NIS maps in a different part of the filesystem. The +NIS server, +.Xr ypserv 8 , +passes this flag to +.Nm ypxfr +if it too has been told to use an alternate path. +.It Fl C Ar taskid program-number ipaddr port +These options are used only when +.Nm ypxfr +is invoked by +.Xr ypserv 8 +in response to a map transfer request initiated by +.Xr yppush 8 . +In this instance, +.Nm ypxfr +needs to 'callback' to the +.Xr yppush 8 +process and interact with it, so +.Xr yppush 8 +passes to it an IP address +.Ar ipaddr , +port number +.Ar port , +registered program number +.Ar program-number +and a transaction ID +.Ar taskid +that it can use to contact the waiting +.Xr yppush 8 +process on the master server. +.It Ar mapname +The name of the map to transfer. +.El +.Sh FILES +.Bl -tag -width Pa -compact +.It Pa /var/yp/[domainname]/[maps] +The NIS maps for a particular NIS domain. +.El +.Sh SEE ALSO +.Xr yp 4 , +.Xr yppush 8 , +.Xr ypserv 8 +.Sh AUTHOR +Bill Paul <wpaul@ctr.columbia.edu> diff --git a/libexec/ypxfr/ypxfr_extern.h b/libexec/ypxfr/ypxfr_extern.h new file mode 100644 index 0000000..5ca370c --- /dev/null +++ b/libexec/ypxfr/ypxfr_extern.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ +#include <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 __P(( const char *, ... )); +extern int _yp_check __P(( char ** )); +extern char *ypxfrerr_string __P(( ypxfrstat )); +extern DB *yp_open_db_rw __P(( const char *, const char *, const int)); +extern void yp_init_dbs __P(( void )); +extern int yp_put_record __P(( DB *, DBT *, DBT * , int )); +extern int yp_get_record __P(( const char *, const char *, const DBT *, DBT *, int )); +extern int ypxfr_get_map __P(( char *, char *, char *, int (*)() )); +extern char *ypxfr_get_master __P(( char *, char *, char *, const int )); +extern unsigned long ypxfr_get_order __P(( char *, char *, char *, const int )); +extern int ypxfr_match __P(( char *, char *, char *, char *, unsigned long )); +extern char *ypxfxerr_string __P(( ypxfrstat )); +extern int ypxfrd_get_map __P(( char *, char *, char *, char *)); +extern int callrpc __P(( char *, int, int, int, xdrproc_t, char *, xdrproc_t, char *)); diff --git a/libexec/ypxfr/ypxfr_getmap.c b/libexec/ypxfr/ypxfr_getmap.c new file mode 100644 index 0000000..14939ae --- /dev/null +++ b/libexec/ypxfr/ypxfr_getmap.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ +#include <stdio.h> +#include <sys/types.h> +#include <time.h> +#include <rpc/rpc.h> +#include <rpc/xdr.h> +#include <rpcsvc/yp.h> +#include "ypxfr_extern.h" + +#ifndef lint +static const char rcsid[] = "$Id$"; +#endif + +extern bool_t xdr_ypresp_all_seq __P(( XDR *, unsigned long * )); + +int (*ypresp_allfn)(); +void *ypresp_data; +extern DB *specdbp; +extern int yp_errno; + +/* + * This is largely the same as yp_all() except we do the transfer + * from a specific server without the aid of ypbind(8). We need to + * be able to specify the source host explicitly since ypxfr may + * only transfer maps from the NIS master server for any given domain. + * However, if we use the libc version of yp_all(), we could end up + * talking to one of the slaves instead. We do need to dig into libc + * a little though, since it contains the magic XDR function we need. + */ +int ypxfr_get_map(map, domain, host, callback) + char *map; + char *domain; + char *host; + int (*callback)(); +{ + CLIENT *clnt; + ypreq_nokey req; + unsigned long status; + struct timeval timeout; + + timeout.tv_usec = 0; + timeout.tv_sec = 10; + + /* YPPROC_ALL is a TCP service */ + if ((clnt = clnt_create(host, YPPROG, YPVERS, "tcp")) == NULL) { + yp_error("%s", clnt_spcreateerror("failed to \ +create tcp handle")); + yp_errno = YPXFR_YPERR; + return(1); + } + + req.domain = domain; + req.map = map; + ypresp_allfn = callback; + ypresp_data = NULL; + + (void)clnt_call(clnt, YPPROC_ALL, xdr_ypreq_nokey, &req, + xdr_ypresp_all_seq, &status, timeout); + + clnt_destroy(clnt); + + if (status == YP_NOMORE) + return(0); + + if (status != YP_TRUE) { + yp_errno = YPXFR_YPERR; + return(1); + } + + return(0); +} diff --git a/libexec/ypxfr/ypxfr_main.c b/libexec/ypxfr/ypxfr_main.c new file mode 100644 index 0000000..6e42c05 --- /dev/null +++ b/libexec/ypxfr/ypxfr_main.c @@ -0,0 +1,567 @@ +/* + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ypxfr_main.c,v 1.8 1997/02/22 14:22:48 peter Exp $ + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <syslog.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <rpc/rpc.h> +#include <rpc/clnt.h> +#include <rpcsvc/yp.h> +struct dom_binding {}; +#include <rpcsvc/ypclnt.h> +#include <rpcsvc/ypxfrd.h> +#include "ypxfr_extern.h" + +#ifndef lint +static const char rcsid[] = "$Id: ypxfr_main.c,v 1.8 1997/02/22 14:22:48 peter Exp $"; +#endif + +char *progname = "ypxfr"; +char *yp_dir = _PATH_YP; +int _rpcpmstart = 0; +int ypxfr_use_yplib = 0; /* Assume the worst. */ +int ypxfr_clear = 1; +int ypxfr_prognum = 0; +struct sockaddr_in ypxfr_callback_addr; +struct yppushresp_xfr ypxfr_resp; +DB *dbp; + +static void ypxfr_exit(retval, temp) + ypxfrstat retval; + char *temp; +{ + CLIENT *clnt; + int sock = RPC_ANYSOCK; + struct timeval timeout; + + /* Clean up no matter what happened previously. */ + if (temp != NULL) { + if (dbp != NULL) + (void)(dbp->close)(dbp); + if (unlink(temp) == -1) { + yp_error("failed to unlink %s",strerror(errno)); + } + } + + if (_rpcpmstart) { + timeout.tv_sec = 20; + timeout.tv_usec = 0; + + if ((clnt = clntudp_create(&ypxfr_callback_addr, ypxfr_prognum, + 1, timeout, &sock)) == NULL) { + yp_error("%s", clnt_spcreateerror("failed to \ +establish callback handle")); + exit(1); + } + + ypxfr_resp.status = retval; + + if (yppushproc_xfrresp_1(&ypxfr_resp, clnt) == NULL) { + yp_error("%s", clnt_sperror(clnt, "callback failed")); + clnt_destroy(clnt); + exit(1); + } + clnt_destroy(clnt); + } else { + yp_error("Exiting: %s", ypxfrerr_string(retval)); + } + + exit(0); +} + +static void usage() +{ + if (_rpcpmstart) { + ypxfr_exit(YPXFR_BADARGS,NULL); + } else { + fprintf(stderr,"usage: %s [-f] [-c] [-d target domain] \ +[-h source host] [-s source domain]\n", progname); + fprintf(stderr,"\t [-p path] [-C taskid program-number \ +ipaddr port] mapname\n"); + exit(1); + } +} + +int ypxfr_foreach(status, key, keylen, val, vallen, data) + int status; + char *key; + int keylen; + char *val; + int vallen; + char *data; +{ + DBT dbkey, dbval; + + if (status != YP_TRUE) + return (status); + + dbkey.data = key; + dbkey.size = keylen; + dbval.data = val; + dbval.size = vallen; + + if (yp_put_record(dbp, &dbkey, &dbval, 0) != YP_TRUE) + return(yp_errno); + + return (0); +} + +main(argc,argv) + int argc; + char *argv[]; +{ + int ch; + int ypxfr_force = 0; + char *ypxfr_dest_domain = NULL; + char *ypxfr_source_host = NULL; + char *ypxfr_source_domain = NULL; + char *ypxfr_local_domain = NULL; + char *ypxfr_master = NULL; + unsigned long ypxfr_order = -1, ypxfr_skew_check = -1; + char *ypxfr_mapname = NULL; + int ypxfr_args = 0; + char ypxfr_temp_map[MAXPATHLEN + 2]; + char tempmap[MAXPATHLEN + 2]; + char buf[MAXPATHLEN + 2]; + DBT key, data; + int remoteport; + int interdom = 0; + int secure = 0; + + debug = 1; + + if (!isatty(fileno(stderr))) { + openlog(progname, LOG_PID, LOG_DAEMON); + _rpcpmstart = 1; + } + + if (argc < 2) + usage(); + + while ((ch = getopt(argc, argv, "fcd:h:s:p:C:")) != -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(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(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), "%d", 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(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, + xdr_void, (void *)&in, + 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..aaaff67 --- /dev/null +++ b/libexec/ypxfr/ypxfr_misc.c @@ -0,0 +1,305 @@ +/* + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/param.h> +#include <rpc/rpc.h> +#include <rpcsvc/yp.h> +struct dom_binding {}; +#include <rpcsvc/ypclnt.h> +#include "ypxfr_extern.h" + +#ifndef lint +static const char rcsid[] = "$Id$"; +#endif + +char *ypxfrerr_string(code) + ypxfrstat code; +{ + switch(code) { + case YPXFR_SUCC: + return ("Map successfully transfered"); + break; + case YPXFR_AGE: + return ("Master's version not newer"); + break; + case YPXFR_NOMAP: + return ("No such map in server's domain"); + break; + case YPXFR_NODOM: + return ("Domain not supported by server"); + break; + case YPXFR_RSRC: + return ("Local resource allocation failure"); + break; + case YPXFR_RPC: + return ("RPC failure talking to server"); + break; + case YPXFR_MADDR: + return ("Could not get master server address"); + break; + case YPXFR_YPERR: + return ("NIS server/map database error"); + break; + case YPXFR_BADARGS: + return ("Request arguments bad"); + break; + case YPXFR_DBM: + return ("Local database operation failed"); + break; + case YPXFR_FILE: + return ("Local file I/O operation failed"); + break; + case YPXFR_SKEW: + return ("Map version skew during transfer"); + break; + case YPXFR_CLEAR: + return ("Couldn't send \"clear\" request to local ypserv"); + break; + case YPXFR_FORCE: + return ("No local order number in map -- use -f flag"); + break; + case YPXFR_XFRERR: + return ("General ypxfr error"); + break; + case YPXFR_REFUSED: + return ("Transfer request refused by ypserv"); + break; + default: + return ("Unknown error code"); + break; + } +} + +/* + * These are wrappers for the usual yp_master() and yp_order() functions. + * They can use either local yplib functions (the real yp_master() and + * yp_order()) or do direct RPCs to a specified server. The latter is + * necessary if ypxfr is run on a machine that isn't configured as an + * NIS client (this can happen very easily: a given machine need not be + * an NIS client in order to be an NIS server). + */ + +/* + * 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(domain,map,source,yplib) + 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 = YPXFR_NODOM; + break; + case YPERR_MAP: + yp_errno = YPXFR_NOMAP; + break; + case YPERR_YPERR: + default: + yp_errno = 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 = YPXFR_RPC; + return(NULL); + } + + req.map = map; + req.domain = domain; + if ((resp = ypproc_master_2(&req, clnt)) == NULL) { + yp_error("%s",clnt_sperror(clnt,"YPPROC_MASTER \ +failed")); + clnt_destroy(clnt); + yp_errno = YPXFR_RPC; + return(NULL); + } + clnt_destroy(clnt); + if (resp->stat != YP_TRUE) { + switch (resp->stat) { + case YP_NODOM: + yp_errno = YPXFR_NODOM; + break; + case YP_NOMAP: + yp_errno = YPXFR_NOMAP; + break; + case YP_YPERR: + default: + yp_errno = YPXFR_YPERR; + break; + } + return(NULL); + } + snprintf(mastername, sizeof(mastername), "%s", resp->peer); +/* xdr_free(xdr_ypresp_master, (char *)&resp); */ + return((char *)&mastername); + } +} + +unsigned long ypxfr_get_order(domain, map, source, yplib) + char *domain; + char *map; + char *source; + const int yplib; +{ + if (yplib) { + unsigned long order; + int res; + if ((res = yp_order(domain, map, (int *)&order))) { + switch (res) { + case YPERR_DOMAIN: + yp_errno = YPXFR_NODOM; + break; + case YPERR_MAP: + yp_errno = YPXFR_NOMAP; + break; + case YPERR_YPERR: + default: + yp_errno = YPXFR_YPERR; + break; + } + return(0); + } else + return(order); + } else { + CLIENT *clnt; + ypresp_order *resp; + ypreq_nokey req; + + if ((clnt = clnt_create(source,YPPROG,YPVERS,"udp")) == NULL) { + yp_error("%s",clnt_spcreateerror("couldn't create \ +udp handle to ypserv")); + yp_errno = YPXFR_RPC; + return(0); + } + req.map = map; + req.domain = domain; + if ((resp = ypproc_order_2(&req, clnt)) == NULL) { + yp_error("%s", clnt_sperror(clnt, "YPPROC_ORDER \ +failed")); + clnt_destroy(clnt); + yp_errno = YPXFR_RPC; + return(0); + } + clnt_destroy(clnt); + if (resp->stat != YP_TRUE) { + switch (resp->stat) { + case YP_NODOM: + yp_errno = YPXFR_NODOM; + break; + case YP_NOMAP: + yp_errno = YPXFR_NOMAP; + break; + case YP_YPERR: + default: + yp_errno = YPXFR_YPERR; + break; + } + return(0); + } + return(resp->ordernum); + } +} + +int ypxfr_match(server, domain, map, key, keylen) + 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((char *)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(xdr_ypresp_val, (char *)ypval); + return(0); + } + + xdr_free(xdr_ypresp_val, (char *)ypval); + + return(1); +} diff --git a/libexec/ypxfr/ypxfrd_getmap.c b/libexec/ypxfr/ypxfrd_getmap.c new file mode 100644 index 0000000..3844beb --- /dev/null +++ b/libexec/ypxfr/ypxfrd_getmap.c @@ -0,0 +1,147 @@ +/* + * 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. + * + * $Id$ + */ + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +#include <time.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 <errno.h> +#include "ypxfr_extern.h" + +#ifndef lint +static const char rcsid[] = "$Id$"; +#endif + +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(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(host, map, domain, tmpname) + 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,xdr_ypxfr_mapname,(char *)&req, + 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); +} |