diff options
Diffstat (limited to 'libexec')
217 files changed, 48347 insertions, 0 deletions
diff --git a/libexec/Makefile b/libexec/Makefile new file mode 100644 index 0000000..3a1d910 --- /dev/null +++ b/libexec/Makefile @@ -0,0 +1,23 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +SUBDIR= atrun bootpd bootpgw comsat fingerd ftpd getNAME getty lfs_cleanerd \ + mail.local makekey revnetgroup rexecd rlogind rpc.rstatd rpc.rusersd \ + rpc.rwalld rshd talkd tftpd uucpd xtend + +.if !exists(../secure) || defined(NOSECURE) +SUBDIR+=telnetd +.else +SUBDIR+= ../secure/libexec/telnetd +.endif + +# Present but disabled: kpasswdd + +.if ${MACHINE} == "hp300" +SUBDIR+=rbootd +.elif ${MACHINE} == "i386" +SUBDIR+=rbootd +.elif ${MACHINE} == "luna68k" +SUBDIR+=rbootd +.endif + +.include <bsd.subdir.mk> diff --git a/libexec/Makefile.inc b/libexec/Makefile.inc new file mode 100644 index 0000000..f9922b6 --- /dev/null +++ b/libexec/Makefile.inc @@ -0,0 +1,3 @@ +# @(#)Makefile.inc 8.1 (Berkeley) 6/4/93 + +BINDIR?= /usr/libexec diff --git a/libexec/atrun/LEGAL b/libexec/atrun/LEGAL new file mode 100644 index 0000000..92b1b49 --- /dev/null +++ b/libexec/atrun/LEGAL @@ -0,0 +1,29 @@ +-----BEGIN PGP SIGNED MESSAGE----- + +Sorry for the long wait, but there still were a few things to +be ironed out in at, which I've finally done :-) + +The FreeBSD team does have my permission to use at, version 2.9, +under the BSD license. + +You'll find it on sunsite.unc.edu's Incoming, hopefully; the +md5 checksum is + +3ba2ca3c0e87e1a04feae2c6c1376b0d at-2.9.tgz + +Best regards + Thomas +- -- +Thomas Koenig, Thomas.Koenig@ciw.uni-karlsruhe.de, ig25@dkauni2.bitnet. +The joy of engineering is to find a straight line on a double +logarithmic diagram. + +-----BEGIN PGP SIGNATURE----- +Version: 2.6.2i + +iQCVAwUBMCjVrPBu+cbJcKCVAQFNiQP/dpWP57s/E8plVGUD3zfgOXDmKUvg8U7a +VwRzJrIMuSgnSJs0wkpvcomc3NLicipfX7hhWLh/xatPM2YbF7O5HZoNdvWvexD2 +1Y67zJ+0HFb1mPnSBOrS5RFiQAe3KqmGec6E14Rih/qNoFQZBVRFXZ4xxuwP+0Rs +e2U+TVTUz6A= +=TvyW +-----END PGP SIGNATURE----- diff --git a/libexec/atrun/Makefile b/libexec/atrun/Makefile new file mode 100644 index 0000000..5e47abf --- /dev/null +++ b/libexec/atrun/Makefile @@ -0,0 +1,26 @@ +# $Id: Makefile,v 1.3 1995/08/21 12:34:16 ache Exp $ + +MAINSRC= ${.CURDIR}/../../usr.bin/at + +.include "$(MAINSRC)/Makefile.inc" + +PROG= atrun +MAN8= atrun.8 +SRCS= atrun.c gloadavg.c + +BINDIR= $(ATLIB_DIR) +MANSRC= . +CLEANFILES += ${MAN8} +MANDEPEND = ${MAN8} + +CFLAGS+= -I$(MAINSRC) -I${.CURDIR} + +${MAN8}: atrun.man + sed -e \ + "s@_ATSPOOL_DIR@$(ATSPOOL_DIR)@g; \ + s@_ATJOB_DIR@$(ATJOB_DIR)@g; \ + s@_ATLIB_DIR@$(ATLIB_DIR)@g; \ + s@_LOADAVG_MX@$(LOADAVG_MX)@g;" \ + < $? > $@ + +.include <bsd.prog.mk> diff --git a/libexec/atrun/atrun.8 b/libexec/atrun/atrun.8 new file mode 100644 index 0000000..ed6e359 --- /dev/null +++ b/libexec/atrun/atrun.8 @@ -0,0 +1,77 @@ +.\" +.\" Copyright (c) 1993 Christopher G. Demetriou +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Christopher G. Demetriou. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software withough specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $Id: atrun.8,v 1.1 1994/01/05 01:02:58 nate Exp $ +.\" +.Dd December 5, 1993 +.Dt ATRUN 8 +.Os FreeBSD 1.1 +.Sh NAME +.Nm atrun +.Nd run jobs queued for later execution +.\" +.Sh SYOPSIS +.Nm atrun +.Sh DESCRIPTION +The +.Nm atrun +utility runs commands queued by +.Xr at 1 . +It is usually invoked by +.Xr crond 8 +every ten minutes. +.Sh FILES +.Bl -tag -width /var/at/lockfile -compact +.It Pa /var/at/jobs +Directory containing job files +.It Pa /var/at/spool +Directory containing output spool files +.It Pa /var/at/lockfile +Job-creation lock file. +.El +.Sh SEE ALSO +.Xr crond 8 , +.Xr at 1 +.Sh AUTHOR +.Bl -tag +Thomas Koenig, ig25@rz.uni-karlsruhe.de +.El +.Sh BUGS +The functionality of +.Nm atrun +should arguaby be merged into +.Xr crond 8 . +.Sh CAVEATS +Since the default configuration causes +.Nm atrun +to be invoked every ten minutes, +commands queued by +.Xr at 1 +may end up being executed up to nine minutes +later than would be otherwise expected. diff --git a/libexec/atrun/atrun.c b/libexec/atrun/atrun.c new file mode 100644 index 0000000..8ff1bfa --- /dev/null +++ b/libexec/atrun/atrun.c @@ -0,0 +1,479 @@ +/* + * atrun.c - run jobs queued by at; run with root privileges. + * Copyright (C) 1993, 1994 Thomas Koenig + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author(s) may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* System Headers */ + +#include <sys/fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <pwd.h> +#include <grp.h> +#include <signal.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <syslog.h> +#ifdef __FreeBSD__ +#include <paths.h> +#else +#include <getopt.h> +#endif + +/* Local headers */ + +#include "gloadavg.h" +#define MAIN +#include "privs.h" + +/* Macros */ + +#ifndef ATJOB_DIR +#define ATJOB_DIR "/usr/spool/atjobs/" +#endif + +#ifndef ATSPOOL_DIR +#define ATSPOOL_DIR "/usr/spool/atspool/" +#endif + +#ifndef LOADAVG_MX +#define LOADAVG_MX 1.5 +#endif + +/* File scope variables */ + +static char *namep; +static char rcsid[] = "$Id: atrun.c,v 1.5 1995/08/21 12:34:17 ache Exp $"; +static debug = 0; + +void perr(const char *a); + +/* Local functions */ +static int +write_string(int fd, const char* a) +{ + return write(fd, a, strlen(a)); +} + +#undef DEBUG_FORK +#ifdef DEBUG_FORK +static pid_t +myfork() +{ + pid_t res; + res = fork(); + if (res == 0) + kill(getpid(),SIGSTOP); + return res; +} + +#define fork myfork +#endif + +static void +run_file(const char *filename, uid_t uid, gid_t gid) +{ +/* Run a file by by spawning off a process which redirects I/O, + * spawns a subshell, then waits for it to complete and sends + * mail to the user. + */ + pid_t pid; + int fd_out, fd_in; + int queue; + char mailbuf[9]; + char *mailname = NULL; + FILE *stream; + int send_mail = 0; + struct stat buf, lbuf; + off_t size; + struct passwd *pentry; + int fflags; + long nuid; + long ngid; + + + PRIV_START + + if (chmod(filename, S_IRUSR) != 0) + { + perr("Cannot change file permissions"); + } + + PRIV_END + + pid = fork(); + if (pid == -1) + perr("Cannot fork"); + + else if (pid != 0) + return; + + /* Let's see who we mail to. Hopefully, we can read it from + * the command file; if not, send it to the owner, or, failing that, + * to root. + */ + + pentry = getpwuid(uid); + if (pentry == NULL) + { + syslog(LOG_ERR,"Userid %lu not found - aborting job %s", + (unsigned long) uid, filename); + exit(EXIT_FAILURE); + } + PRIV_START + + stream=fopen(filename, "r"); + + PRIV_END + +#ifdef __FreeBSD__ + if (pentry->pw_expire && time(NULL) >= pentry->pw_expire) + { + syslog(LOG_ERR, "Userid %lu is expired - aborting job %s", + (unsigned long) uid, filename); + exit(EXIT_FAILURE); + } +#endif + + if (stream == NULL) + perr("Cannot open input file"); + + if ((fd_in = dup(fileno(stream))) <0) + perr("Error duplicating input file descriptor"); + + if (fstat(fd_in, &buf) == -1) + perr("Error in fstat of input file descriptor"); + + if (lstat(filename, &lbuf) == -1) + perr("Error in fstat of input file"); + + if (S_ISLNK(lbuf.st_mode)) { + syslog(LOG_ERR,"Symbolic link encountered in job %s - aborting", + filename); + exit(EXIT_FAILURE); + } + if ((lbuf.st_dev != buf.st_dev) || (lbuf.st_ino != buf.st_ino) || + (lbuf.st_uid != buf.st_uid) || (lbuf.st_gid != buf.st_gid) || + (lbuf.st_size!=buf.st_size)) { + syslog(LOG_ERR,"Somebody changed files from under us for job %s - " + "aborting",filename); + exit(EXIT_FAILURE); + } + if (buf.st_nlink > 1) { + syslog(LOG_ERR,"Someboy is trying to run a linked script for job %s", + filename); + exit(EXIT_FAILURE); + } + if ((fflags = fcntl(fd_in, F_GETFD)) <0) + perr("Error in fcntl"); + + fcntl(fd_in, F_SETFD, fflags & ~FD_CLOEXEC); + + if (fscanf(stream, "#!/bin/sh\n# atrun uid=%ld gid=%ld\n# mail %8s %d", + &nuid, &ngid, mailbuf, &send_mail) != 4) + { + syslog(LOG_ERR,"File %s is in wrong format - aborting", + filename); + exit(EXIT_FAILURE); + } + if (mailbuf[0] == '-') { + syslog(LOG_ERR,"illegal mail name %s in %s",mailbuf,filename); + exit(EXIT_FAILURE); + } + mailname = mailbuf; + if (nuid != uid) { + syslog(LOG_ERR,"Job %s - userid %d does not match file uid %d", + filename, nuid, uid); + exit(EXIT_FAILURE); + } + if (ngid != gid) { + syslog(LOG_ERR,"Job %s - groupid %d does not match file gid %d", + filename, ngid, gid); + exit(EXIT_FAILURE); + } + fclose(stream); + if (chdir(ATSPOOL_DIR) < 0) + perr("Cannot chdir to " ATSPOOL_DIR); + + /* Create a file to hold the output of the job we are about to run. + * Write the mail header. + */ + if((fd_out=open(filename, + O_WRONLY | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR)) < 0) + perr("Cannot create output file"); + + write_string(fd_out, "Subject: Output from your job "); + write_string(fd_out, filename); + write_string(fd_out, "\n\n"); + fstat(fd_out, &buf); + size = buf.st_size; + + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + + pid = fork(); + if (pid < 0) + perr("Error in fork"); + + else if (pid == 0) + { + char *nul = NULL; + char **nenvp = &nul; + + /* Set up things for the child; we want standard input from the input file, + * and standard output and error sent to our output file. + */ + + if (lseek(fd_in, (off_t) 0, SEEK_SET) < 0) + perr("Error in lseek"); + + if (dup(fd_in) != STDIN_FILENO) + perr("Error in I/O redirection"); + + if (dup(fd_out) != STDOUT_FILENO) + perr("Error in I/O redirection"); + + if (dup(fd_out) != STDERR_FILENO) + perr("Error in I/O redirection"); + + close(fd_in); + close(fd_out); + if (chdir(ATJOB_DIR) < 0) + perr("Cannot chdir to " ATJOB_DIR); + + queue = *filename; + + PRIV_START + + nice(tolower(queue) - 'a'); + + if (chdir(pentry->pw_dir)) + chdir("/"); + + if (initgroups(pentry->pw_name,pentry->pw_gid)) + perr("Cannot delete saved userids"); + + if (setgid(gid) < 0) + perr("Cannot change group"); + + if (setuid(uid) < 0) + perr("Cannot set user id"); + + if(execle("/bin/sh","sh",(char *) NULL, nenvp) != 0) + perr("Exec failed for /bin/sh"); + + PRIV_END + } + /* We're the parent. Let's wait. + */ + close(fd_in); + close(fd_out); + waitpid(pid, (int *) NULL, 0); + + /* Send mail. Unlink the output file first, so it is deleted after + * the run. + */ + stat(filename, &buf); + if (open(filename, O_RDONLY) != STDIN_FILENO) + perr("Open of jobfile failed"); + + unlink(filename); + if ((buf.st_size != size) || send_mail) + { + PRIV_START + + if (chdir(pentry->pw_dir)) + chdir("/"); + + if (initgroups(pentry->pw_name,pentry->pw_gid)) + perr("Cannot delete saved userids"); + + if (setgid(gid) < 0) + perr("Cannot change group"); + + if (setuid(uid) < 0) + perr("Cannot set user id"); + +#ifdef __FreeBSD__ + execl(_PATH_SENDMAIL, "sendmail", "-F", "Atrun Service", + "-odi", "-oem", + mailname, (char *) NULL); +#else + execl(MAIL_CMD, MAIL_CMD, mailname, (char *) NULL); +#endif + perr("Exec failed for mail command"); + + PRIV_END + } + exit(EXIT_SUCCESS); +} + +/* Global functions */ + +/* Needed in gloadavg.c */ +void +perr(const char *a) +{ + if (debug) + { + perror(a); + } + else + syslog(LOG_ERR, "%s: %m", a); + + exit(EXIT_FAILURE); +} + +int +main(int argc, char *argv[]) +{ +/* Browse through ATJOB_DIR, checking all the jobfiles wether they should + * be executed and or deleted. The queue is coded into the first byte of + * the job filename, the date (in minutes since Eon) as a hex number in the + * following eight bytes, followed by a dot and a serial number. A file + * which has not been executed yet is denoted by its execute - bit set. + * For those files which are to be executed, run_file() is called, which forks + * off a child which takes care of I/O redirection, forks off another child + * for execution and yet another one, optionally, for sending mail. + * Files which already have run are removed during the next invocation. + */ + DIR *spool; + struct dirent *dirent; + struct stat buf; + unsigned long ctm; + unsigned long jobno; + char queue; + time_t now, run_time; + char batch_name[] = "Z2345678901234"; + uid_t batch_uid; + gid_t batch_gid; + int c; + int run_batch; + double load_avg = LOADAVG_MX; + +/* We don't need root privileges all the time; running under uid and gid daemon + * is fine. + */ + + RELINQUISH_PRIVS_ROOT(DAEMON_UID, DAEMON_GID) + + openlog("atrun", LOG_PID, LOG_CRON); + + opterr = 0; + errno = 0; + while((c=getopt(argc, argv, "dl:"))!= EOF) + { + switch (c) + { + case 'l': + if (sscanf(optarg, "%lf", &load_avg) != 1) + perr("garbled option -l"); + if (load_avg <= 0.) + load_avg = LOADAVG_MX; + break; + + case 'd': + debug ++; + break; + + case '?': + perr("unknown option"); + break; + + default: + perr("idiotic option - aborted"); + break; + } + } + + namep = argv[0]; + if (chdir(ATJOB_DIR) != 0) + perr("Cannot change to " ATJOB_DIR); + + /* Main loop. Open spool directory for reading and look over all the + * files in there. If the filename indicates that the job should be run + * and the x bit is set, fork off a child which sets its user and group + * id to that of the files and exec a /bin/sh which executes the shell + * script. Unlink older files if they should no longer be run. For + * deletion, their r bit has to be turned on. + * + * Also, pick the oldest batch job to run, at most one per invocation of + * atrun. + */ + if ((spool = opendir(".")) == NULL) + perr("Cannot read " ATJOB_DIR); + + now = time(NULL); + run_batch = 0; + batch_uid = (uid_t) -1; + batch_gid = (gid_t) -1; + + while ((dirent = readdir(spool)) != NULL) { + if (stat(dirent->d_name,&buf) != 0) + perr("Cannot stat in " ATJOB_DIR); + + /* We don't want directories + */ + if (!S_ISREG(buf.st_mode)) + continue; + + if (sscanf(dirent->d_name,"%c%5lx%8lx",&queue,&jobno,&ctm) != 3) + continue; + + run_time = (time_t) ctm*60; + + if ((S_IXUSR & buf.st_mode) && (run_time <=now)) { + if (isupper(queue) && (strcmp(batch_name,dirent->d_name) > 0)) { + run_batch = 1; + strncpy(batch_name, dirent->d_name, sizeof(batch_name)); + batch_uid = buf.st_uid; + batch_gid = buf.st_gid; + } + + /* The file is executable and old enough + */ + if (islower(queue)) + run_file(dirent->d_name, buf.st_uid, buf.st_gid); + } + /* Delete older files + */ + if ((run_time < now) && !(S_IXUSR & buf.st_mode) && (S_IRUSR & buf.st_mode)) + unlink(dirent->d_name); + } + /* run the single batch file, if any + */ + if (run_batch && (gloadavg() < load_avg)) + run_file(batch_name, batch_uid, batch_gid); + + closelog(); + exit(EXIT_SUCCESS); +} diff --git a/libexec/atrun/atrun.h b/libexec/atrun/atrun.h new file mode 100644 index 0000000..be0588a --- /dev/null +++ b/libexec/atrun/atrun.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 1993 Christopher G. Demetriou + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christopher G. Demetriou. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: atrun.h,v 1.1 1993/12/05 11:36:45 cgd Exp $ + */ + +#define ATRUN_MAXLOAD 1.5 diff --git a/libexec/atrun/atrun.man b/libexec/atrun/atrun.man new file mode 100644 index 0000000..abce667 --- /dev/null +++ b/libexec/atrun/atrun.man @@ -0,0 +1,66 @@ +.\" $Id: atrun.man,v 1.1 1994/05/10 18:23:08 kernel Exp $ +.Dd April 12, 1995 +.Dt ATRUN 8 +.Os "FreeBSD 2.1" +.Sh NAME +.Nm atrun +.Nd run jobs queued for later execution +.Sh SYNOPSIS +.Nm atrun +.Op Fl l Ar load_avg +.Op Fl d +.Sh DESCRIPTION +.Nm Atrun +runs jobs queued by +.Xr at 1 . +Root's +.Xr crontab 5 +file +.Pa /etc/crontab +has to contain the line +.nf +*/5 * * * * root _ATLIB_DIR/atrun +.fi +so +.Xr atrun 8 +gets called every five minutes. +.Pp +At every invocation, every job in lowercase queues whose starting time +has passed is started. +A maximum of one batch jobs (denoted by uppercase queues) are started +each time +.Nm atrun +is invoked. +.Sh OPTIONS +.Bl -tag -width indent +.It Fl l Ar load_avg +Specifies a limiting load factor, over which batch jobs should +not be run, instead of the compiled \- in value of _LOADAVG_MX. +.It Fl d +Debug; print error messages to standard error instead of using +.Xr syslog 3 . +.El +.Sh WARNINGS +For +.Nm atrun +to work, you have to start up a +.Xr cron 8 +daemon. +.Sh FILES +.Bl -tag -width _ATSPOOL_DIR -compact +.It Pa _ATSPOOL_DIR +Directory containing output spool files +.It Pa _ATJOB_DIR +Directory containing job files +.El +.Sh SEE ALSO +.Xr cron 8 , +.Xr crontab 1 , +.Xr crontab 5 , +.Xr at 1 , +.Xr syslog 3 . +.Sh BUGS +The functionality of +.Nm atrun +should be merged into +.Xr cron 8 . diff --git a/libexec/atrun/gloadavg.c b/libexec/atrun/gloadavg.c new file mode 100644 index 0000000..6e996f0 --- /dev/null +++ b/libexec/atrun/gloadavg.c @@ -0,0 +1,69 @@ +/* + * gloadavg.c - get load average for Linux + * Copyright (C) 1993 Thomas Koenig + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author(s) may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __FreeBSD__ +#define _POSIX_SOURCE 1 + +/* System Headers */ + +#include <stdio.h> +#else +#include <stdlib.h> +#endif + +/* Local headers */ + +#include "gloadavg.h" + +/* File scope variables */ + +static char rcsid[] = "$Id: gloadavg.c,v 1.2 1995/08/10 04:06:54 ache Exp $"; + +/* Global functions */ + +double +gloadavg(void) +/* return the current load average as a floating point number, or <0 for + * error + */ +{ + double result; +#ifndef __FreeBSD__ + FILE *fp; + + if((fp=fopen(PROC_DIR "loadavg","r")) == NULL) + result = -1.0; + else + { + if(fscanf(fp,"%lf",&result) != 1) + result = -1.0; + fclose(fp); + } +#else + if (getloadavg(&result, 1) != 1) + perr("Error in getloadavg"); +#endif + return result; +} diff --git a/libexec/atrun/gloadavg.h b/libexec/atrun/gloadavg.h new file mode 100644 index 0000000..c7e2c2c --- /dev/null +++ b/libexec/atrun/gloadavg.h @@ -0,0 +1,29 @@ +/* + * gloadavg.h - header for atrun(8) + * Copyright (C) 1993 Thomas Koenig + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author(s) may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +double gloadavg(void); +#if 0 +static char atrun_h_rcsid[] = "$Id: gloadavg.h,v 1.2 1995/08/10 04:06:55 ache Exp $"; +#endif diff --git a/libexec/bootpd/Announce b/libexec/bootpd/Announce new file mode 100644 index 0000000..e4ae04c --- /dev/null +++ b/libexec/bootpd/Announce @@ -0,0 +1,63 @@ + +This is an enhanced version of the CMU BOOTP server which was derived +from the original BOOTP server created by Bill Croft at Stanford. +This version merges most of the enhancements and bug-fixes from the +NetBSD, Columbia, and other versions. + +New features in version 2.4 include: + + Added a simple BOOTP gateway program: bootpgw + Allow host name anywhere IP address is expected. + Automatically lookup the IP address when the name of a + bootptab entry is a valid hostname. + (Dummy entries names should start with '.') + Merged changes from NetBSD and Columbia versions. + Merged changes for Solaris-2.X and SVR4 systems. + Combined bootptest into the bootp release. + Merged tag 18 support (:ef=...:) from Jason Zions. + Use :ef=extension_file_name: and make the + extension files for all clients using bootpef. + Merged HP compatibility (:ra=...:) from David R Linn. + Allows you to override the reply address. + (i.e. send the reply to a broadcast address) + Add /etc/ethers support for NetBSD. + More systems support getether (Ultrix, OSF, NetBSD) + Added RFC 1533 tags 40,41,42 + :yd=<NIS domain>:ys=<NIS server>:nt=<NTP server>: + ConvOldTab.sh to convert old (1.1) bootptab to new format. + Permits extended-length replies with more option data. + +Problems fixed in this version: + + Fixed references to free host structures. + (used to cause core dump on Solaris) + Remove change that added null terminator to string options. + (this annoyed some clients...) + Add missing symbols to dump routine, fix order. + Works (again) with no -DSYSLOGD defined. + Fixed several more NULL references in readfile. + Added proper length checks to option insertions. + Fixed bootptest IP address printing. + Cleaned-up signed/unsigned and byteorder bugs. + Added SVR4/Streams support to getif and getether + Removed extra newlines in syslog messages. + Specify facility code when calling syslog(3) + When lookup_hwa fails, assume numeric HW address. + +Systems on which I have seen this code work: + SunOS 4.X (Solaris 1.X) + SunOS 5.X (Solaris 2.X) + System V/386 Rel. 4.0 + +Systems on which others say this code works: + CDC EP/IX (1.4.3, 2.1.1) + DEC Ultrix (4.2, 4.3) + NetBSD (Current-8/94) + OSF/1 (DEC Alpha CPU) + +Please direct questions, comments, and bug reports to: + <bootp@andrew.cmu.edu> + +Gordon W. Ross Mercury Computer Systems +gwr@mc.com 199 Riverneck Road +508-256-1300 Chelmsford, MA 01824-2820 diff --git a/libexec/bootpd/Changes b/libexec/bootpd/Changes new file mode 100644 index 0000000..0616548 --- /dev/null +++ b/libexec/bootpd/Changes @@ -0,0 +1,245 @@ +Changes, most recent first +Date, <email> Real Name + what... + +--> bootp-2.4.0 + +08/20/94 gwr@mc.com (Gordon W. Ross) + Fix code to build bootfile name based on combination of + client requested name and bootfile specifications. + Behave similarly with or without CHECK_FILE_ACCESS. + +07/30/94 Dirk Koeppen <dirk@incom.de> + Add "min wait" option (mw) to cause bootpd to ignore + requests from clients that have not waited long enough. + Add code to honor client requests containing the DHCP + option "Maximum Message Size" and use its value to + determine the size of the reply message. + +--> bootp-2.3.8 + +06/25/94 Christos Zoulas <christos@deshaw.com> + Add "-h" flag to override host name (affects default IP + address provided in reply messages. (Also minor bug fix) + +05/27/94 gwr@mc.com (Gordon W. Ross) + Add code to call "arp -s IPADDR HWADDR" on systems + that do not provide an SIOCSARP ioctl (i.e. NetBSD) + +--> bootp-2.3.7 + +05/05/94 Walter Wong <wcw+@CMU.EDU> + Reduce noize at debug level one, where log messages + are generated only for hosts that are recognized + and replied to by bootpd. (At request of HP folks.) + +04/30/94 gwr@mc.com (Gordon W. Ross) + Use memxxx functions unless USE_BFUNCS is defined. + Added -f <file> option to bootptest (requested file). + +04/29/94 tpaquett@ita.lgc.com (Trevor Paquette) + Remove call to haddr_conv802() in sendreply(). + The setarp should get the non-transformed address. + +04/27/94 gwr@mc.com + Improve logic for building bootfile pathname, so a path + will be put in the reply if either the client or bootpd + specifies a boot file. (Needed for NetBSD diskless boot) + +04/25/94 shamash@boxhill.com (Ari Shamash) + Fix prs_inetaddr() so it allows '_' in hostnames. + +04/16/94 gwr@mc.com (Gordon W. Ross) + Fix setarp for SVR4 (needs to use I_STR ioctl) + Thanks to several people: (all sent the same fix) + Barney Wolff <barney@databus.com>, + bear@upsys.se (Bj|rn Sj|holm), + Michael Kuschke <Michael.Kuschke@Materna.DE>, + +03/25/95 Ulrich Heuer </I=zhhi9/G=Ulrich/S=Heuer/@zhflur.ubs.ubs.ch> + Make option string lengths not include a null terminator. + The trailing null breaks some clients. + +03/15/94 "Edmund J. Sutcliffe" <ejs1@tower.york.ac.uk> + Add support for the "EX" option: Execute a program + before sending a BOOTREPLY to a client. Support for + this option is conditional on YORK_EX_OPTION. + +03/10/94 Nigel Metheringham <nigelm@ohm.york.ac.uk> + Make getether.c work on Linux. + +03/09/94 Koch@Math.Uni-Duisburg.DE (Peter Koch) + Add missing MANDIR definition to Makefile. + +03/08/94 Jeroen.Scheerder@let.ruu.nl + Fix args to report in getether code for Ultrix. + Run install individually for each program. + +--> bootp-2.3.6 +03/07/94 gwr@mc.com + Cleanup for release (run gnu indent, tab-size=4) + +02/24/94 Jeroen.Scheerder@let.ruu.nl + Allow underscore in host names - readfile.c:goodname() + Add ConvOldTab.sh - converts 1.1 bootptab to new format. + +02/20/94 gwr@mc.com (Gordon W. Ross) + Make readfile tolerant of hardware addresses that start + with a letter. (If lookup_hwa() fails, assume numeric.) + Fix whitespace skip before :vm= auto: and avoid lookup. + +02/12/94 walker@zk3.dec.com (Mary Walker) + Added support for 64-bit longs (for the DEC Alpha) + Allow ieee802 hardware address in bit-reversed oreder + +02/07/94 hl@tekla.fi (Harald Lundberg) + Fix conflict with DUMP_FILE in syslog.h on OSF1 + Use int for (struct bootp).bp_xid (for DEC Alpha) + Added Ultrix support to bootptest (getether) + +02/06/94 brezak@ch.hp.com (John Brezak) + Add man-page and install targets to Makefile.NetBSD + Add getether support for NetBSD + +02/05/94 gwr@mc.com (Gordon W. Ross) + Added tags 40,41,42 (NIS domain, NIS server, NTP server) + Add stub to getether for machines not yet supported. + +--> bootp-2.3.5 +01/29/94 gwr@mc.com (Gordon W. Ross) + Make bootpgw put a correct address in "giaddr" when + the client request came via broadcast. + +01/22/94 gwr@mc.com (Gordon W. Ross) + Fix syslog call (missing "facility" code) + Add SVR4/Streams support to getif() and getether() + Fix getif bug (matched when it should not) + Macro-ize lots of similar cases in readfile.c + +12/27/93 brezak@ch.hp.com (John Brezak) + Remove all newlines passed to syslog(3) + Add /etc/ethers support for NetBSD. + +12/18/93 gwr@mc.com (Gordon W. Ross) + Fix bootptest IP address printing. + Fix byte-order bugs in bootpgw and bootptest. + Clean-up signed/unsigned mismatches. + Back out SLIP support changes for now + (code fragment saved in ToDo). + +--> bootp-2.3.4 (beta test release) +12/12/93 gwr@mc.com (Gordon W. Ross) + Fixed several more NULL references in readfile. + Added proper length checks to option insertions. + +--> bootp-2.3.3 (beta test release) +12/09/93 gwr@mc.com (Gordon W. Ross) + Added ASSERT checks to readfile.c:fill_defaults() + +12/08/93 brezak@ch.hp.com (John Brezak) + New Makefile.NetBSD + Added setsid() and #ifdef TIOCNOTTY + (bootpd.c, bootpgw.c) + Moved #include <net/if.h> out of #ifdef SUNOS + Fixed several multiple declaration problems + +12/04/93 gwr@mc.com (Gordon W. Ross) + Re-implemented Extension File support + based on work by Jason Zions <jazz@hal.com> + Added support for Reply-Address-Override to support + HP clients (need reply sent to broadcast address) + from David R. Linn <drl@vuse.vanderbilt.edu> + +--> bootp-2.3.2 (beta test release) +11/27/93 gwr@mc.com (Gordon W. Ross) + Incorporated bootptest into the bootp release. + Added ANSI function prototypes everywhere. + +11/17/93 dpm@depend.com (David P. Maynard) + Added automatic SLIP address determination. + (This is NOT dynamic IP address assignment.) + Cleaned up some type warnings from gcc. + +11/11/93 gwr@mc.com (Gordon W. Ross) + Works (again) with no -DSYSLOGD defined. + Provide a default value for the subnet mask. + More #ifdef's for SunOS specific code (lookup_hwa) + Added a simple BOOTP gateway program: bootpgw + Reorganized for more code sharing (with bootpgw) + +--> bootp-2.3.1 (alpha test release) +11/08/93 gwr@mc.com (Gordon W. Ross) + Back-out changes to honor option structure in request + (this needs to be a per-client option). + Merged changes from NetBSD and Columbia versions. + Allow host name anywhere IP address is expected. + Add null terminators to option strings. + Add missing symbols to dump routine, dump symbols + in alphabetical order, one tag per line. + +--> bootp-2.2.D (posted as patch 2) +10/19/93 gwr@mc.com (Gordon W. Ross) + Fix references to free memory (leads to core dumps). + +--> bootp-2.2.C (posted as patch 1) +10/14/93 gwr@mc.com (Gordon W. Ross) + Fix data access alignment problems on SPARC/Solaris. + +--> bootp-2.2.B (posted to usenet) +10/11/93 gwr@mc.com (Gordon W. Ross) + Allow extended-length BOOTP packets (more vendor options) + Honor option format specified in client requests. + Added Solaris-2.X changes from db@sunbim.be (Danny Backx). + +All history before this point may be inaccurate. Please send +changes if any of the credits are incorrect. -gwr + +--> bootp-2.2+NetBSD released +08/27/93 brezak@ch.hp.com (John Brezak) + Added RFC 1396 support (tags 14-17) + +--> bootp-2.2+NetBSD (version?) +??/??/93 mckim@lerc.nasa.gov (Jim McKim) + Ported to NetBSD (see Makefile.NetBSD) + Set server host name in responses. + Check all interfaces in address match routine. + +--> bootp-2.2+FdC released +01/27/93 <fdc@watsun.cc.columbia.edu> Frank da Cruz + Added RFC 1395 information: Merit dump file, + client domain name, swap server address, root path. + +--> bootp-2.2alpha released +11/14/91 <walt+@cmu.edu> Walter L. Wimer + Add "td" to TFTP directory for "secure" (chroot) TFTP. + Add "sa" tag to set explicit server address. + Automatically determine if child of inetd. + Use RFC 1048 format when request has magic number zero. + Fixed various bugs. Give bootptab a separate man page. + +--> bootp-2.1 released +01/09/89 <walt+@cmu.edu> Walter L. Wimer + Check world read bit on TFTP boot file. + Add support for rfc1085 "bootfile size" tag. + Add generic tags. Fix byte order of rfc1048 data. + Fix various crashing bugs. + +--> bootp-2.0 released +07/15/88 <walt+@cmu.edu> Walter L. Wimer + Added vendor information to conform to RFC1048. + Adopted termcap-like file format to support above. + Added hash table lookup instead of linear search. + Other cleanups. + +--> bootp-1.3(?) released +07/24/87 <ddp@andrew.cmu.edu> Drew D. Perkins + Modified to use syslog instead of Kovar's + routines. Add debugging dumps. Many other fixups. + +--> bootp-1.2(?) released +07/30/86 David Kovar at Carnegie Mellon University + Modified to work at CMU. + +--> bootp-1.1 released +01/22/86 Bill Croft at Stanford University + Original created. diff --git a/libexec/bootpd/ConvOldTab.sh b/libexec/bootpd/ConvOldTab.sh new file mode 100755 index 0000000..00683f0 --- /dev/null +++ b/libexec/bootpd/ConvOldTab.sh @@ -0,0 +1,141 @@ +#!/bin/sh +# convert_bootptab Jeroen.Scheerder@let.ruu.nl 02/25/94 +# This script can be used to convert bootptab files in old format +# to new (termcap-like) bootptab files +# +# The old format - real entries are commented out by '###' +# +# Old-style bootp files consist of two sections. +# The first section has two entries: +# First, a line that specifies the home directory +# (where boot file paths are relative to) + +###/tftpboot + +# The next non-empty non-comment line specifies the default bootfile + +###no-file + +# End of first section - indicated by '%%' at the start of the line + +###%% + +# The remainder of this file contains one line per client +# interface with the information shown by the table headings +# below. The host name is also tried as a suffix for the +# bootfile when searching the home directory (that is, +# bootfile.host) +# +# Note that htype is always 1, indicating the hardware type Ethernet. +# Conversion therefore always yields ':ha=ether:'. +# +# host htype haddr iaddr bootfile +# + +###somehost 1 00:0b:ad:01:de:ad 128.128.128.128 dummy + +# That's all for the description of the old format. +# For the new-and-improved format, see bootptab(5). + +set -u$DX + +case $# +in 2 ) OLDTAB=$1 ; NEWTAB=$2 ;; + * ) echo "Usage: `basename $0` <Input> <Output>" + exit 1 +esac + +if [ ! -r $OLDTAB ] +then + echo "`basename $0`: $OLDTAB does not exist or is unreadable." + exit 1 +fi + +if touch $NEWTAB 2> /dev/null +then + : +else + echo "`basename $0`: cannot write to $NEWTAB." + exit 1 +fi + + +cat << END_OF_HEADER >> $NEWTAB +# /etc/bootptab: database for bootp server (/etc/bootpd) +# This file was generated automagically + +# Blank lines and lines beginning with '#' are ignored. +# +# Legend: (see bootptab.5) +# first field -- hostname (not indented) +# bf -- bootfile +# bs -- bootfile size in 512-octet blocks +# cs -- cookie servers +# df -- dump file name +# dn -- domain name +# ds -- domain name servers +# ef -- extension file +# gw -- gateways +# ha -- hardware address +# hd -- home directory for bootfiles +# hn -- host name set for client +# ht -- hardware type +# im -- impress servers +# ip -- host IP address +# lg -- log servers +# lp -- LPR servers +# ns -- IEN-116 name servers +# ra -- reply address +# rl -- resource location protocol servers +# rp -- root path +# sa -- boot server address +# sm -- subnet mask +# sw -- swap server +# tc -- template host (points to similar host entry) +# td -- TFTP directory +# to -- time offset (seconds) +# ts -- time servers +# vm -- vendor magic number +# Tn -- generic option tag n +# +# Be careful about including backslashes where they're needed. Weird (bad) +# things can happen when a backslash is omitted where one is intended. +# Also, note that generic option data must be either a string or a +# sequence of bytes where each byte is a two-digit hex value. + +# First, we define a global entry which specifies the stuff every host uses. +# (Host name lookups are relative to the domain: your.domain.name) + +END_OF_HEADER + +# Fix up HW addresses in aa:bb:cc:dd:ee:ff and aa-bb-cc-dd-ee-ff style first +# Then awk our stuff together +sed -e 's/[:-]//g' < $OLDTAB | \ +nawk 'BEGIN { PART = 0 ; FIELD=0 ; BOOTPATH="unset" ; BOOTFILE="unset" } + /^%%/ { + PART = 1 + printf ".default:\\\n\t:ht=ether:\\\n\t:hn:\\\n\t:dn=your.domain.name:\\\n\t:ds=your,dns,servers:\\\n\t:sm=255.255.0.0:\\\n\t:hd=%s:\\\n\t:rp=%s:\\\n\t:td=%s:\\\n\t:bf=%s:\\\n\t:to=auto:\n\n", BOOTPATH, BOOTPATH, BOOTPATH, BOOTFILE + next + } + /^$/ { next } + /^#/ { next } + { + if ( PART == 0 && FIELD < 2 ) + { + if ( FIELD == 0 ) BOOTPATH=$1 + if ( FIELD == 1 ) BOOTFILE=$1 + FIELD++ + } + } + { + if ( PART == 1 ) + { + HOST=$1 + HA=$3 + IP=$4 + BF=$5 + printf "%s:\\\n\t:tc=.default:\\\n\t:ha=0x%s:\\\n\t:ip=%s:\\\n\t:bf=%s:\n", HOST, HA, IP, BF + } + }' >> $NEWTAB + +exit 0 diff --git a/libexec/bootpd/Installation b/libexec/bootpd/Installation new file mode 100644 index 0000000..466cabc --- /dev/null +++ b/libexec/bootpd/Installation @@ -0,0 +1,29 @@ + +Installation instructions for SunOS + +Compile the executable: +For SunOS 4.X: + make sunos4 +For SunOS 5.X: (Solaris) + make sunos5 + +Install the executables: + + make install + +Edit (or create) the bootptab: +(See bootptab.sample and bootptab.5 manual entry) + edit /etc/bootptab + +Edit /etc/services to add these two lines: +bootps 67/udp bootp # BOOTP Server +bootpc 68/udp # BOOTP Client + +Edit /etc/inetd.conf to add the line: +bootp dgram udp wait root /usr/etc/bootpd bootpd -i + +If you compiled report.c with LOG_LOCAL2 (defined in the Makefile) +then you may want to capture syslog messages from BOOTP by changing +your syslog.conf file. (See the sample syslog.conf file here). +Test the change with: logger -t test -p local2.info "message" + diff --git a/libexec/bootpd/Makefile b/libexec/bootpd/Makefile new file mode 100644 index 0000000..e923968 --- /dev/null +++ b/libexec/bootpd/Makefile @@ -0,0 +1,16 @@ +# bootpd/Makefile +# $Id: Makefile,v 1.2 1995/05/30 05:45:45 rgrimes Exp $ + +PROG= bootpd +CFLAGS+= -DETC_ETHERS +CFLAGS+= -DSYSLOG -DDEBUG -DVEND_CMU + +SUBDIR= tools + +SRCS= bootpd.c dovend.c readfile.c hash.c dumptab.c \ + lookup.c getif.c hwaddr.c report.c tzone.c rtmsg.c + +MAN5= bootptab.5 +MAN8= bootpd.8 + +.include <bsd.prog.mk> diff --git a/libexec/bootpd/Makefile.UNIX b/libexec/bootpd/Makefile.UNIX new file mode 100644 index 0000000..e333ce5 --- /dev/null +++ b/libexec/bootpd/Makefile.UNIX @@ -0,0 +1,184 @@ +# +# Makefile for the BOOTP programs: +# bootpd - BOOTP server daemon +# bootpef - BOOTP extension file builder +# bootpgw - BOOTP gateway daemon +# bootptest - BOOTP tester (client) +# + +# OPTion DEFinitions: +# Remove the -DVEND_CMU if you don't wish to support the "CMU vendor format" +# in addition to the RFC1048 format. Leaving out DEBUG saves little. +OPTDEFS= -DSYSLOG -DVEND_CMU -DDEBUG + +# Uncomment and edit this to choose the facility code used for syslog. +# LOG_FACILITY= "-DLOG_BOOTP=LOG_LOCAL2" + +# SYStem DEFinitions: +# Either uncomment some of the following, or do: +# "make sunos4" (or "make sunos5", etc.) +# SYSDEFS= -DSUNOS -DETC_ETHERS +# SYSDEFS= -DSVR4 +# SYSLIBS= -lsocket -lnsl + +# Uncomment this if your system does not provide streror(3) +# STRERROR=strerror.o + +# FILE DEFinitions: +# The next few lines may be uncommented and changed to alter the default +# filenames bootpd uses for its configuration and dump files. +#CONFFILE= -DCONFIG_FILE=\"/usr/etc/bootptab\" +#DUMPFILE= -DDUMPTAB_FILE=\"/usr/etc/bootpd.dump\" +#FILEDEFS= $(CONFFILE) $(DUMPFILE) + +# MORE DEFinitions (whatever you might want to add) +# One might define NDEBUG (to remove "assert()" checks). +MOREDEFS= + +INSTALL=/usr/bin/install +DESTDIR= +BINDIR=/usr/etc +MANDIR=/usr/local/man + +CFLAGS= $(OPTDEFS) $(SYSDEFS) $(FILEDEFS) $(MOREDEFS) +PROGS= bootpd bootpef bootpgw bootptest +TESTS= trylook trygetif trygetea + +all: $(PROGS) + +tests: $(TESTS) + +system: install + +install: $(PROGS) + -for f in $(PROGS) ;\ + do \ + $(INSTALL) -c -s $$f $(DESTDIR)$(BINDIR) ;\ + done + +MAN5= bootptab.5 +MAN8= bootpd.8 bootpef.8 bootptest.8 +install.man: $(MAN5) $(MAN8) + -for f in $(MAN5) ;\ + do \ + $(INSTALL) -c -m 644 $$f $(DESTDIR)$(MANDIR)/man5 ;\ + done + -for f in $(MAN8) ;\ + do \ + $(INSTALL) -c -m 644 $$f $(DESTDIR)$(MANDIR)/man8 ;\ + done + +clean: + -rm -f core *.o + -rm -f $(PROGS) $(TESTS) + +distclean: + -rm -f *.BAK *.CKP *~ .emacs* + +# +# Handy targets for individual systems: +# + +# DEC/OSF1 on the Alpha +alpha: + $(MAKE) SYSDEFS="-DETC_ETHERS -Dint32=int -D_SOCKADDR_LEN" \ + STRERROR=strerror.o + +# Control Data EP/IX 1.4.3 system, BSD 4.3 mode +epix143: + $(MAKE) CC="cc -systype bsd43" \ + SYSDEFS="-Dconst= -D_SIZE_T -DNO_UNISTD -DUSE_BFUNCS" \ + STRERROR=strerror.o + +# Control Data EP/IX 2.1.1 system, SVR4 mode +epix211: + $(MAKE) CC="cc -systype svr4" \ + SYSDEFS="-DSVR4" \ + SYSLIBS="-lsocket -lnsl" + +# Silicon Graphics IRIX (no <sys/sockio.h>, so not SVR4) +irix: + $(MAKE) SYSDEFS="-DSYSV -DIRIX" + +# SunOS 4.X +sunos4: + $(MAKE) SYSDEFS="-DSUNOS -DETC_ETHERS" \ + STRERROR=strerror.o + +# Solaris 2.X (i.e. SunOS 5.X) +sunos5: + $(MAKE) SYSDEFS="-DSVR4 -DETC_ETHERS" \ + SYSLIBS="-lsocket -lnsl" + +# UNIX System V Rel. 4 (also: IRIX 5.X, others) +svr4: + $(MAKE) SYSDEFS="-DSVR4" \ + SYSLIBS="-lsocket -lnsl" + +# +# How to build each program: +# + +OBJ_D= bootpd.o dovend.o readfile.o hash.o dumptab.o \ + lookup.o getif.o hwaddr.o tzone.o report.o $(STRERROR) +bootpd: $(OBJ_D) + $(CC) -o $@ $(OBJ_D) $(SYSLIBS) + +OBJ_EF= bootpef.o dovend.o readfile.o hash.o dumptab.o \ + lookup.o hwaddr.o tzone.o report.o $(STRERROR) +bootpef: $(OBJ_EF) + $(CC) -o $@ $(OBJ_EF) $(SYSLIBS) + +OBJ_GW= bootpgw.o getif.o hwaddr.o report.o $(STRERROR) +bootpgw: $(OBJ_GW) + $(CC) -o $@ $(OBJ_GW) $(SYSLIBS) + +OBJ_TEST= bootptest.o print-bootp.o getif.o getether.o \ + report.o $(STRERROR) +bootptest: $(OBJ_TEST) + $(CC) -o $@ $(OBJ_TEST) $(SYSLIBS) + +# This is just for testing the lookup functions. +TRYLOOK= trylook.o lookup.o report.o $(STRERROR) +trylook : $(TRYLOOK) + $(CC) -o $@ $(TRYLOOK) $(SYSLIBS) + +# This is just for testing getif. +TRYGETIF= trygetif.o getif.o report.o $(STRERROR) +trygetif : $(TRYGETIF) + $(CC) -o $@ $(TRYGETIF) $(SYSLIBS) + +# This is just for testing getether. +TRYGETEA= trygetea.o getether.o report.o $(STRERROR) +trygetea : $(TRYGETEA) + $(CC) -o $@ $(TRYGETEA) $(SYSLIBS) + +# This rule just keeps the LOG_BOOTP define localized. +report.o : report.c + $(CC) $(CFLAGS) $(LOG_FACILITY) -c $< + +# Punt SunOS -target noise +.c.o: + $(CC) $(CFLAGS) -c $< + +# +# Header file dependencies: +# + +bootpd.o : bootp.h bptypes.h hash.h hwaddr.h bootpd.h dovend.h +bootpd.o : readfile.h report.h tzone.h patchlevel.h getif.h +bootpef.o : bootp.h bptypes.h hash.h hwaddr.h bootpd.h dovend.h +bootpef.o : readfile.h report.h tzone.h patchlevel.h +bootpgw.o : bootp.h bptypes.h getif.h hwaddr.h report.h patchlevel.h +bootptest.o : bootp.h bptypes.h bootptest.h getif.h patchlevel.h +dovend.o : bootp.h bptypes.h bootpd.h hash.h hwaddr.h report.h dovend.h +dumptab.o : bootp.h bptypes.h hash.h hwaddr.h report.h patchlevel.h bootpd.h +getif.o : getif.h report.h +hash.o : hash.h +hwaddr.o : bptypes.h hwaddr.h report.h +lookup.o : bootp.h bptypes.h lookup.h report.h +print-bootp.o : bootp.h bptypes.h bootptest.h +readfile.o : bootp.h bptypes.h hash.h hwaddr.h lookup.h readfile.h +readfile.o : report.h tzone.h bootpd.h +report.o : report.h +tzone.o : bptypes.h report.h tzone.h diff --git a/libexec/bootpd/Problems b/libexec/bootpd/Problems new file mode 100644 index 0000000..9478676 --- /dev/null +++ b/libexec/bootpd/Problems @@ -0,0 +1,47 @@ + +Common problems and ways to work around them: + +Bootpd complains that it "can not get IP addr for HOSTNAME" + + If the entry is a "dummy" (not a real host) used only for + reference by other entries, put '.' in front of the name. + + If the entry is for a real client and the IP address for + the client can not be found using gethostbyname(), specify + the IP address for the client using numeric form. + +Bootpd takes a long time to finish parsing the bootptab file: + + Excessive startup time is usually caused by waiting for + timeouts on failed DNS lookup operations. If this is the + problem, find the client names for which DNS lookup fails + and change the bootptab to specify the IP addresses for + those clients using numeric form. + + When bootptab entries do not specify an ip address, bootpd + attempts to lookup the tagname as a host name to find the + IP address. To suppress this default action, either make + the entry a "dummy" or specify its IP numeric address. + + If your DNS lookups work but are just slow, consider either + running bootpd on the same machine as the DNS server or + running a caching DNS server on the host running bootpd. + +My huge bootptab file causes startup time to be so long that clients +give up waiting for a reply. + + Truly huge bootptab files make "inetd" mode impractical. + Start bootpd in "standalone" mode when the server boots. + + Another possibility is to run one bootpd on each network + segment so each one can have a smaller bootptab. Only one + instance of bootpd may run on one server, so you would need + to use a different server for each network segment. + +My bootp clients are given responses with a boot file name that is +not a fully specified path. + + Make sure the TFTP directory or home directory tags are set: + :td=/tftpboot: (or) + :hd=/usr/boot: (for example) + diff --git a/libexec/bootpd/README b/libexec/bootpd/README new file mode 100644 index 0000000..c7755b7 --- /dev/null +++ b/libexec/bootpd/README @@ -0,0 +1,133 @@ + +This is an enhanced version of the CMU BOOTP server which was derived +from the original BOOTP server created by Bill Croft at Stanford. +This version merges all the enhancements and bug-fixes from the +NetBSD, Columbia, and other versions. + +Please direct questions, comments, and bug reports to the list: + <bootp@andrew.cmu.edu> + +You can subscribe to this mailing list by sending mail to: + bootp-request@andrew.cmu.edu +(The body of the message should contain: "Add <your-address>") + +[ From the NetBSD README file: ] + +BOOTPD is a useful adjunct to the nfs diskless boot EPROM code. + +The alternatives for initiating a boot of a kernel across a network +are to use RARP protocol, or BOOTP protocol. BOOTP is more flexible; +it allows additional items of information to be returned to the +booting client; it also supports booting across gateways. + +[ From the CMU README file: ] + +Notes: +1) BOOTP was originally designed and implemented by Bill Croft at Stanford. + Much of the credit for the ideas and the code goes to him. We've added + code to support the vendor specific area of the packet as specified in + RFC1048. We've also improved the host lookup algorithm and added some + extra logging. + +2) The server now uses syslog to do logging. Specifically it uses the 4.3bsd + version. I've #ifdef'd all of these calls. If you are running 4.2 you + should compile without the -DSYSLOG switch. + +3) You must update your /etc/services file to contain the following two lines: + bootps 67/udp bootp # BOOTP Server + bootpc 68/udp # BOOTP Client + +4) Edit the bootptab. It has some explanitory comments, and there + is a manual entry describing its format (bootptab.5) + If you have any questions, just let us know. + +Construction: + [ See the file Installation which is more up-to-date. -gwr ] + + Make sure all of the files exist first. If anything is missing, + please contact either Walt Wimer or Drew Perkins by E-mail or phone. + Addresses and phone numbers are listed below. + + Type 'make'. The options at present are: -DSYSLOG which enables logging + code, -DDEBUG which enables table dumping via signals, and -DVEND_CMU + which enables the CMU extensions for CMU PC/IP. + + Edit the bootptab. The man page and the comments in the file should + explain how to go about doing so. If you have any problems, let me know. + + Type 'make install'. This should put all of the files in the right place. + + Edit your /etc/rc.local or /etc/inetd.conf file to start up bootpd upon + reboot. The following is a sample /etc/inetd.conf entry: + # BOOTP server + bootps dgram udp wait root /usr/etc/bootpd bootpd -i + +Care and feeding: + If you change the interface cards on your host or add new hosts you will + need to update /etc/bootptab. Just edit it as before. Once you write + it back out, bootpd will notice that there is a new copy and will + reread it the next time it gets a request. + + If your bootp clients don't get a response then several things might be + wrong. Most often, the entry for that host is not in the database. + Check the hardware address and then check the entry and make sure + everything is right. Other problems include the server machine crashing, + bad cables, and the like. If your network is very congested you should + try making your bootp clients send additional requests before giving up. + + +November 7, 1988 + + +Walter L. Wimer Drew D. Perkins +ww0n@andrew.cmu.edu ddp@andrew.cmu.edu +(412) 268-6252 (412) 268-8576 + +4910 Forbes Ave +Pittsburgh, PA 15213 + +[ Contents description by file: ] + +Announce* Text of release announcements +Changes Change history, reverse chronological +Installation Instructions for building and installing +Makefile* for "make" +README This file +ToDo Things not yet done +bootp.h The protocol header file +bootpd.8 Manual page for bootpd, boopgw +bootpd.c BOOTP server main module +bootpd.h header for above (and others) +bootpef.8 Manual page for bootpef +bootpef.c BOOTP extension file compiler +bootpgw.c BOOTP gateway main module +bootptab.5 A manual describing the bootptab format +bootptab.cmu A sample database file for the server +bootptab.mcs Another sample from <gwr@mc.com> +bootptest.8 Manual page for bootptest +bootptest.c BOOTP test program (fake client) +bootptest.h header for above +dovend.c Vendor Option builder (for bootpd, bootpef) +dovend.h header for above +dumptab.c Implements debugging dump for bootpd +getether.c For bootptest (not used yet) +getif.c Get network interface info. +getif.h header for above +hash.c The hash table module +hash.h header for above +hwaddr.c Hardware address support +hwaddr.h header for above +lookup.c Internet Protocol address lookup +lookup.h header for above +patchlevel.h Holds version numbers +print-bootp.c Prints BOOTP packets (taken from BSD tcpdump) +readfile.c The configuration file-reading routines +readfile.h header for above +report.c Does syslog-style messages +report.h header for above +strerror.c Library errno-to-string (for systems lacking it) +syslog.conf Sample config file for syslogd(8) +syslog.h For systems that lack syslog(3) +try*.c Test programs (for debugging) +tzone.c Get timezone offset +tzone.h header for above diff --git a/libexec/bootpd/bootp.h b/libexec/bootpd/bootp.h new file mode 100644 index 0000000..0651aa5 --- /dev/null +++ b/libexec/bootpd/bootp.h @@ -0,0 +1,147 @@ +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + +/* + * Bootstrap Protocol (BOOTP). RFC951 and RFC1395. + * + * $Id: bootp.h,v 1.1.1.1 1994/09/10 14:44:54 csgr Exp $ + * + * + * This file specifies the "implementation-independent" BOOTP protocol + * information which is common to both client and server. + * + */ + +#include "bptypes.h" /* for int32, u_int32 */ + +#define BP_CHADDR_LEN 16 +#define BP_SNAME_LEN 64 +#define BP_FILE_LEN 128 +#define BP_VEND_LEN 64 +#define BP_MINPKTSZ 300 /* to check sizeof(struct bootp) */ + +struct bootp { + unsigned char bp_op; /* packet opcode type */ + unsigned char bp_htype; /* hardware addr type */ + unsigned char bp_hlen; /* hardware addr length */ + unsigned char bp_hops; /* gateway hops */ + unsigned int32 bp_xid; /* transaction ID */ + unsigned short bp_secs; /* seconds since boot began */ + unsigned short bp_flags; /* RFC1532 broadcast, etc. */ + struct in_addr bp_ciaddr; /* client IP address */ + struct in_addr bp_yiaddr; /* 'your' IP address */ + struct in_addr bp_siaddr; /* server IP address */ + struct in_addr bp_giaddr; /* gateway IP address */ + unsigned char bp_chaddr[BP_CHADDR_LEN]; /* client hardware address */ + char bp_sname[BP_SNAME_LEN]; /* server host name */ + char bp_file[BP_FILE_LEN]; /* boot file name */ + unsigned char bp_vend[BP_VEND_LEN]; /* vendor-specific area */ + /* note that bp_vend can be longer, extending to end of packet. */ +}; + +/* + * UDP port numbers, server and client. + */ +#define IPPORT_BOOTPS 67 +#define IPPORT_BOOTPC 68 + +#define BOOTREPLY 2 +#define BOOTREQUEST 1 + +/* + * Hardware types from Assigned Numbers RFC. + */ +#define HTYPE_ETHERNET 1 +#define HTYPE_EXP_ETHERNET 2 +#define HTYPE_AX25 3 +#define HTYPE_PRONET 4 +#define HTYPE_CHAOS 5 +#define HTYPE_IEEE802 6 +#define HTYPE_ARCNET 7 + +/* + * Vendor magic cookie (v_magic) for CMU + */ +#define VM_CMU "CMU" + +/* + * Vendor magic cookie (v_magic) for RFC1048 + */ +#define VM_RFC1048 { 99, 130, 83, 99 } + + + +/* + * Tag values used to specify what information is being supplied in + * the vendor (options) data area of the packet. + */ +/* RFC 1048 */ +#define TAG_END ((unsigned char) 255) +#define TAG_PAD ((unsigned char) 0) +#define TAG_SUBNET_MASK ((unsigned char) 1) +#define TAG_TIME_OFFSET ((unsigned char) 2) +#define TAG_GATEWAY ((unsigned char) 3) +#define TAG_TIME_SERVER ((unsigned char) 4) +#define TAG_NAME_SERVER ((unsigned char) 5) +#define TAG_DOMAIN_SERVER ((unsigned char) 6) +#define TAG_LOG_SERVER ((unsigned char) 7) +#define TAG_COOKIE_SERVER ((unsigned char) 8) +#define TAG_LPR_SERVER ((unsigned char) 9) +#define TAG_IMPRESS_SERVER ((unsigned char) 10) +#define TAG_RLP_SERVER ((unsigned char) 11) +#define TAG_HOST_NAME ((unsigned char) 12) +#define TAG_BOOT_SIZE ((unsigned char) 13) +/* RFC 1395 */ +#define TAG_DUMP_FILE ((unsigned char) 14) +#define TAG_DOMAIN_NAME ((unsigned char) 15) +#define TAG_SWAP_SERVER ((unsigned char) 16) +#define TAG_ROOT_PATH ((unsigned char) 17) +/* RFC 1497 */ +#define TAG_EXTEN_FILE ((unsigned char) 18) +/* RFC 1533 */ +#define TAG_NIS_DOMAIN ((unsigned char) 40) +#define TAG_NIS_SERVER ((unsigned char) 41) +#define TAG_NTP_SERVER ((unsigned char) 42) +/* DHCP maximum message size. */ +#define TAG_MAX_MSGSZ ((unsigned char) 57) + +/* XXX - Add new tags here */ + + +/* + * "vendor" data permitted for CMU bootp clients. + */ + +struct cmu_vend { + char v_magic[4]; /* magic number */ + unsigned int32 v_flags; /* flags/opcodes, etc. */ + struct in_addr v_smask; /* Subnet mask */ + struct in_addr v_dgate; /* Default gateway */ + struct in_addr v_dns1, v_dns2; /* Domain name servers */ + struct in_addr v_ins1, v_ins2; /* IEN-116 name servers */ + struct in_addr v_ts1, v_ts2; /* Time servers */ + int32 v_unused[6]; /* currently unused */ +}; + + +/* v_flags values */ +#define VF_SMASK 1 /* Subnet mask field contains valid data */ diff --git a/libexec/bootpd/bootpd.8 b/libexec/bootpd/bootpd.8 new file mode 100644 index 0000000..359ce53 --- /dev/null +++ b/libexec/bootpd/bootpd.8 @@ -0,0 +1,305 @@ +.\" Copyright (c) 1988, 1989, 1991 Carnegie Mellon University +.\" +.\" $Header: /home/ncvs/src/libexec/bootpd/bootpd.8,v 1.1.1.1 1994/09/30 05:45:04 pst Exp $ +.\" +.TH BOOTPD 8 "November 06, 1993" "Carnegie Mellon University" +.SH NAME +bootpd, bootpgw \- Internet Boot Protocol server/gateway +.SH SYNOPSIS +.B bootpd +[ +.B \-i +.B \-s +.B \-t +timeout +.B \-d +level +.B \-c +chdir\-path +] +[ +.I bootptab +[ +.I dumpfile +] ] +.br +.B bootpgw +[ +.B \-i +.B \-s +.B \-t +timeout +.B \-d +level +] server +.SH DESCRIPTION +.I Bootpd +implements an Internet Bootstrap Protocol (BOOTP) server as defined in +RFC951, RFC1532, and RFC1533. +.I Bootpgw +implements a simple BOOTP gateway which can be used to forward +requests and responses between clients on one subnet and a +BOOTP server (i.e. +.IR bootpd ) +on another subnet. While either +.I bootpd +or +.I bootpgw +will forward BOOTREPLY packets, only +.I bootpgw +will forward BOOTREQUEST packets. +.PP +One host on each network segment is normally configured to run either +.I bootpd +or +.I bootpgw +from +.I inetd +by including one of the following lines in the file +.IR /etc/inetd.conf : +.IP +bootps dgram udp wait root /usr/libexec/bootpd bootpd bootptab +.br +bootps dgram udp wait root /usr/libexec/bootpgw bootpgw server +.PP +This mode of operation is referred to as "inetd mode" and causes +.I bootpd +(or +.IR bootpgw ) +to be started only when a boot request arrives. If it does not +receive another packet within fifteen minutes of the last one +it received, it will exit to conserve system resources. The +.B \-t +option controls this timeout (see OPTIONS). +.PP +It is also possible to run +.I bootpd +(or +.IR bootpgw ) +in "standalone mode" (without +.IR inetd ) +by simply invoking it from a shell like any other regular command. +Standalone mode is particularly useful when +.I bootpd +is used with a large configuration database, where the start up +delay might otherwise prevent timely response to client requests. +(Automatic start up in standalone mode can be done by invoking +.I bootpd +from within +.IR /etc/rc.local , +for example.) +Standalone mode is less useful for +.I bootgw +which +has very little start up delay because +it does not read a configuration file. +.PP +Either program automatically detects whether it was invoked from inetd +or from a shell and automatically selects the appropriate mode. +The +.B \-s +or +.B \-i +option may be used to force standalone or inetd mode respectively +(see OPTIONS). +.SH OPTIONS +.TP +.BI \-t \ timeout +Specifies the +.I timeout +value (in minutes) that a +.I bootpd +or +.I bootpgw +process will wait for a BOOTP packet before exiting. +If no packets are recieved for +.I timeout +seconds, then the program will exit. +A timeout value of zero means "run forever". +In standalone mode, this option is forced to zero. +.TP +.BI \-d \ debug\-level +Sets the +.I debug\-level +variable that controls the amount of debugging messages generated. +For example, -d4 or -d 4 will set the debugging level to 4. +For compatibility with older versions of +.IR bootpd , +omitting the numeric parameter (i.e. just -d) will +simply increment the debug level by one. +.TP +.BI \-c \ chdir\-path +Sets the current directory used by +.I bootpd +while checking the existence and size of client boot files. This is +useful when client boot files are specified as relative pathnames, and +.I bootpd +needs to use the same current directory as the TFTP server +(typically /tftpboot). This option is not recoginzed by +.IR bootpgw . +.TP +.B \-i +Force inetd mode. This option is obsolete, but remains for +compatibility with older versions of +.IR bootpd . +.TP +.B \-s +Force standalone mode. This option is obsolete, but remains for +compatibility with older versions of +.IR bootpd . +.TP +.I bootptab +Specifies the name of the configuration file from which +.I bootpd +loads its database of known clients and client options +.RI ( bootpd +only). +.TP +.I dumpfile +Specifies the name of the file that +.I bootpd +will dump its internal database into when it receives a +SIGUSR1 signal +.RI ( bootpd +only). This option is only recognized if +.I bootpd +was compiled with the -DDEBUG flag. +.TP +.I server +Specifies the name of a BOOTP server to which +.I bootpgw +will forward all BOOTREQUEST packets it receives +.RI ( bootpgw +only). +.SH OPERATION +.PP +Both +.I bootpd +and +.I bootpgw +operate similarly in that both listen for any packets sent to the +.I bootps +port, and both simply forward any BOOTREPLY packets. +They differ in their handling of BOOTREQUEST packets. +.PP +When +.I bootpgw +is started, it determines the address of a BOOTP server +whose name is provided as a command line parameter. When +.I bootpgw +receives a BOOTREQUEST packet, it sets the "gateway address" +and "hop count" fields in the packet and forwards the packet +to the BOOTP server at the address determined earlier. +Requests are forwarded only if they indicate that +the client has been waiting for at least three seconds. +.PP +When +.I bootpd +is started it reads a configuration file, (normally +.IR /etc/bootptab ) +that initializes the internal database of known clients and client +options. This internal database is reloaded +from the configuration file when +.I bootpd +receives a hangup signal (SIGHUP) or when it discovers that the +configuration file has changed. +.PP +When +.I bootpd +receives a BOOTREQUEST packet, it +.\" checks the modification time of the +.\" configuration file and reloads the database if necessary. Then it +looks for a database entry matching the client request. +If the client is known, +.I bootpd +composes a BOOTREPLY packet using the database entry found above, +and sends the reply to the client (possibly using a gateway). +If the client is unknown, the request is discarded +(with a notice if debug > 0). +.PP +If +.I bootpd +is compiled with the -DDEBUG option, receipt of a SIGUSR1 signal causes +it to dump its internal database to the file +.I /etc/bootpd.dump +or the dumpfile specified as a command line parameter. +.PP +During initialization, both programs +determine the UDP port numbers to be used by calling +.I getservbyname +(which nomally uses +.IR /etc/services). +Two service names (and port numbers) are used: +.IP +bootps \- BOOTP Server listening port +.br +bootpc \- BOOTP Client destination port +.LP +If the port numbers cannot +be determined using +.I getservbyname +then the values default to boopts=67 and bootpc=68. +.SH FILES +.TP 20 +/etc/bootptab +Database file read by +.IR bootpd . +.TP +/etc/bootpd.dump +Debugging dump file created by +.IR bootpd . +.TP +/etc/services +Internet service numbers. +.TP +/tftpboot +Current directory typically used by the TFTP server and +.IR bootpd . + +.SH BUGS +Individual host entries must not exceed 1024 characters. + +.SH CREDITS +.PP +This distribution is currently maintained by +Walter L. Wimer <walt+@cmu.edu>. +.PP +The original BOOTP server was created by +Bill Croft at Stanford University in January 1986. +.PP +The current version of +.I bootpd +is primarily the work of David Kovar, +Drew D. Perkins, and Walter L. Wimer, +at Carnegie Mellon University. +.TP +Enhancements and bug\-fixes have been contributed by: +(in alphabetical order) +.br +Danny Backx <db@sunbim.be> +.br +John Brezak <brezak@ch.hp.com> +.br +Frank da Cruz <fdc@cc.columbia.edu> +.br +David R. Linn <drl@vuse.vanderbilt.edu> +.br +Jim McKim <mckim@lerc.nasa.gov> +.br +Gordon W. Ross <gwr@mc.com> +.br +Jason Zions <jazz@hal.com> +.SH "SEE ALSO" +.LP +bootptab(5), inetd(8), tftpd(8) +.LP +DARPA Internet Request For Comments: +.TP 10 +RFC951 +Bootstrap Protocol +.TP 10 +RFC1532 +Clarifications and Extensions for the Bootstrap Protocol +.TP 10 +RFC1533 +DHCP Options and BOOTP Vendor Extensions diff --git a/libexec/bootpd/bootpd.c b/libexec/bootpd/bootpd.c new file mode 100644 index 0000000..cf693fd --- /dev/null +++ b/libexec/bootpd/bootpd.c @@ -0,0 +1,1380 @@ +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + +#ifndef lint +static char rcsid[] = "$Id: bootpd.c,v 1.1.1.1 1994/09/30 05:45:04 pst Exp $"; +#endif + +/* + * BOOTP (bootstrap protocol) server daemon. + * + * Answers BOOTP request packets from booting client machines. + * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol. + * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions. + * See RFC 1395 for option tags 14-17. + * See accompanying man page -- bootpd.8 + * + * HISTORY + * See ./Changes + * + * BUGS + * See ./ToDo + */ + + + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#ifndef NO_UNISTD +#include <unistd.h> +#endif +#include <stdlib.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <netdb.h> +#include <syslog.h> +#include <assert.h> + +#ifdef NO_SETSID +# include <fcntl.h> /* for O_RDONLY, etc */ +#endif + +#ifdef SVR4 +/* Using sigset() avoids the need to re-arm each time. */ +#define signal sigset +#endif + +#ifndef USE_BFUNCS +# include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +# define bcopy(a,b,c) memcpy(b,a,c) +# define bzero(p,l) memset(p,0,l) +# define bcmp(a,b,c) memcmp(a,b,c) +#endif + +#include "bootp.h" +#include "hash.h" +#include "hwaddr.h" +#include "bootpd.h" +#include "dovend.h" +#include "getif.h" +#include "readfile.h" +#include "report.h" +#include "tzone.h" +#include "patchlevel.h" + +#ifndef CONFIG_FILE +#define CONFIG_FILE "/etc/bootptab" +#endif +#ifndef DUMPTAB_FILE +#define DUMPTAB_FILE "/tmp/bootpd.dump" +#endif + + + +/* + * Externals, forward declarations, and global variables + */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern void dumptab P((char *)); + +PRIVATE void catcher P((int)); +PRIVATE int chk_access P((char *, int32 *)); +#ifdef VEND_CMU +PRIVATE void dovend_cmu P((struct bootp *, struct host *)); +#endif +PRIVATE void dovend_rfc1048 P((struct bootp *, struct host *, int32)); +PRIVATE void handle_reply P((void)); +PRIVATE void handle_request P((void)); +PRIVATE void sendreply P((int forward, int32 dest_override)); +PRIVATE void usage P((void)); + +#undef P + +/* + * IP port numbers for client and server obtained from /etc/services + */ + +u_short bootps_port, bootpc_port; + + +/* + * Internet socket and interface config structures + */ + +struct sockaddr_in bind_addr; /* Listening */ +struct sockaddr_in recv_addr; /* Packet source */ +struct sockaddr_in send_addr; /* destination */ + + +/* + * option defaults + */ +int debug = 0; /* Debugging flag (level) */ +struct timeval actualtimeout = +{ /* fifteen minutes */ + 15 * 60L, /* tv_sec */ + 0 /* tv_usec */ +}; + +/* + * General + */ + +int s; /* Socket file descriptor */ +char *pktbuf; /* Receive packet buffer */ +int pktlen; +char *progname; +char *chdir_path; +char hostname[MAXHOSTNAMELEN]; /* System host name */ +struct in_addr my_ip_addr; + +/* Flags set by signal catcher. */ +PRIVATE int do_readtab = 0; +PRIVATE int do_dumptab = 0; + +/* + * Globals below are associated with the bootp database file (bootptab). + */ + +char *bootptab = CONFIG_FILE; +char *bootpd_dump = DUMPTAB_FILE; + + + +/* + * Initialization such as command-line processing is done and then the + * main server loop is started. + */ + +void +main(argc, argv) + int argc; + char **argv; +{ + struct timeval *timeout; + struct bootp *bp; + struct servent *servp; + struct hostent *hep; + char *stmp; + int n, ba_len, ra_len; + int nfound, readfds; + int standalone; + + progname = strrchr(argv[0], '/'); + if (progname) progname++; + else progname = argv[0]; + + /* + * Initialize logging. + */ + report_init(0); /* uses progname */ + + /* + * Log startup + */ + report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL); + + /* Debugging for compilers with struct padding. */ + assert(sizeof(struct bootp) == BP_MINPKTSZ); + + /* Get space for receiving packets and composing replies. */ + pktbuf = malloc(MAX_MSG_SIZE); + if (!pktbuf) { + report(LOG_ERR, "malloc failed"); + exit(1); + } + bp = (struct bootp *) pktbuf; + + /* + * Check to see if a socket was passed to us from inetd. + * + * Use getsockname() to determine if descriptor 0 is indeed a socket + * (and thus we are probably a child of inetd) or if it is instead + * something else and we are running standalone. + */ + s = 0; + ba_len = sizeof(bind_addr); + bzero((char *) &bind_addr, ba_len); + errno = 0; + standalone = TRUE; + if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) { + /* + * Descriptor 0 is a socket. Assume we are a child of inetd. + */ + if (bind_addr.sin_family == AF_INET) { + standalone = FALSE; + bootps_port = ntohs(bind_addr.sin_port); + } else { + /* Some other type of socket? */ + report(LOG_ERR, "getsockname: not an INET socket"); + } + } + + /* + * Set defaults that might be changed by option switches. + */ + stmp = NULL; + timeout = &actualtimeout; + + /* + * Read switches. + */ + for (argc--, argv++; argc > 0; argc--, argv++) { + if (argv[0][0] != '-') + break; + switch (argv[0][1]) { + + case 'c': /* chdir_path */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (stmp[0] != '/')) { + fprintf(stderr, + "bootpd: invalid chdir specification\n"); + break; + } + chdir_path = stmp; + break; + + case 'd': /* debug level */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else if (argv[1] && argv[1][0] == '-') { + /* + * Backwards-compatible behavior: + * no parameter, so just increment the debug flag. + */ + debug++; + break; + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { + fprintf(stderr, + "%s: invalid debug level\n", progname); + break; + } + debug = n; + break; + + case 'h': /* override hostname */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp) { + fprintf(stderr, + "bootpd: missing hostname\n"); + break; + } + strncpy(hostname, stmp, sizeof(hostname)-1); + break; + + case 'i': /* inetd mode */ + standalone = FALSE; + break; + + case 's': /* standalone mode */ + standalone = TRUE; + break; + + case 't': /* timeout */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { + fprintf(stderr, + "%s: invalid timeout specification\n", progname); + break; + } + actualtimeout.tv_sec = (int32) (60 * n); + /* + * If the actual timeout is zero, pass a NULL pointer + * to select so it blocks indefinitely, otherwise, + * point to the actual timeout value. + */ + timeout = (n > 0) ? &actualtimeout : NULL; + break; + + default: + fprintf(stderr, "%s: unknown switch: -%c\n", + progname, argv[0][1]); + usage(); + break; + + } /* switch */ + } /* for args */ + + /* + * Override default file names if specified on the command line. + */ + if (argc > 0) + bootptab = argv[0]; + + if (argc > 1) + bootpd_dump = argv[1]; + + /* + * Get my hostname and IP address. + */ + if (hostname[0] == '\0') { + if (gethostname(hostname, sizeof(hostname)) == -1) { + fprintf(stderr, "bootpd: can't get hostname\n"); + exit(1); + } + } + hep = gethostbyname(hostname); + if (!hep) { + fprintf(stderr, "Can not get my IP address\n"); + exit(1); + } + bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); + + if (standalone) { + /* + * Go into background and disassociate from controlling terminal. + */ + if (debug < 3) { + if (fork()) + exit(0); +#ifdef NO_SETSID + setpgrp(0,0); +#ifdef TIOCNOTTY + n = open("/dev/tty", O_RDWR); + if (n >= 0) { + ioctl(n, TIOCNOTTY, (char *) 0); + (void) close(n); + } +#endif /* TIOCNOTTY */ +#else /* SETSID */ + if (setsid() < 0) + perror("setsid"); +#endif /* SETSID */ + } /* if debug < 3 */ + + /* + * Nuke any timeout value + */ + timeout = NULL; + + } /* if standalone (1st) */ + + /* Set the cwd (i.e. to /tftpboot) */ + if (chdir_path) { + if (chdir(chdir_path) < 0) + report(LOG_ERR, "%s: chdir failed", chdir_path); + } + + /* Get the timezone. */ + tzone_init(); + + /* Allocate hash tables. */ + rdtab_init(); + + /* + * Read the bootptab file. + */ + readtab(1); /* force read */ + + if (standalone) { + + /* + * Create a socket. + */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + report(LOG_ERR, "socket: %s", get_network_errmsg()); + exit(1); + } + + /* + * Get server's listening port number + */ + servp = getservbyname("bootps", "udp"); + if (servp) { + bootps_port = ntohs((u_short) servp->s_port); + } else { + bootps_port = (u_short) IPPORT_BOOTPS; + report(LOG_ERR, + "udp/bootps: unknown service -- assuming port %d", + bootps_port); + } + + /* + * Bind socket to BOOTPS port. + */ + bind_addr.sin_family = AF_INET; + bind_addr.sin_addr.s_addr = INADDR_ANY; + bind_addr.sin_port = htons(bootps_port); + if (bind(s, (struct sockaddr *) &bind_addr, + sizeof(bind_addr)) < 0) + { + report(LOG_ERR, "bind: %s", get_network_errmsg()); + exit(1); + } + } /* if standalone (2nd)*/ + + /* + * Get destination port number so we can reply to client + */ + servp = getservbyname("bootpc", "udp"); + if (servp) { + bootpc_port = ntohs(servp->s_port); + } else { + report(LOG_ERR, + "udp/bootpc: unknown service -- assuming port %d", + IPPORT_BOOTPC); + bootpc_port = (u_short) IPPORT_BOOTPC; + } + + /* + * Set up signals to read or dump the table. + */ + if ((int) signal(SIGHUP, catcher) < 0) { + report(LOG_ERR, "signal: %s", get_errmsg()); + exit(1); + } + if ((int) signal(SIGUSR1, catcher) < 0) { + report(LOG_ERR, "signal: %s", get_errmsg()); + exit(1); + } + + /* + * Process incoming requests. + */ + for (;;) { + readfds = 1 << s; + nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, timeout); + if (nfound < 0) { + if (errno != EINTR) { + report(LOG_ERR, "select: %s", get_errmsg()); + } + /* + * Call readtab() or dumptab() here to avoid the + * dangers of doing I/O from a signal handler. + */ + if (do_readtab) { + do_readtab = 0; + readtab(1); /* force read */ + } + if (do_dumptab) { + do_dumptab = 0; + dumptab(bootpd_dump); + } + continue; + } + if (!(readfds & (1 << s))) { + if (debug > 1) + report(LOG_INFO, "exiting after %ld minutes of inactivity", + actualtimeout.tv_sec / 60); + exit(0); + } + ra_len = sizeof(recv_addr); + n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, + (struct sockaddr *) &recv_addr, &ra_len); + if (n <= 0) { + continue; + } + if (debug > 1) { + report(LOG_INFO, "recvd pkt from IP addr %s", + inet_ntoa(recv_addr.sin_addr)); + } + if (n < sizeof(struct bootp)) { + if (debug) { + report(LOG_INFO, "received short packet"); + } + continue; + } + pktlen = n; + + readtab(0); /* maybe re-read bootptab */ + + switch (bp->bp_op) { + case BOOTREQUEST: + handle_request(); + break; + case BOOTREPLY: + handle_reply(); + break; + } + } +} + + + + +/* + * Print "usage" message and exit + */ + +PRIVATE void +usage() +{ + fprintf(stderr, + "usage: bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n"); + fprintf(stderr, "\t -c n\tset current directory\n"); + fprintf(stderr, "\t -d n\tset debug level\n"); + fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); + fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); + fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); + exit(1); +} + +/* Signal catchers */ +PRIVATE void +catcher(sig) + int sig; +{ + if (sig == SIGHUP) + do_readtab = 1; + if (sig == SIGUSR1) + do_dumptab = 1; +#ifdef SYSV + /* For older "System V" derivatives with no sigset(). */ + /* XXX - Should just do it the POSIX way (sigaction). */ + signal(sig, catcher); +#endif +} + + + +/* + * Process BOOTREQUEST packet. + * + * Note: This version of the bootpd.c server never forwards + * a request to another server. That is the job of a gateway + * program such as the "bootpgw" program included here. + * + * (Also this version does not interpret the hostname field of + * the request packet; it COULD do a name->address lookup and + * forward the request there.) + */ +PRIVATE void +handle_request() +{ + struct bootp *bp = (struct bootp *) pktbuf; + struct host *hp = NULL; + struct host dummyhost; + int32 bootsize = 0; + unsigned hlen, hashcode; + int32 dest; + char realpath[1024]; + char *clntpath; + char *homedir, *bootfile; + int n; + + /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ + + /* + * If the servername field is set, compare it against us. + * If we're not being addressed, ignore this request. + * If the server name field is null, throw in our name. + */ + if (strlen(bp->bp_sname)) { + if (strcmp(bp->bp_sname, hostname)) { + if (debug) + report(LOG_INFO, "\ +ignoring request for server %s from client at %s address %s", + bp->bp_sname, netname(bp->bp_htype), + haddrtoa(bp->bp_chaddr, bp->bp_hlen)); + /* XXX - Is it correct to ignore such a request? -gwr */ + return; + } + } else { + strcpy(bp->bp_sname, hostname); + } + + /* Convert the request into a reply. */ + bp->bp_op = BOOTREPLY; + if (bp->bp_ciaddr.s_addr == 0) { + /* + * client doesnt know his IP address, + * search by hardware address. + */ + if (debug > 1) { + report(LOG_INFO, "request from %s address %s", + netname(bp->bp_htype), + haddrtoa(bp->bp_chaddr, bp->bp_hlen)); + } + hlen = haddrlength(bp->bp_htype); + if (hlen != bp->bp_hlen) { + report(LOG_NOTICE, "bad addr len from from %s address %s", + netname(bp->bp_htype), + haddrtoa(bp->bp_chaddr, hlen)); + } + dummyhost.htype = bp->bp_htype; + bcopy(bp->bp_chaddr, dummyhost.haddr, hlen); + hashcode = hash_HashFunction(bp->bp_chaddr, hlen); + hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp, + &dummyhost); + if (hp == NULL && + bp->bp_htype == HTYPE_IEEE802) + { + /* Try again with address in "canonical" form. */ + haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen); + if (debug > 1) { + report(LOG_INFO, "\ +HW addr type is IEEE 802. convert to %s and check again\n", + haddrtoa(dummyhost.haddr, bp->bp_hlen)); + } + hashcode = hash_HashFunction(dummyhost.haddr, hlen); + hp = (struct host *) hash_Lookup(hwhashtable, hashcode, + hwlookcmp, &dummyhost); + } + if (hp == NULL) { + /* + * XXX - Add dynamic IP address assignment? + */ + if (debug > 1) + report(LOG_INFO, "unknown client %s address %s", + netname(bp->bp_htype), + haddrtoa(bp->bp_chaddr, bp->bp_hlen)); + return; /* not found */ + } + (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr; + + } else { + + /* + * search by IP address. + */ + if (debug > 1) { + report(LOG_INFO, "request from IP addr %s", + inet_ntoa(bp->bp_ciaddr)); + } + dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr; + hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4); + hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp, + &dummyhost); + if (hp == NULL) { + if (debug > 1) { + report(LOG_NOTICE, "IP address not found: %s", + inet_ntoa(bp->bp_ciaddr)); + } + return; + } + } + + if (debug) { + report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr), + hp->hostname->string); + } + + /* + * If there is a response delay threshold, ignore requests + * with a timestamp lower than the threshold. + */ + if (hp->flags.min_wait) { + u_int32 t = (u_int32) ntohs(bp->bp_secs); + if (t < hp->min_wait) { + if (debug > 1) + report(LOG_INFO, + "ignoring request due to timestamp (%d < %d)", + t, hp->min_wait); + return; + } + } + +#ifdef YORK_EX_OPTION + /* + * The need for the "ex" tag arose out of the need to empty + * shared networked drives on diskless PCs. This solution is + * not very clean but it does work fairly well. + * Written by Edmund J. Sutcliffe <edmund@york.ac.uk> + * + * XXX - This could compromise security if a non-trusted user + * managed to write an entry in the bootptab with :ex=trojan: + * so I would leave this turned off unless you need it. -gwr + */ + /* Run a program, passing the client name as a parameter. */ + if (hp->flags.exec_file) { + char tst[100]; + /* XXX - Check string lengths? -gwr */ + strcpy (tst, hp->exec_file->string); + strcat (tst, " "); + strcat (tst, hp->hostname->string); + strcat (tst, " &"); + if (debug) + report(LOG_INFO, "executing %s", tst); + system(tst); /* Hope this finishes soon... */ + } +#endif /* YORK_EX_OPTION */ + + /* + * If a specific TFTP server address was specified in the bootptab file, + * fill it in, otherwise zero it. + * XXX - Rather than zero it, should it be the bootpd address? -gwr + */ + (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ? + hp->bootserver.s_addr : 0L; + +#ifdef STANFORD_PROM_COMPAT + /* + * Stanford bootp PROMs (for a Sun?) have no way to leave + * the boot file name field blank (because the boot file + * name is automatically generated from some index). + * As a work-around, this little hack allows those PROMs to + * specify "sunboot14" with the same effect as a NULL name. + * (The user specifies boot device 14 or some such magic.) + */ + if (strcmp(bp->bp_file, "sunboot14") == 0) + bp->bp_file[0] = '\0'; /* treat it as unspecified */ +#endif + + /* + * Fill in the client's proper bootfile. + * + * If the client specifies an absolute path, try that file with a + * ".host" suffix and then without. If the file cannot be found, no + * reply is made at all. + * + * If the client specifies a null or relative file, use the following + * table to determine the appropriate action: + * + * Homedir Bootfile Client's file + * specified? specified? specification Action + * ------------------------------------------------------------------- + * No No Null Send null filename + * No No Relative Discard request + * No Yes Null Send if absolute else null + * No Yes Relative Discard request *XXX + * Yes No Null Send null filename + * Yes No Relative Lookup with ".host" + * Yes Yes Null Send home/boot or bootfile + * Yes Yes Relative Lookup with ".host" *XXX + * + */ + + /* + * XXX - I don't like the policy of ignoring a client when the + * boot file is not accessible. The TFTP server might not be + * running on the same machine as the BOOTP server, in which + * case checking accessibility of the boot file is pointless. + * + * Therefore, file accessibility is now demanded ONLY if you + * define CHECK_FILE_ACCESS in the Makefile options. -gwr + */ + + /* + * The "real" path is as seen by the BOOTP daemon on this + * machine, while the client path is relative to the TFTP + * daemon chroot directory (i.e. /tftpboot). + */ + if (hp->flags.tftpdir) { + strcpy(realpath, hp->tftpdir->string); + clntpath = &realpath[strlen(realpath)]; + } else { + realpath[0] = '\0'; + clntpath = realpath; + } + + /* + * Determine client's requested homedir and bootfile. + */ + homedir = NULL; + bootfile = NULL; + if (bp->bp_file[0]) { + homedir = bp->bp_file; + bootfile = strrchr(homedir, '/'); + if (bootfile) { + if (homedir == bootfile) + homedir = NULL; + *bootfile++ = '\0'; + } else { + /* no "/" in the string */ + bootfile = homedir; + homedir = NULL; + } + if (debug > 2) { + report(LOG_INFO, "requested path=\"%s\" file=\"%s\"", + (homedir) ? homedir : "", + (bootfile) ? bootfile : ""); + } + } + + /* + * Specifications in bootptab override client requested values. + */ + if (hp->flags.homedir) + homedir = hp->homedir->string; + if (hp->flags.bootfile) + bootfile = hp->bootfile->string; + + /* + * Construct bootfile path. + */ + if (homedir) { + if (homedir[0] != '/') + strcat(clntpath, "/"); + strcat(clntpath, homedir); + homedir = NULL; + } + if (bootfile) { + if (bootfile[0] != '/') + strcat(clntpath, "/"); + strcat(clntpath, bootfile); + bootfile = NULL; + } + + /* + * First try to find the file with a ".host" suffix + */ + n = strlen(clntpath); + strcat(clntpath, "."); + strcat(clntpath, hp->hostname->string); + if (chk_access(realpath, &bootsize) < 0) { + clntpath[n] = 0; /* Try it without the suffix */ + if (chk_access(realpath, &bootsize) < 0) { + /* neither "file.host" nor "file" was found */ +#ifdef CHECK_FILE_ACCESS + + if (bp->bp_file[0]) { + /* + * Client wanted specific file + * and we didn't have it. + */ + report(LOG_NOTICE, + "requested file not found: \"%s\"", clntpath); + return; + } + /* + * Client didn't ask for a specific file and we couldn't + * access the default file, so just zero-out the bootfile + * field in the packet and continue processing the reply. + */ + bzero(bp->bp_file, sizeof(bp->bp_file)); + goto null_file_name; + +#else /* CHECK_FILE_ACCESS */ + + /* Complain only if boot file size was needed. */ + if (hp->flags.bootsize_auto) { + report(LOG_ERR, "can not determine size of file \"%s\"", + clntpath); + } + +#endif /* CHECK_FILE_ACCESS */ + } + } + strncpy(bp->bp_file, clntpath, BP_FILE_LEN); + if (debug > 2) + report(LOG_INFO, "bootfile=\"%s\"", clntpath); + +null_file_name: + + + /* + * Handle vendor options based on magic number. + */ + + if (debug > 1) { + report(LOG_INFO, "vendor magic field is %d.%d.%d.%d", + (int) ((bp->bp_vend)[0]), + (int) ((bp->bp_vend)[1]), + (int) ((bp->bp_vend)[2]), + (int) ((bp->bp_vend)[3])); + } + /* + * If this host isn't set for automatic vendor info then copy the + * specific cookie into the bootp packet, thus forcing a certain + * reply format. Only force reply format if user specified it. + */ + if (hp->flags.vm_cookie) { + /* Slam in the user specified magic number. */ + bcopy(hp->vm_cookie, bp->bp_vend, 4); + } + /* + * Figure out the format for the vendor-specific info. + * Note that bp->bp_vend may have been set above. + */ + if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) { + /* RFC1048 conformant bootp client */ + dovend_rfc1048(bp, hp, bootsize); + if (debug > 1) { + report(LOG_INFO, "sending reply (with RFC1048 options)"); + } + } +#ifdef VEND_CMU + else if (!bcmp(bp->bp_vend, vm_cmu, 4)) { + dovend_cmu(bp, hp); + if (debug > 1) { + report(LOG_INFO, "sending reply (with CMU options)"); + } + } +#endif + else { + if (debug > 1) { + report(LOG_INFO, "sending reply (with no options)"); + } + } + + dest = (hp->flags.reply_addr) ? + hp->reply_addr.s_addr : 0L; + + /* not forwarded */ + sendreply(0, dest); +} + + +/* + * Process BOOTREPLY packet. + */ +PRIVATE void +handle_reply() +{ + if (debug) { + report(LOG_INFO, "processing boot reply"); + } + /* forwarded, no destination override */ + sendreply(1, 0); +} + + +/* + * Send a reply packet to the client. 'forward' flag is set if we are + * not the originator of this reply packet. + */ +PRIVATE void +sendreply(forward, dst_override) + int forward; + int32 dst_override; +{ + struct bootp *bp = (struct bootp *) pktbuf; + struct in_addr dst; + u_short port = bootpc_port; + unsigned char *ha; + int len; + + /* + * XXX - Should honor bp_flags "broadcast" bit here. + * Temporary workaround: use the :ra=ADDR: option to + * set the reply address to the broadcast address. + */ + + /* + * If the destination address was specified explicitly + * (i.e. the broadcast address for HP compatiblity) + * then send the response to that address. Otherwise, + * act in accordance with RFC951: + * If the client IP address is specified, use that + * else if gateway IP address is specified, use that + * else make a temporary arp cache entry for the client's + * NEW IP/hardware address and use that. + */ + if (dst_override) { + dst.s_addr = dst_override; + if (debug > 1) { + report(LOG_INFO, "reply address override: %s", + inet_ntoa(dst)); + } + } else if (bp->bp_ciaddr.s_addr) { + dst = bp->bp_ciaddr; + } else if (bp->bp_giaddr.s_addr && forward == 0) { + dst = bp->bp_giaddr; + port = bootps_port; + if (debug > 1) { + report(LOG_INFO, "sending reply to gateway %s", + inet_ntoa(dst)); + } + } else { + dst = bp->bp_yiaddr; + ha = bp->bp_chaddr; + len = bp->bp_hlen; + if (len > MAXHADDRLEN) + len = MAXHADDRLEN; + + if (debug > 1) + report(LOG_INFO, "setarp %s - %s", + inet_ntoa(dst), haddrtoa(ha, len)); + setarp(s, &dst, ha, len); + } + + if ((forward == 0) && + (bp->bp_siaddr.s_addr == 0)) + { + struct ifreq *ifr; + struct in_addr siaddr; + /* + * If we are originating this reply, we + * need to find our own interface address to + * put in the bp_siaddr field of the reply. + * If this server is multi-homed, pick the + * 'best' interface (the one on the same net + * as the client). Of course, the client may + * be on the other side of a BOOTP gateway... + */ + ifr = getif(s, &dst); + if (ifr) { + struct sockaddr_in *sip; + sip = (struct sockaddr_in *) &(ifr->ifr_addr); + siaddr = sip->sin_addr; + } else { + /* Just use my "official" IP address. */ + siaddr = my_ip_addr; + } + + /* XXX - No need to set bp_giaddr here. */ + + /* Finally, set the server address field. */ + bp->bp_siaddr = siaddr; + } + /* Set up socket address for send. */ + send_addr.sin_family = AF_INET; + send_addr.sin_port = htons(port); + send_addr.sin_addr = dst; + + /* Send reply with same size packet as request used. */ + if (sendto(s, pktbuf, pktlen, 0, + (struct sockaddr *) &send_addr, + sizeof(send_addr)) < 0) + { + report(LOG_ERR, "sendto: %s", get_network_errmsg()); + } +} /* sendreply */ + + +/* nmatch() - now in getif.c */ +/* setarp() - now in hwaddr.c */ + + +/* + * This call checks read access to a file. It returns 0 if the file given + * by "path" exists and is publically readable. A value of -1 is returned if + * access is not permitted or an error occurs. Successful calls also + * return the file size in bytes using the long pointer "filesize". + * + * The read permission bit for "other" users is checked. This bit must be + * set for tftpd(8) to allow clients to read the file. + */ + +PRIVATE int +chk_access(path, filesize) + char *path; + int32 *filesize; +{ + struct stat st; + + if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) { + *filesize = (int32) st.st_size; + return 0; + } else { + return -1; + } +} + + +/* + * Now in dumptab.c : + * dumptab() + * dump_host() + * list_ipaddresses() + */ + +#ifdef VEND_CMU + +/* + * Insert the CMU "vendor" data for the host pointed to by "hp" into the + * bootp packet pointed to by "bp". + */ + +PRIVATE void +dovend_cmu(bp, hp) + struct bootp *bp; + struct host *hp; +{ + struct cmu_vend *vendp; + struct in_addr_list *taddr; + + /* + * Initialize the entire vendor field to zeroes. + */ + bzero(bp->bp_vend, sizeof(bp->bp_vend)); + + /* + * Fill in vendor information. Subnet mask, default gateway, + * domain name server, ien name server, time server + */ + vendp = (struct cmu_vend *) bp->bp_vend; + strcpy(vendp->v_magic, (char *)vm_cmu); + if (hp->flags.subnet_mask) { + (vendp->v_smask).s_addr = hp->subnet_mask.s_addr; + (vendp->v_flags) |= VF_SMASK; + if (hp->flags.gateway) { + (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr; + } + } + if (hp->flags.domain_server) { + taddr = hp->domain_server; + if (taddr->addrcount > 0) { + (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr; + if (taddr->addrcount > 1) { + (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr; + } + } + } + if (hp->flags.name_server) { + taddr = hp->name_server; + if (taddr->addrcount > 0) { + (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr; + if (taddr->addrcount > 1) { + (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr; + } + } + } + if (hp->flags.time_server) { + taddr = hp->time_server; + if (taddr->addrcount > 0) { + (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr; + if (taddr->addrcount > 1) { + (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr; + } + } + } + /* Log message now done by caller. */ +} /* dovend_cmu */ + +#endif /* VEND_CMU */ + + + +/* + * Insert the RFC1048 vendor data for the host pointed to by "hp" into the + * bootp packet pointed to by "bp". + */ +#define NEED(LEN, MSG) do \ + if (bytesleft < (LEN)) { \ + report(LOG_NOTICE, noroom, \ + hp->hostname->string, MSG); \ + return; \ + } while (0) +PRIVATE void +dovend_rfc1048(bp, hp, bootsize) + struct bootp *bp; + struct host *hp; + int32 bootsize; +{ + int bytesleft, len; + byte *vp; + char *tmpstr; + + static char noroom[] = "%s: No room for \"%s\" option"; + + vp = bp->bp_vend; + + if (hp->flags.msg_size) { + pktlen = hp->msg_size; + } else { + /* + * If the request was longer than the official length, build + * a response of that same length where the additional length + * is assumed to be part of the bp_vend (options) area. + */ + if (pktlen > sizeof(*bp)) { + if (debug > 1) + report(LOG_INFO, "request message length=%d", pktlen); + } + /* + * Check whether the request contains the option: + * Maximum DHCP Message Size (RFC1533 sec. 9.8) + * and if so, override the response length with its value. + * This request must lie within the first BP_VEND_LEN + * bytes of the option space. + */ + { + byte *p, *ep; + byte tag, len; + short msgsz = 0; + + p = vp + 4; + ep = p + BP_VEND_LEN - 4; + while (p < ep) { + tag = *p++; + /* Check for tags with no data first. */ + if (tag == TAG_PAD) + continue; + if (tag == TAG_END) + break; + /* Now scan the length byte. */ + len = *p++; + switch (tag) { + case TAG_MAX_MSGSZ: + if (len == 2) { + bcopy(p, (char*)&msgsz, 2); + msgsz = ntohs(msgsz); + } + break; + case TAG_SUBNET_MASK: + /* XXX - Should preserve this if given... */ + break; + } /* swtich */ + p += len; + } + + if (msgsz > sizeof(*bp)) { + if (debug > 1) + report(LOG_INFO, "request has DHCP msglen=%d", msgsz); + pktlen = msgsz; + } + } + } + + if (pktlen < sizeof(*bp)) { + report(LOG_ERR, "invalid response length=%d", pktlen); + pktlen = sizeof(*bp); + } + bytesleft = ((byte*)bp + pktlen) - vp; + if (pktlen > sizeof(*bp)) { + if (debug > 1) + report(LOG_INFO, "extended reply, length=%d, options=%d", + pktlen, bytesleft); + } + + /* Copy in the magic cookie */ + bcopy(vm_rfc1048, vp, 4); + vp += 4; + bytesleft -= 4; + + if (hp->flags.subnet_mask) { + /* always enough room here. */ + *vp++ = TAG_SUBNET_MASK;/* -1 byte */ + *vp++ = 4; /* -1 byte */ + insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */ + bytesleft -= 6; /* Fix real count */ + if (hp->flags.gateway) { + (void) insert_ip(TAG_GATEWAY, + hp->gateway, + &vp, &bytesleft); + } + } + if (hp->flags.bootsize) { + /* always enough room here */ + bootsize = (hp->flags.bootsize_auto) ? + ((bootsize + 511) / 512) : (hp->bootsize); /* Round up */ + *vp++ = TAG_BOOT_SIZE; + *vp++ = 2; + *vp++ = (byte) ((bootsize >> 8) & 0xFF); + *vp++ = (byte) (bootsize & 0xFF); + bytesleft -= 4; /* Tag, length, and 16 bit blocksize */ + } + /* + * This one is special: Remaining options go in the ext file. + * Only the subnet_mask, bootsize, and gateway should precede. + */ + if (hp->flags.exten_file) { + /* + * Check for room for exten_file. Add 3 to account for + * TAG_EXTEN_FILE, length, and TAG_END. + */ + len = strlen(hp->exten_file->string); + NEED((len + 3), "ef"); + *vp++ = TAG_EXTEN_FILE; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->exten_file->string, vp, len); + vp += len; + *vp++ = TAG_END; + bytesleft -= len + 3; + return; /* no more options here. */ + } + /* + * The remaining options are inserted by the following + * function (which is shared with bootpef.c). + * Keep back one byte for the TAG_END. + */ + len = dovend_rfc1497(hp, vp, bytesleft - 1); + vp += len; + bytesleft -= len; + + /* There should be at least one byte left. */ + NEED(1, "(end)"); + *vp++ = TAG_END; + bytesleft--; + + /* Log message done by caller. */ + if (bytesleft > 0) { + /* + * Zero out any remaining part of the vendor area. + */ + bzero(vp, bytesleft); + } +} /* dovend_rfc1048 */ +#undef NEED + + +/* + * Now in readfile.c: + * hwlookcmp() + * iplookcmp() + */ + +/* haddrtoa() - now in hwaddr.c */ +/* + * Now in dovend.c: + * insert_ip() + * insert_generic() + * insert_u_long() + */ + +/* get_errmsg() - now in report.c */ + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/bootpd.h b/libexec/bootpd/bootpd.h new file mode 100644 index 0000000..e5ce341 --- /dev/null +++ b/libexec/bootpd/bootpd.h @@ -0,0 +1,211 @@ +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + + +/* + * bootpd.h -- common header file for all the modules of the bootpd program. + */ + +#include "bptypes.h" +#include "hash.h" +#include "hwaddr.h" + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef PRIVATE +#define PRIVATE static +#endif + +#ifndef SIGUSR1 +#define SIGUSR1 30 /* From 4.3 <signal.h> */ +#endif + +#define MAXSTRINGLEN 80 /* Max string length */ + +/* Local definitions: */ +#define MAX_MSG_SIZE (3*512) /* Maximum packet size */ + + +/* + * Return pointer to static string which gives full network error message. + */ +#define get_network_errmsg get_errmsg + + +/* + * Data structure used to hold an arbitrary-lengthed list of IP addresses. + * The list may be shared among multiple hosts by setting the linkcount + * appropriately. + */ + +struct in_addr_list { + unsigned int linkcount, addrcount; + struct in_addr addr[1]; /* Dynamically extended */ +}; + + +/* + * Data structures used to hold shared strings and shared binary data. + * The linkcount must be set appropriately. + */ + +struct shared_string { + unsigned int linkcount; + char string[1]; /* Dynamically extended */ +}; + +struct shared_bindata { + unsigned int linkcount, length; + byte data[1]; /* Dynamically extended */ +}; + + +/* + * Flag structure which indicates which symbols have been defined for a + * given host. This information is used to determine which data should or + * should not be reported in the bootp packet vendor info field. + */ + +struct flag { + unsigned bootfile :1, + bootserver :1, + bootsize :1, + bootsize_auto :1, + cookie_server :1, + domain_server :1, + gateway :1, + generic :1, + haddr :1, + homedir :1, + htype :1, + impress_server :1, + iaddr :1, + log_server :1, + lpr_server :1, + name_server :1, + name_switch :1, + rlp_server :1, + send_name :1, + subnet_mask :1, + tftpdir :1, + time_offset :1, + time_server :1, + dump_file :1, + domain_name :1, + swap_server :1, + root_path :1, + exten_file :1, + reply_addr :1, + nis_domain :1, + nis_server :1, + ntp_server :1, + exec_file :1, + msg_size :1, + min_wait :1, + /* XXX - Add new tags here */ + vm_cookie :1; +}; + + + +/* + * The flags structure contains TRUE flags for all the fields which + * are considered valid, regardless of whether they were explicitly + * specified or indirectly inferred from another entry. + * + * The gateway and the various server fields all point to a shared list of + * IP addresses. + * + * The hostname, home directory, and bootfile are all shared strings. + * + * The generic data field is a shared binary data structure. It is used to + * hold future RFC1048 vendor data until bootpd is updated to understand it. + * + * The vm_cookie field specifies the four-octet vendor magic cookie to use + * if it is desired to always send the same response to a given host. + * + * Hopefully, the rest is self-explanatory. + */ + +struct host { + unsigned linkcount; /* hash list inserts */ + struct flag flags; /* ALL valid fields */ + struct in_addr_list *cookie_server, + *domain_server, + *gateway, + *impress_server, + *log_server, + *lpr_server, + *name_server, + *rlp_server, + *time_server, + *nis_server, + *ntp_server; + struct shared_string *bootfile, + *hostname, + *domain_name, + *homedir, + *tftpdir, + *dump_file, + *exten_file, + *root_path, + *nis_domain, + *exec_file; + struct shared_bindata *generic; + byte vm_cookie[4], + htype, /* RFC826 says this should be 16-bits but + RFC951 only allocates 1 byte. . . */ + haddr[MAXHADDRLEN]; + int32 time_offset; + unsigned int32 bootsize, + msg_size, + min_wait; + struct in_addr bootserver, + iaddr, + swap_server, + reply_addr, + subnet_mask; + /* XXX - Add new tags here (or above as appropriate) */ +}; + + + +/* + * Variables shared among modules. + */ + +extern int debug; +extern char *bootptab; +extern char *progname; + +extern u_char vm_cmu[4]; +extern u_char vm_rfc1048[4]; + +extern hash_tbl *hwhashtable; +extern hash_tbl *iphashtable; +extern hash_tbl *nmhashtable; + diff --git a/libexec/bootpd/bootptab.5 b/libexec/bootpd/bootptab.5 new file mode 100644 index 0000000..7495383 --- /dev/null +++ b/libexec/bootpd/bootptab.5 @@ -0,0 +1,395 @@ +.\" Copyright (c) 1988, 1989, 1991 Carnegie Mellon University +.\" +.\" $Header: /home/ncvs/src/usr.sbin/bootpd/bootptab.5,v 1.1.1.1 1994/09/10 14:44:54 csgr Exp $ +.\" +.TH BOOTPTAB 5 "October 31, 1991" "Carnegie Mellon University" +.UC 6 + +.SH NAME +bootptab \- Internet Bootstrap Protocol server database +.SH DESCRIPTION +The +.I bootptab +file is the configuration database file for +.IR bootpd , +the Internet Bootstrap Protocol server. +It's format is similar to that of +.IR termcap (5) +in which two-character case-sensitive tag symbols are used to +represent host parameters. These parameter declarations are separated by +colons (:), with a general format of: +.PP +.I " hostname:tg=value. . . :tg=value. . . :tg=value. . . ." +.PP +where +.I hostname +is the actual name of a bootp client (or a "dummy entry"), and +.I tg +is a two-character tag symbol. Dummy entries have an invalid hostname +(one with a "." as the first character) and are used to provide +default values used by other entries via the +.B tc=.dummy-entry +mechanism. Most tags must be followed by an equals-sign +and a value as above. Some may also appear in a boolean form with no +value (i.e. +.RI : tg :). +The currently recognized tags are: +.PP +.br + bf Bootfile +.br + bs Bootfile size in 512-octet blocks +.br + cs Cookie server address list +.br + df Merit dump file +.br + dn Domain name +.br + ds Domain name server address list +.br + ef Extension file +.br + gw Gateway address list +.br + ha Host hardware address +.br + hd Bootfile home directory +.br + hn Send client's hostname to client +.br + ht Host hardware type (see Assigned Numbers RFC) +.br + im Impress server address list +.br + ip Host IP address +.br + lg Log server address list +.br + lp LPR server address list +.br + ns IEN-116 name server address list +.br + nt NTP (time) Server (RFC 1129) +.br + ra Reply address override +.br + rl Resource location protocol server address list +.br + rp Root path to mount as root +.br + sa TFTP server address client should use +.br + sm Host subnet mask +.br + sw Swap server address +.br + tc Table continuation (points to similar "template" host entry) +.br + td TFTP root directory used by "secure" TFTP servers +.br + to Time offset in seconds from UTC +.br + ts Time server address list +.br + vm Vendor magic cookie selector +.br + yd YP (NIS) domain name +.br + ys YP (NIS) server address + +.PP +There is also a generic tag, +.RI T n , +where +.I n +is an RFC1084 vendor field tag number. Thus it is possible to immediately +take advantage of future extensions to RFC1084 without being forced to modify +.I bootpd +first. Generic data may be represented as either a stream of hexadecimal +numbers or as a quoted string of ASCII characters. The length of the generic +data is automatically determined and inserted into the proper field(s) of the +RFC1084-style bootp reply. +.PP +The following tags take a whitespace-separated list of IP addresses: +.BR cs , +.BR ds , +.BR gw , +.BR im , +.BR lg , +.BR lp , +.BR ns , +.BR nt , +.BR ra , +.BR rl , +and +.BR ts . +The +.BR ip , +.BR sa , +.BR sw , +.BR sm , +and +.B ys +tags each take a single IP address. +All IP addresses are specified in standard Internet "dot" notation +and may use decimal, octal, or hexadecimal numbers +(octal numbers begin with 0, hexadecimal numbers begin with '0x' or '0X'). +Any IP addresses may alternatively be specified as a hostname, causing +.I bootpd +to lookup the IP address for that host name using gethostbyname(3). +If the +.B ip +tag is not specified, +.I bootpd +will determine the IP address using the entry name as the host name. +(Dummy entries use an invalid host name to avoid automatic IP lookup.) +.PP +The +.B ht +tag specifies the hardware type code as either an unsigned decimal, octal, or +hexadecimal integer or one of the following symbolic names: +.B ethernet +or +.B ether +for 10Mb Ethernet, +.B ethernet3 +or +.B ether3 +for 3Mb experimental Ethernet, +.BR ieee802 , +.BR tr , +or +.B token-ring +for IEEE 802 networks, +.B pronet +for Proteon ProNET Token Ring, or +.BR chaos , +.BR arcnet , +or +.B ax.25 +for Chaos, ARCNET, and AX.25 Amateur Radio networks, respectively. +The +.B ha +tag takes a hardware address which may be specified as a host name +or in numeric form. Note that the numeric form +.I must +be specified in hexadecimal; optional periods and/or a leading '0x' may be +included for readability. The +.B ha +tag must be preceded by the +.B ht +tag (either explicitly or implicitly; see +.B tc +below). +If the hardware address is not specified and the type is specified +as either "ethernet" or "ieee802", then +.I bootpd +will try to determine the hardware address using ether_hton(3). +.PP +The hostname, home directory, and bootfile are ASCII strings which may be +optionally surrounded by double quotes ("). The client's request and the +values of the +.B hd +and +.B bf +symbols determine how the server fills in the bootfile field of the bootp +reply packet. +.PP +If the client provides a file name it is left as is. +Otherwise, if the +.B bf +option is specified its value is copied into the reply packet. +If the +.B hd +option is specified as well, its value is prepended to the +boot file copied into the reply packet. +The existence of the boot file is checked only if the +.BR bs =auto +option is used (to determine the boot file size). +A reply may be sent whether or not the boot file exists. +.PP +Some newer versions of +.I tftpd +provide a security feature to change their root directory using +the +.IR chroot (2) +system call. +The +.B td +tag may be used to inform +.I bootpd +of this special root directory used by +.IR tftpd . +(One may alternatively use the +.I bootpd +"-c chdir" option.) +The +.B hd +tag is actually relative to the root directory specified by the +.B td +tag. +For example, if the real absolute path to your BOOTP client bootfile is +/tftpboot/bootfiles/bootimage, and +.IR tftpd +uses /tftpboot as its "secure" directory, then specify the following in +.IR bootptab : +.PP +.br + :td=/tftpboot:hd=/bootfiles:bf=bootimage: +.PP +If your bootfiles are located directly in /tftpboot, use: +.PP +.br + :td=/tftpboot:hd=/:bf=bootimage: +.PP +The +.B sa +tag may be used to specify the IP address of the particular TFTP server +you wish the client to use. In the absence of this tag, +.I bootpd +will tell the client to perform TFTP to the same machine +.I bootpd +is running on. +.PP +The time offset +.B to +may be either a signed decimal integer specifying the client's +time zone offset in seconds from UTC, or the keyword +.B auto +which uses the server's time zone offset. Specifying the +.B to +symbol as a boolean has the same effect as specifying +.B auto +as its value. +.PP +The bootfile size +.B bs +may be either a decimal, octal, or hexadecimal integer specifying the size of +the bootfile in 512-octet blocks, or the keyword +.B auto +which causes the server to automatically calculate the bootfile size at each +request. As with the time offset, specifying the +.B bs +symbol as a boolean has the same effect as specifying +.B auto +as its value. +.PP +The vendor magic cookie selector (the +.B vm +tag) may take one of the following keywords: +.B auto +(indicating that vendor information is determined by the client's request), +.B rfc1048 +or +.B rfc1084 +(which always forces an RFC1084-style reply), or +.B cmu +(which always forces a CMU-style reply). +.PP +The +.B hn +tag is strictly a boolean tag; it does not take the usual equals-sign and +value. It's presence indicates that the hostname should be sent to RFC1084 +clients. +.I Bootpd +attempts to send the entire hostname as it is specified in the configuration +file; if this will not fit into the reply packet, the name is shortened to +just the host field (up to the first period, if present) and then tried. +In no case is an arbitrarily-truncated hostname sent (if nothing reasonable +will fit, nothing is sent). +.PP +Often, many host entries share common values for certain tags (such as name +servers, etc.). Rather than repeatedly specifying these tags, a full +specification can be listed for one host entry and shared by others via the +.B tc +(table continuation) mechanism. +Often, the template entry is a dummy host which doesn't actually exist and +never sends bootp requests. This feature is similar to the +.B tc +feature of +.IR termcap (5) +for similar terminals. Note that +.I bootpd +allows the +.B tc +tag symbol to appear anywhere in the host entry, unlike +.I termcap +which requires it to be the last tag. Information explicitly specified for a +host always overrides information implied by a +.B tc +tag symbol, regardless of its location within the entry. The +value of the +.B tc +tag may be the hostname or IP address of any host entry +previously listed in the configuration file. +.PP +Sometimes it is necessary to delete a specific tag after it has been inferred +via +.BR tc . +This can be done using the construction +.IB tag @ +which removes the effect of +.I tag +as in +.IR termcap (5). +For example, to completely undo an IEN-116 name server specification, use +":ns@:" at an appropriate place in the configuration entry. After removal +with +.BR @ , +a tag is eligible to be set again through the +.B tc +mechanism. +.PP +Blank lines and lines beginning with "#" are ignored in the configuration +file. Host entries are separated from one another by newlines; a single host +entry may be extended over multiple lines if the lines end with a backslash +(\\). It is also acceptable for lines to be longer than 80 characters. Tags +may appear in any order, with the following exceptions: the hostname must be +the very first field in an entry, and the hardware type must precede the +hardware address. +.PP +An example +.I /etc/bootptab +file follows: +.PP +.nf + # Sample bootptab file (domain=andrew.cmu.edu) + + .default:\\ + :hd=/usr/boot:bf=null:\\ + :ds=netserver, lancaster:\\ + :ns=pcs2, pcs1:\\ + :ts=pcs2, pcs1:\\ + :sm=255.255.255.0:\\ + :gw=gw.cs.cmu.edu:\\ + :hn:to=-18000: + + carnegie:ht=6:ha=7FF8100000AF:tc=.default: + baldwin:ht=1:ha=0800200159C3:tc=.default: + wylie:ht=1:ha=00DD00CADF00:tc=.default: + arnold:ht=1:ha=0800200102AD:tc=.default: + bairdford:ht=1:ha=08002B02A2F9:tc=.default: + bakerstown:ht=1:ha=08002B0287C8:tc=.default: + + # Special domain name server and option tags for next host + butlerjct:ha=08002001560D:ds=128.2.13.42:\\ + :T37=0x12345927AD3BCF:\\ + :T99="Special ASCII string":\\ + :tc=.default: + + gastonville:ht=6:ha=7FFF81000A47:tc=.default: + hahntown:ht=6:ha=7FFF81000434:tc=.default: + hickman:ht=6:ha=7FFF810001BA:tc=.default: + lowber:ht=1:ha=00DD00CAF000:tc=.default: + mtoliver:ht=1:ha=00DD00FE1600:tc=.default: + +.fi +.SH FILES +/etc/bootptab + +.SH "SEE ALSO" +.br +bootpd(8), tftpd(8), +.br +DARPA Internet Request For Comments RFC951, RFC1048, RFC1084, Assigned Numbers diff --git a/libexec/bootpd/bootptab.cmu b/libexec/bootpd/bootptab.cmu new file mode 100644 index 0000000..66212d4 --- /dev/null +++ b/libexec/bootpd/bootptab.cmu @@ -0,0 +1,124 @@ +# /etc/bootptab: database for bootp server (/etc/bootpd) +# (I've hacked on this but can't test it... -gwr) + +# Blank lines and lines beginning with '#' are ignored. +# +# Legend: (see bootptab.5) +# first field -- hostname (not indented) +# bf -- bootfile +# bs -- bootfile size in 512-octet blocks +# cs -- cookie servers +# df -- dump file name +# dn -- domain name +# ds -- domain name servers +# ef -- extension file +# gw -- gateways +# ha -- hardware address +# hd -- home directory for bootfiles +# hn -- host name set for client +# ht -- hardware type +# im -- impress servers +# ip -- host IP address +# lg -- log servers +# lp -- LPR servers +# ns -- IEN-116 name servers +# ra -- reply address +# rl -- resource location protocol servers +# rp -- root path +# sa -- boot server address +# sm -- subnet mask +# sw -- swap server +# tc -- template host (points to similar host entry) +# td -- TFTP directory +# to -- time offset (seconds) +# ts -- time servers +# vm -- vendor magic number +# Tn -- generic option tag n +# +# Be careful about including backslashes where they're needed. Weird (bad) +# things can happen when a backslash is omitted where one is intended. +# Also, note that generic option data must be either a string or a +# sequence of bytes where each byte is a two-digit hex value. + +# First, we define a global entry which specifies the stuff every host uses. +# (Host name lookups are relative to the domain: andrew.cmu.edu) +.default:\ + :hn:dn=cmu.edu:\ + :hd=/usr/boot:\ + :ds=netserver, lancaster:\ + :ns=pcs2, pcs1:\ + :ts=pcs2, pcs1:\ + :sm=255.255.0.0:\ + :gw=gw.cs.cmu.edu:\ + to=auto: + + +# Next, we can define different master entries for each subnet. . . +.subnet13 :sm=255.255.255.0:gw=128.2.13.1 :tc=.default: +.subnet19 :sm=255.255.255.0:gw=128.2.19.1 :tc=.default: +.subnet232 :sm=255.255.255.0:gw=128.2.232.1 :tc=.default: + +# +# We should be able to use as many levels of indirection as desired. Use +# your imagination. . . +# + + +# Individual entries (could also have different servers for some/all of these +# hosts, but we don't really use this feature at CMU): + +carnegie:tc=.subnet13:ht=ieee802:ha=7FF8100000AF: +baldwin:tc=.subnet19:ha=0800200159C3: +wylie:tc=.subnet232:ha=00DD00CADF00: +arnold:tc=.subnet19:ha=0800200102AD: +bairdford:tc=.subnet19:ha=08002B02A2F9: +bakerstown:tc=.subnet19:ha=08002B0287C8: +butlerjct:tc=.subnet232:ha=08002001560D: +gastonville:tc=.subnet232:ht=ieee802:ha=7FFF81000A47: +hahntown:tc=.subnet13:ht=ieee802:ha=7FFF81000434: +hickman:tc=.subnet19:ht=ieee802:ha=7FFF810001BA: +lowber:tc=.subnet13:ha=00DD00CAF000: +mtoliver:tc=.subnet19:ha=00DD00FE1600: +osborne:tc=.subnet232:ha=00DD00CAD600: +russelton:tc=.subnet232:ha=080020017FC3: +thornburg:tc=.subnet13:ha=080020012A33: + + +# Hmmm. . . Let's throw in some whitespace for readability. . . . + +andrew: tc=.subnet19:ha=00DD00C88900: +birdville: tc=.subnet19:ha=00DD00FE2D00: +coudersport: tc=.subnet13:ha=00DD00CB1E00: +bridgeville: tc=.subnet232:ha=080020011394: +franklin: tc=.subnet19:ha=08002B02A5D5: +hollidaysburg: tc=.subnet19:ht=ieee802:ha=7FFF810002C8: +honesdale: tc=.subnet19:ha=08002B02F83F: +huntingdon: tc=.subnet19:ha=08002B02E410: +indiana: tc=.subnet13:ha=08002B029BEC: +jimthorpe: tc=.subnet232:ha=08002B02FBBA: +kittanning: tc=.subnet232:ha=08002B0273FC: +lebanon: tc=.subnet232:ha=08002B037F67: +lewisburg: tc=.subnet19:ha=50005A1A0DE4: +middleburg: tc=.subnet232:ha=00DD00FE1200: +aspinwall: tc=.subnet13:ha=08002B03C163: +berlin: tc=.subnet13:ha=00DD000A4400: +norristown: tc=.subnet13:ha=08002001455B: +pottsville: tc=.subnet13:ha=00DD000A3700: +ridgway: tc=.subnet19:ha=08002B029425: +scranton: tc=.subnet232:ha=0800200113A1: +chalfont: tc=.subnet13:ha=08002001124B: +washington: tc=.subnet19:ha=00DD00656E00: +wellsboro: tc=.subnet13:ha=00DD00CB1C00: +bb1: tc=.subnet19:ha=00DD000A1F00: +adamstown: tc=.subnet13:ha=08002B02D0E6: +beta: tc=.subnet19:ha=02070100B197: +carbondale: tc=.subnet232:ha=08002B022A73: +clairton: tc=.subnet19:ha=080020010FD1: +egypt: tc=.subnet13:ha=00DD00847B00: +fairchance: tc=.subnet232:ha=00DD000AB100: +fairhope: tc=.subnet232:ha=00DD00CB0800: +galeton: tc=.subnet232:ha=08002001138C: +imperial: tc=.subnet232:ha=08002001130C: +kingston: tc=.subnet232:ha=080020011382: +knox: tc=.subnet232:ha=50005A1A0D2A: +lakecity: tc=.subnet13:ha=080020011380: diff --git a/libexec/bootpd/bootptab.mcs b/libexec/bootpd/bootptab.mcs new file mode 100644 index 0000000..6fa04d1 --- /dev/null +++ b/libexec/bootpd/bootptab.mcs @@ -0,0 +1,92 @@ +# /etc/bootptab: database for bootp server (/etc/bootpd) +# Last update: gwr, Sun Dec 12 19:00:00 EDT 1993 +# Blank lines and lines beginning with '#' are ignored. +# +# Legend: (see bootptab.5) +# first field -- hostname (not indented) +# bf -- bootfile +# bs -- bootfile size in 512-octet blocks +# cs -- cookie servers +# df -- dump file name +# dn -- domain name +# ds -- domain name servers +# ef -- extension file +# gw -- gateways +# ha -- hardware address +# hd -- home directory for bootfiles +# hn -- host name set for client +# ht -- hardware type +# im -- impress servers +# ip -- host IP address +# lg -- log servers +# lp -- LPR servers +# ns -- IEN-116 name servers +# ra -- reply address +# rl -- resource location protocol servers +# rp -- root path +# sa -- boot server address +# sm -- subnet mask +# sw -- swap server +# tc -- template host (points to similar host entry) +# td -- TFTP directory +# to -- time offset (seconds) +# ts -- time servers +# vm -- vendor magic number +# Tn -- generic option tag n +# +# Be careful about including backslashes where they're needed. Weird (bad) +# things can happen when a backslash is omitted where one is intended. +# Also, note that generic option data must be either a string or a +# sequence of bytes where each byte is a two-digit hex value. + +# First, we define a global entry which specifies the stuff every host uses. + +# If you leave "td" empty, run bootpd with the "-c /tftpboot" switch +# so path names (boot files) will be interpreted relative to the same +# directory as tftpd will use when opening files. +.default:\ + :hn:dn="mc.com":\ + :td=/tftpboot:\ + :ds=merlin, jericho:\ + :to=auto: + +# Next, we can define different master entries for each subnet. . . + +.subnet16:\ + :tc=.default:\ + :sm=255.255.255.0:\ + :gw=merlin:\ + :sa=merlin: + +.subnet17:\ + :tc=.default:\ + :sm=255.255.255.0:\ + :gw=merlin-gw:\ + :sa=merlin-gw: + +# +# We should be able to use as many levels of indirection as desired. Use +# your imagination. . . +# + +# Individual entries (could also have different servers for some/all of these +# hosts, but we don't really use this feature at CMU): + +# Emulex terminal server +emulex: tc=.subnet16:ha=00.00.C9.00.42.E0:bf=P4KTL0E: + +# Lantronix eps1 +eps1: tc=.subnet16:ha=00.80.A3.04.1D.78: + +# Tadpole 885 board. +tp885: tc=.subnet17:ha=08.00.4C.00.2F.74:bf=tp885sys2.cfe: + +# MVME147 VxWorks board. +#mvme147:tc=.subnet17:ha=08.00.3e.20.da.47:bf=mv147vxw.st: + +# These are just for testing + +walnut:tc=.subnet16:ha=walnut: +banana:tc=.subnet17:ha=banana: +thor:tc=.subnet17:ha=thor: +classic:tc=.subnet16:ha=classic: diff --git a/libexec/bootpd/bptypes.h b/libexec/bootpd/bptypes.h new file mode 100644 index 0000000..537da4e --- /dev/null +++ b/libexec/bootpd/bptypes.h @@ -0,0 +1,23 @@ +/* bptypes.h */ + +#ifndef BPTYPES_H +#define BPTYPES_H + +/* + * 32 bit integers are different types on various architectures + */ + +#ifndef int32 +#define int32 long +#endif +typedef unsigned int32 u_int32; + +/* + * Nice typedefs. . . + */ + +typedef int boolean; +typedef unsigned char byte; + + +#endif /* BPTYPES_H */ diff --git a/libexec/bootpd/dovend.c b/libexec/bootpd/dovend.c new file mode 100644 index 0000000..ba6ab28 --- /dev/null +++ b/libexec/bootpd/dovend.c @@ -0,0 +1,413 @@ +/* + * dovend.c : Inserts all but the first few vendor options. + */ + +#include <sys/types.h> + +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <syslog.h> + +#ifndef USE_BFUNCS +# include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +# define bcopy(a,b,c) memcpy(b,a,c) +# define bzero(p,l) memset(p,0,l) +# define bcmp(a,b,c) memcmp(a,b,c) +# define index strchr +#endif + +#include "bootp.h" +#include "bootpd.h" +#include "report.h" +#include "dovend.h" + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +PRIVATE int insert_generic P((struct shared_bindata *, byte **, int *)); + +/* + * Insert the 2nd part of the options into an option buffer. + * Return amount of space used. + * + * This inserts everything EXCEPT: + * magic cookie, subnet mask, gateway, bootsize, extension file + * Those are handled separately (in bootpd.c) to allow this function + * to be shared between bootpd and bootpef. + * + * When an "extension file" is in use, the options inserted by + * this function go into the exten_file, not the bootp response. + */ + +int +dovend_rfc1497(hp, buf, len) + struct host *hp; + byte *buf; + int len; +{ + int bytesleft = len; + byte *vp = buf; + char *tmpstr; + + static char noroom[] = "%s: No room for \"%s\" option"; +#define NEED(LEN, MSG) do \ + if (bytesleft < (LEN)) { \ + report(LOG_NOTICE, noroom, \ + hp->hostname->string, MSG); \ + return (vp - buf); \ + } while (0) + + /* + * Note that the following have already been inserted: + * magic_cookie, subnet_mask, gateway, bootsize + * + * The remaining options are inserted in order of importance. + * (Of course the importance of each is a matter of opinion.) + * The option insertion order should probably be configurable. + * + * This is the order used in the NetBSD version. Can anyone + * explain why the time_offset and swap_server are first? + * Also, why is the hostname so far down the list? -gwr + */ + + if (hp->flags.time_offset) { + NEED(6, "to"); + *vp++ = TAG_TIME_OFFSET;/* -1 byte */ + *vp++ = 4; /* -1 byte */ + insert_u_long(htonl(hp->time_offset), &vp); /* -4 bytes */ + bytesleft -= 6; + } + /* + * swap server, root path, dump path + */ + if (hp->flags.swap_server) { + NEED(6, "sw"); + /* There is just one SWAP_SERVER, so it is not an iplist. */ + *vp++ = TAG_SWAP_SERVER;/* -1 byte */ + *vp++ = 4; /* -1 byte */ + insert_u_long(hp->swap_server.s_addr, &vp); /* -4 bytes */ + bytesleft -= 6; /* Fix real count */ + } + if (hp->flags.root_path) { + /* + * Check for room for root_path. Add 2 to account for + * TAG_ROOT_PATH and length. + */ + len = strlen(hp->root_path->string); + NEED((len + 2), "rp"); + *vp++ = TAG_ROOT_PATH; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->root_path->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + if (hp->flags.dump_file) { + /* + * Check for room for dump_file. Add 2 to account for + * TAG_DUMP_FILE and length. + */ + len = strlen(hp->dump_file->string); + NEED((len + 2), "df"); + *vp++ = TAG_DUMP_FILE; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->dump_file->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + /* + * DNS server and domain + */ + if (hp->flags.domain_server) { + if (insert_ip(TAG_DOMAIN_SERVER, + hp->domain_server, + &vp, &bytesleft)) + NEED(8, "ds"); + } + if (hp->flags.domain_name) { + /* + * Check for room for domain_name. Add 2 to account for + * TAG_DOMAIN_NAME and length. + */ + len = strlen(hp->domain_name->string); + NEED((len + 2), "dn"); + *vp++ = TAG_DOMAIN_NAME; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->domain_name->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + /* + * NIS (YP) server and domain + */ + if (hp->flags.nis_server) { + if (insert_ip(TAG_NIS_SERVER, + hp->nis_server, + &vp, &bytesleft)) + NEED(8, "ds"); + } + if (hp->flags.nis_domain) { + /* + * Check for room for nis_domain. Add 2 to account for + * TAG_NIS_DOMAIN and length. + */ + len = strlen(hp->nis_domain->string); + NEED((len + 2), "dn"); + *vp++ = TAG_NIS_DOMAIN; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->nis_domain->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + /* IEN 116 name server */ + if (hp->flags.name_server) { + if (insert_ip(TAG_NAME_SERVER, + hp->name_server, + &vp, &bytesleft)) + NEED(8, "ns"); + } + if (hp->flags.rlp_server) { + if (insert_ip(TAG_RLP_SERVER, + hp->rlp_server, + &vp, &bytesleft)) + NEED(8, "rl"); + } + /* Time server (RFC 868) */ + if (hp->flags.time_server) { + if (insert_ip(TAG_TIME_SERVER, + hp->time_server, + &vp, &bytesleft)) + NEED(8, "ts"); + } + /* NTP (time) Server (RFC 1129) */ + if (hp->flags.ntp_server) { + if (insert_ip(TAG_NTP_SERVER, + hp->ntp_server, + &vp, &bytesleft)) + NEED(8, "ts"); + } + /* + * I wonder: If the hostname were "promoted" into the BOOTP + * response part, might these "extension" files possibly be + * shared between several clients? + * + * Also, why not just use longer BOOTP packets with all the + * additional length used as option data. This bootpd version + * already supports that feature by replying with the same + * packet length as the client request packet. -gwr + */ + if (hp->flags.name_switch && hp->flags.send_name) { + /* + * Check for room for hostname. Add 2 to account for + * TAG_HOST_NAME and length. + */ + len = strlen(hp->hostname->string); +#if 0 + /* + * XXX - Too much magic. The user can always set the hostname + * to the short version in the bootptab file. -gwr + */ + if ((len + 2) > bytesleft) { + /* + * Not enough room for full (domain-qualified) hostname, try + * stripping it down to just the first field (host). + */ + tmpstr = hp->hostname->string; + len = 0; + while (*tmpstr && (*tmpstr != '.')) { + tmpstr++; + len++; + } + } +#endif + NEED((len + 2), "hn"); + *vp++ = TAG_HOST_NAME; + *vp++ = (byte) (len & 0xFF); + bcopy(hp->hostname->string, vp, len); + vp += len; + bytesleft -= len + 2; + } + /* + * The rest of these are less important, so they go last. + */ + if (hp->flags.lpr_server) { + if (insert_ip(TAG_LPR_SERVER, + hp->lpr_server, + &vp, &bytesleft)) + NEED(8, "lp"); + } + if (hp->flags.cookie_server) { + if (insert_ip(TAG_COOKIE_SERVER, + hp->cookie_server, + &vp, &bytesleft)) + NEED(8, "cs"); + } + if (hp->flags.log_server) { + if (insert_ip(TAG_LOG_SERVER, + hp->log_server, + &vp, &bytesleft)) + NEED(8, "lg"); + } + /* + * XXX - Add new tags here (to insert options) + */ + if (hp->flags.generic) { + if (insert_generic(hp->generic, &vp, &bytesleft)) + NEED(64, "(generic)"); + } + /* + * The end marker is inserted by the caller. + */ + return (vp - buf); +#undef NEED +} /* dovend_rfc1497 */ + + + +/* + * Insert a tag value, a length value, and a list of IP addresses into the + * memory buffer indirectly pointed to by "dest". "tag" is the RFC1048 tag + * number to use, "iplist" is a pointer to a list of IP addresses + * (struct in_addr_list), and "bytesleft" points to an integer which + * indicates the size of the "dest" buffer. + * + * Return zero if everything fits. + * + * This is used to fill the vendor-specific area of a bootp packet in + * conformance to RFC1048. + */ + +int +insert_ip(tag, iplist, dest, bytesleft) + byte tag; + struct in_addr_list *iplist; + byte **dest; + int *bytesleft; +{ + struct in_addr *addrptr; + unsigned addrcount = 1; + byte *d; + + if (iplist == NULL) + return (0); + + if (*bytesleft >= 6) { + d = *dest; /* Save pointer for later */ + **dest = tag; + (*dest) += 2; + (*bytesleft) -= 2; /* Account for tag and length */ + addrptr = iplist->addr; + addrcount = iplist->addrcount; + while ((*bytesleft >= 4) && (addrcount > 0)) { + insert_u_long(addrptr->s_addr, dest); + addrptr++; + addrcount--; + (*bytesleft) -= 4; /* Four bytes per address */ + } + d[1] = (byte) ((*dest - d - 2) & 0xFF); + } + return (addrcount); +} + + + +/* + * Insert generic data into a bootp packet. The data is assumed to already + * be in RFC1048 format. It is inserted using a first-fit algorithm which + * attempts to insert as many tags as possible. Tags and data which are + * too large to fit are skipped; any remaining tags are tried until they + * have all been exhausted. + * Return zero if everything fits. + */ + +static int +insert_generic(gendata, buff, bytesleft) + struct shared_bindata *gendata; + byte **buff; + int *bytesleft; +{ + byte *srcptr; + int length, numbytes; + int skipped = 0; + + if (gendata == NULL) + return (0); + + srcptr = gendata->data; + length = gendata->length; + while ((length > 0) && (*bytesleft > 0)) { + switch (*srcptr) { + case TAG_END: + length = 0; /* Force an exit on next iteration */ + break; + case TAG_PAD: + *(*buff)++ = *srcptr++; + (*bytesleft)--; + length--; + break; + default: + numbytes = srcptr[1] + 2; + if (*bytesleft < numbytes) + skipped += numbytes; + else { + bcopy(srcptr, *buff, numbytes); + (*buff) += numbytes; + (*bytesleft) -= numbytes; + } + srcptr += numbytes; + length -= numbytes; + break; + } + } /* while */ + return (skipped); +} + +/* + * Insert the unsigned long "value" into memory starting at the byte + * pointed to by the byte pointer (*dest). (*dest) is updated to + * point to the next available byte. + * + * Since it is desirable to internally store network addresses in network + * byte order (in struct in_addr's), this routine expects longs to be + * passed in network byte order. + * + * However, due to the nature of the main algorithm, the long must be in + * host byte order, thus necessitating the use of ntohl() first. + */ + +void +insert_u_long(value, dest) + u_int32 value; + byte **dest; +{ + byte *temp; + int n; + + value = ntohl(value); /* Must use host byte order here */ + temp = (*dest += 4); + for (n = 4; n > 0; n--) { + *--temp = (byte) (value & 0xFF); + value >>= 8; + } + /* Final result is network byte order */ +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/dovend.h b/libexec/bootpd/dovend.h new file mode 100644 index 0000000..b30c982 --- /dev/null +++ b/libexec/bootpd/dovend.h @@ -0,0 +1,13 @@ +/* dovend.h */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern int dovend_rfc1497 P((struct host *hp, u_char *buf, int len)); +extern int insert_ip P((int, struct in_addr_list *, u_char **, int *)); +extern void insert_u_long P((u_int32, u_char **)); + +#undef P diff --git a/libexec/bootpd/dumptab.c b/libexec/bootpd/dumptab.c new file mode 100644 index 0000000..8c049b9 --- /dev/null +++ b/libexec/bootpd/dumptab.c @@ -0,0 +1,382 @@ +/* + * dumptab.c - handles dumping the database + */ + +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#include <stdio.h> +#include <stdlib.h> +#include <syslog.h> +#include <time.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#define bcopy(a,b,c) memcpy(b,a,c) +#define bzero(p,l) memset(p,0,l) +#define bcmp(a,b,c) memcmp(a,b,c) +#endif + +#include "bootp.h" +#include "hash.h" +#include "hwaddr.h" +#include "report.h" +#include "patchlevel.h" +#include "bootpd.h" + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +static void dump_generic P((FILE *, struct shared_bindata *)); +static void dump_host P((FILE *, struct host *)); +static void list_ipaddresses P((FILE *, struct in_addr_list *)); + +#undef P + +#ifndef DEBUG +void +dumptab(filename) + char *filename; +{ + report(LOG_INFO, "No dumptab support!"); +} + +#else /* DEBUG */ + +/* + * Dump the internal memory database to bootpd_dump. + */ + +void +dumptab(filename) + char *filename; +{ + int n; + struct host *hp; + FILE *fp; + long t; + /* Print symbols in alphabetical order for reader's convenience. */ + static char legend[] = "#\n# Legend:\t(see bootptab.5)\n\ +#\tfirst field -- hostname (not indented)\n\ +#\tbf -- bootfile\n\ +#\tbs -- bootfile size in 512-octet blocks\n\ +#\tcs -- cookie servers\n\ +#\tdf -- dump file name\n\ +#\tdn -- domain name\n\ +#\tds -- domain name servers\n\ +#\tef -- extension file\n\ +#\tex -- exec file (YORK_EX_OPTION)\n\ +#\tgw -- gateways\n\ +#\tha -- hardware address\n\ +#\thd -- home directory for bootfiles\n\ +#\thn -- host name set for client\n\ +#\tht -- hardware type\n\ +#\tim -- impress servers\n\ +#\tip -- host IP address\n\ +#\tlg -- log servers\n\ +#\tlp -- LPR servers\n\ +#\tms -- message size\n\ +#\tmw -- min wait (secs)\n\ +#\tns -- IEN-116 name servers\n\ +#\tnt -- NTP servers (RFC 1129)\n\ +#\tra -- reply address override\n\ +#\trl -- resource location protocol servers\n\ +#\trp -- root path\n\ +#\tsa -- boot server address\n\ +#\tsm -- subnet mask\n\ +#\tsw -- swap server\n\ +#\ttc -- template host (points to similar host entry)\n\ +#\ttd -- TFTP directory\n\ +#\tto -- time offset (seconds)\n\ +#\tts -- time servers\n\ +#\tvm -- vendor magic number\n\ +#\tyd -- YP (NIS) domain\n\ +#\tys -- YP (NIS) servers\n\ +#\tTn -- generic option tag n\n\ +\n"; + + /* + * Open bootpd.dump file. + */ + if ((fp = fopen(filename, "w")) == NULL) { + report(LOG_ERR, "error opening \"%s\": %s", + filename, get_errmsg()); + exit(1); + } + t = time(NULL); + fprintf(fp, "\n# %s %s.%d\n", progname, VERSION, PATCHLEVEL); + fprintf(fp, "# %s: dump of bootp server database.\n", filename); + fprintf(fp, "# Dump taken %s", ctime(&t)); + fwrite(legend, 1, sizeof(legend) - 1, fp); + + n = 0; + for (hp = (struct host *) hash_FirstEntry(nmhashtable); hp != NULL; + hp = (struct host *) hash_NextEntry(nmhashtable)) { + dump_host(fp, hp); + fprintf(fp, "\n"); + n++; + } + fclose(fp); + + report(LOG_INFO, "dumped %d entries to \"%s\".", n, filename); +} + + + +/* + * Dump all the available information on the host pointed to by "hp". + * The output is sent to the file pointed to by "fp". + */ + +static void +dump_host(fp, hp) + FILE *fp; + struct host *hp; +{ + /* Print symbols in alphabetical order for reader's convenience. */ + if (hp) { + fprintf(fp, "%s:", (hp->hostname ? + hp->hostname->string : "?")); + if (hp->flags.bootfile) { + fprintf(fp, "\\\n\t:bf=%s:", hp->bootfile->string); + } + if (hp->flags.bootsize) { + fprintf(fp, "\\\n\t:bs="); + if (hp->flags.bootsize_auto) { + fprintf(fp, "auto:"); + } else { + fprintf(fp, "%d:", hp->bootsize); + } + } + if (hp->flags.cookie_server) { + fprintf(fp, "\\\n\t:cs="); + list_ipaddresses(fp, hp->cookie_server); + fprintf(fp, ":"); + } + if (hp->flags.dump_file) { + fprintf(fp, "\\\n\t:df=%s:", hp->dump_file->string); + } + if (hp->flags.domain_name) { + fprintf(fp, "\\\n\t:dn=%s:", hp->domain_name->string); + } + if (hp->flags.domain_server) { + fprintf(fp, "\\\n\t:ds="); + list_ipaddresses(fp, hp->domain_server); + fprintf(fp, ":"); + } + if (hp->flags.exten_file) { + fprintf(fp, "\\\n\t:ef=%s:", hp->exten_file->string); + } + if (hp->flags.exec_file) { + fprintf(fp, "\\\n\t:ex=%s:", hp->exec_file->string); + } + if (hp->flags.gateway) { + fprintf(fp, "\\\n\t:gw="); + list_ipaddresses(fp, hp->gateway); + fprintf(fp, ":"); + } + /* FdC: swap_server (see below) */ + if (hp->flags.homedir) { + fprintf(fp, "\\\n\t:hd=%s:", hp->homedir->string); + } + /* FdC: dump_file (see above) */ + /* FdC: domain_name (see above) */ + /* FdC: root_path (see below) */ + if (hp->flags.name_switch && hp->flags.send_name) { + fprintf(fp, "\\\n\t:hn:"); + } + if (hp->flags.htype) { + int hlen = haddrlength(hp->htype); + fprintf(fp, "\\\n\t:ht=%u:", (unsigned) hp->htype); + if (hp->flags.haddr) { + fprintf(fp, "ha=\"%s\":", + haddrtoa(hp->haddr, hlen)); + } + } + if (hp->flags.impress_server) { + fprintf(fp, "\\\n\t:im="); + list_ipaddresses(fp, hp->impress_server); + fprintf(fp, ":"); + } + /* NetBSD: swap_server (see below) */ + if (hp->flags.iaddr) { + fprintf(fp, "\\\n\t:ip=%s:", inet_ntoa(hp->iaddr)); + } + if (hp->flags.log_server) { + fprintf(fp, "\\\n\t:lg="); + list_ipaddresses(fp, hp->log_server); + fprintf(fp, ":"); + } + if (hp->flags.lpr_server) { + fprintf(fp, "\\\n\t:lp="); + list_ipaddresses(fp, hp->lpr_server); + fprintf(fp, ":"); + } + if (hp->flags.msg_size) { + fprintf(fp, "\\\n\t:ms=%d:", hp->msg_size); + } + if (hp->flags.min_wait) { + fprintf(fp, "\\\n\t:mw=%d:", hp->min_wait); + } + if (hp->flags.name_server) { + fprintf(fp, "\\\n\t:ns="); + list_ipaddresses(fp, hp->name_server); + fprintf(fp, ":"); + } + if (hp->flags.ntp_server) { + fprintf(fp, "\\\n\t:nt="); + list_ipaddresses(fp, hp->ntp_server); + fprintf(fp, ":"); + } + if (hp->flags.reply_addr) { + fprintf(fp, "\\\n\t:ra=%s:", inet_ntoa(hp->reply_addr)); + } + if (hp->flags.rlp_server) { + fprintf(fp, "\\\n\t:rl="); + list_ipaddresses(fp, hp->rlp_server); + fprintf(fp, ":"); + } + if (hp->flags.root_path) { + fprintf(fp, "\\\n\t:rp=%s:", hp->root_path->string); + } + if (hp->flags.bootserver) { + fprintf(fp, "\\\n\t:sa=%s:", inet_ntoa(hp->bootserver)); + } + if (hp->flags.subnet_mask) { + fprintf(fp, "\\\n\t:sm=%s:", inet_ntoa(hp->subnet_mask)); + } + if (hp->flags.swap_server) { + fprintf(fp, "\\\n\t:sw=%s:", inet_ntoa(hp->subnet_mask)); + } + if (hp->flags.tftpdir) { + fprintf(fp, "\\\n\t:td=%s:", hp->tftpdir->string); + } + /* NetBSD: rootpath (see above) */ + /* NetBSD: domainname (see above) */ + /* NetBSD: dumpfile (see above) */ + if (hp->flags.time_offset) { + fprintf(fp, "\\\n\t:to=%ld:", hp->time_offset); + } + if (hp->flags.time_server) { + fprintf(fp, "\\\n\t:ts="); + list_ipaddresses(fp, hp->time_server); + fprintf(fp, ":"); + } + if (hp->flags.vm_cookie) { + fprintf(fp, "\\\n\t:vm="); + if (!bcmp(hp->vm_cookie, vm_rfc1048, 4)) { + fprintf(fp, "rfc1048:"); + } else if (!bcmp(hp->vm_cookie, vm_cmu, 4)) { + fprintf(fp, "cmu:"); + } else { + fprintf(fp, "%d.%d.%d.%d:", + (int) ((hp->vm_cookie)[0]), + (int) ((hp->vm_cookie)[1]), + (int) ((hp->vm_cookie)[2]), + (int) ((hp->vm_cookie)[3])); + } + } + if (hp->flags.nis_domain) { + fprintf(fp, "\\\n\t:yd=%s:", + hp->nis_domain->string); + } + if (hp->flags.nis_server) { + fprintf(fp, "\\\n\t:ys="); + list_ipaddresses(fp, hp->nis_server); + fprintf(fp, ":"); + } + /* + * XXX - Add new tags here (or above, + * so they print in alphabetical order). + */ + + if (hp->flags.generic) { + dump_generic(fp, hp->generic); + } + } +} + + +static void +dump_generic(fp, generic) + FILE *fp; + struct shared_bindata *generic; +{ + u_char *bp = generic->data; + u_char *ep = bp + generic->length; + u_char tag; + int len; + + while (bp < ep) { + tag = *bp++; + if (tag == TAG_PAD) + continue; + if (tag == TAG_END) + return; + len = *bp++; + if (bp + len > ep) { + fprintf(fp, " #junk in generic! :"); + return; + } + fprintf(fp, "\\\n\t:T%d=", tag); + while (len) { + fprintf(fp, "%02X", *bp); + bp++; + len--; + if (len) + fprintf(fp, "."); + } + fprintf(fp, ":"); + } +} + + + +/* + * Dump an entire struct in_addr_list of IP addresses to the indicated file. + * + * The addresses are printed in standard ASCII "dot" notation and separated + * from one another by a single space. A single leading space is also + * printed before the first adddress. + * + * Null lists produce no output (and no error). + */ + +static void +list_ipaddresses(fp, ipptr) + FILE *fp; + struct in_addr_list *ipptr; +{ + unsigned count; + struct in_addr *addrptr; + + if (ipptr) { + count = ipptr->addrcount; + addrptr = ipptr->addr; + while (count > 0) { + fprintf(fp, "%s", inet_ntoa(*addrptr++)); + count--; + if (count) + fprintf(fp, ", "); + } + } +} + +#endif /* DEBUG */ + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/getether.c b/libexec/bootpd/getether.c new file mode 100644 index 0000000..724a376 --- /dev/null +++ b/libexec/bootpd/getether.c @@ -0,0 +1,374 @@ +/* + * getether.c : get the ethernet address of an interface + * + * All of this code is quite system-specific. As you may well + * guess, it took a good bit of detective work to figure out! + * + * If you figure out how to do this on another system, + * please let me know. <gwr@mc.com> + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <ctype.h> +#include <syslog.h> + +#include "report.h" +#define EALEN 6 + +#if defined(ultrix) || (defined(__osf__) && defined(__alpha)) +/* + * This is really easy on Ultrix! Thanks to + * Harald Lundberg <hl@tekla.fi> for this code. + * + * The code here is not specific to the Alpha, but that was the + * only symbol we could find to identify DEC's version of OSF. + * (Perhaps we should just define DEC in the Makefile... -gwr) + */ + +#include <sys/ioctl.h> +#include <net/if.h> /* struct ifdevea */ + +getether(ifname, eap) + char *ifname, *eap; +{ + int rc = -1; + int fd; + struct ifdevea phys; + bzero(&phys, sizeof(phys)); + strcpy(phys.ifr_name, ifname); + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + report(LOG_ERR, "getether: socket(INET,DGRAM) failed"); + return -1; + } + if (ioctl(fd, SIOCRPHYSADDR, &phys) < 0) { + report(LOG_ERR, "getether: ioctl SIOCRPHYSADDR failed"); + } else { + bcopy(&phys.current_pa[0], eap, EALEN); + rc = 0; + } + close(fd); + return rc; +} + +#define GETETHER +#endif /* ultrix|osf1 */ + + +#ifdef SUNOS + +#include <sys/sockio.h> +#include <sys/time.h> /* needed by net_if.h */ +#include <net/nit_if.h> /* for NIOCBIND */ +#include <net/if.h> /* for struct ifreq */ + +getether(ifname, eap) + char *ifname; /* interface name from ifconfig structure */ + char *eap; /* Ether address (output) */ +{ + int rc = -1; + + struct ifreq ifrnit; + int nit; + + bzero((char *) &ifrnit, sizeof(ifrnit)); + strncpy(&ifrnit.ifr_name[0], ifname, IFNAMSIZ); + + nit = open("/dev/nit", 0); + if (nit < 0) { + report(LOG_ERR, "getether: open /dev/nit: %s", + get_errmsg()); + return rc; + } + do { + if (ioctl(nit, NIOCBIND, &ifrnit) < 0) { + report(LOG_ERR, "getether: NIOCBIND on nit"); + break; + } + if (ioctl(nit, SIOCGIFADDR, &ifrnit) < 0) { + report(LOG_ERR, "getether: SIOCGIFADDR on nit"); + break; + } + bcopy(&ifrnit.ifr_addr.sa_data[0], eap, EALEN); + rc = 0; + } while (0); + close(nit); + return rc; +} + +#define GETETHER +#endif /* SUNOS */ + + +#if defined(__FreeBSD__) || defined(__NetBSD__) +/* Thanks to John Brezak <brezak@ch.hp.com> for this code. */ +#include <sys/ioctl.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> + +getether(ifname, eap) + char *ifname; /* interface name from ifconfig structure */ + char *eap; /* Ether address (output) */ +{ + int fd, rc = -1; + register int n; + struct ifreq ibuf[16], ifr; + struct ifconf ifc; + register struct ifreq *ifrp, *ifend; + + /* Fetch the interface configuration */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + report(LOG_ERR, "getether: socket %s: %s", ifname, get_errmsg()); + return (fd); + } + ifc.ifc_len = sizeof(ibuf); + ifc.ifc_buf = (caddr_t) ibuf; + if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) < 0 || + ifc.ifc_len < sizeof(struct ifreq)) { + report(LOG_ERR, "getether: SIOCGIFCONF: %s", get_errmsg); + goto out; + } + /* Search interface configuration list for link layer address. */ + ifrp = ibuf; + ifend = (struct ifreq *) ((char *) ibuf + ifc.ifc_len); + while (ifrp < ifend) { + /* Look for interface */ + if (strcmp(ifname, ifrp->ifr_name) == 0 && + ifrp->ifr_addr.sa_family == AF_LINK && + ((struct sockaddr_dl *) &ifrp->ifr_addr)->sdl_type == IFT_ETHER) { + bcopy(LLADDR((struct sockaddr_dl *) &ifrp->ifr_addr), eap, EALEN); + rc = 0; + break; + } + /* Bump interface config pointer */ + n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name); + if (n < sizeof(*ifrp)) + n = sizeof(*ifrp); + ifrp = (struct ifreq *) ((char *) ifrp + n); + } + + out: + close(fd); + return (rc); +} + +#define GETETHER +#endif /* __NetBSD__ */ + + +#ifdef SVR4 +/* + * This is for "Streams TCP/IP" by Lachman Associates. + * They sure made this cumbersome! -gwr + */ + +#include <sys/sockio.h> +#include <sys/dlpi.h> +#include <stropts.h> +#ifndef NULL +#define NULL 0 +#endif + +getether(ifname, eap) + char *ifname; /* interface name from ifconfig structure */ + char *eap; /* Ether address (output) */ +{ + int rc = -1; + char devname[32]; + char tmpbuf[sizeof(union DL_primitives) + 16]; + struct strbuf cbuf; + int fd, flags; + union DL_primitives *dlp; + char *enaddr; + int unit = -1; /* which unit to attach */ + + sprintf(devname, "/dev/%s", ifname); + fd = open(devname, 2); + if (fd < 0) { + /* Try without the trailing digit. */ + char *p = devname + 5; + while (isalpha(*p)) + p++; + if (isdigit(*p)) { + unit = *p - '0'; + *p = '\0'; + } + fd = open(devname, 2); + if (fd < 0) { + report(LOG_ERR, "getether: open %s: %s", + devname, get_errmsg()); + return rc; + } + } +#ifdef DL_ATTACH_REQ + /* + * If this is a "Style 2" DLPI, then we must "attach" first + * to tell the driver which unit (board, port) we want. + * For now, decide this based on the device name. + * (Should do "info_req" and check dl_provider_style ...) + */ + if (unit >= 0) { + memset(tmpbuf, 0, sizeof(tmpbuf)); + dlp = (union DL_primitives *) tmpbuf; + dlp->dl_primitive = DL_ATTACH_REQ; + dlp->attach_req.dl_ppa = unit; + cbuf.buf = tmpbuf; + cbuf.len = DL_ATTACH_REQ_SIZE; + if (putmsg(fd, &cbuf, NULL, 0) < 0) { + report(LOG_ERR, "getether: attach: putmsg: %s", get_errmsg()); + goto out; + } + /* Recv the ack. */ + cbuf.buf = tmpbuf; + cbuf.maxlen = sizeof(tmpbuf); + flags = 0; + if (getmsg(fd, &cbuf, NULL, &flags) < 0) { + report(LOG_ERR, "getether: attach: getmsg: %s", get_errmsg()); + goto out; + } + /* + * Check the type, etc. + */ + if (dlp->dl_primitive == DL_ERROR_ACK) { + report(LOG_ERR, "getether: attach: dlpi_errno=%d, unix_errno=%d", + dlp->error_ack.dl_errno, + dlp->error_ack.dl_unix_errno); + goto out; + } + if (dlp->dl_primitive != DL_OK_ACK) { + report(LOG_ERR, "getether: attach: not OK or ERROR"); + goto out; + } + } /* unit >= 0 */ +#endif /* DL_ATTACH_REQ */ + + /* + * Get the Ethernet address the same way the ARP module + * does when it is pushed onto a new stream (bind). + * One should instead be able just do an dl_info_req + * but many drivers do not supply the hardware address + * in the response to dl_info_req (they MUST supply it + * for dl_bind_ack because the ARP module requires it). + */ + memset(tmpbuf, 0, sizeof(tmpbuf)); + dlp = (union DL_primitives *) tmpbuf; + dlp->dl_primitive = DL_BIND_REQ; + dlp->bind_req.dl_sap = 0x8FF; /* XXX - Unused SAP */ + cbuf.buf = tmpbuf; + cbuf.len = DL_BIND_REQ_SIZE; + if (putmsg(fd, &cbuf, NULL, 0) < 0) { + report(LOG_ERR, "getether: bind: putmsg: %s", get_errmsg()); + goto out; + } + /* Recv the ack. */ + cbuf.buf = tmpbuf; + cbuf.maxlen = sizeof(tmpbuf); + flags = 0; + if (getmsg(fd, &cbuf, NULL, &flags) < 0) { + report(LOG_ERR, "getether: bind: getmsg: %s", get_errmsg()); + goto out; + } + /* + * Check the type, etc. + */ + if (dlp->dl_primitive == DL_ERROR_ACK) { + report(LOG_ERR, "getether: bind: dlpi_errno=%d, unix_errno=%d", + dlp->error_ack.dl_errno, + dlp->error_ack.dl_unix_errno); + goto out; + } + if (dlp->dl_primitive != DL_BIND_ACK) { + report(LOG_ERR, "getether: bind: not OK or ERROR"); + goto out; + } + if (dlp->bind_ack.dl_addr_offset == 0) { + report(LOG_ERR, "getether: bind: ack has no address"); + goto out; + } + if (dlp->bind_ack.dl_addr_length < EALEN) { + report(LOG_ERR, "getether: bind: ack address truncated"); + goto out; + } + /* + * Copy the Ethernet address out of the message. + */ + enaddr = tmpbuf + dlp->bind_ack.dl_addr_offset; + memcpy(eap, enaddr, EALEN); + rc = 0; + + out: + close(fd); + return rc; +} + +#define GETETHER +#endif /* SVR4 */ + + +#ifdef linux +/* + * This is really easy on Linux! This version (for linux) + * written by Nigel Metheringham <nigelm@ohm.york.ac.uk> + * + * The code is almost identical to the Ultrix code - however + * the names are different to confuse the innocent :-) + * Most of this code was stolen from the Ultrix bit above. + */ + +#include <sys/ioctl.h> +#include <net/if.h> /* struct ifreq */ + +/* In a properly configured system this should be either sys/socketio.h + or sys/sockios.h, but on my distribution these don't line up correctly */ +#include <linux/sockios.h> /* Needed for IOCTL defs */ + +getether(ifname, eap) + char *ifname, *eap; +{ + int rc = -1; + int fd; + struct ifreq phys; + bzero(&phys, sizeof(phys)); + strcpy(phys.ifr_name, ifname); + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + report(LOG_ERR, "getether: socket(INET,DGRAM) failed"); + return -1; + } + if (ioctl(fd, SIOCGIFHWADDR, &phys) < 0) { + report(LOG_ERR, "getether: ioctl SIOCGIFHWADDR failed"); + } else { + bcopy(phys.ifr_hwaddr, eap, EALEN); + rc = 0; + } + close(fd); + return rc; +} + +#define GETETHER +#endif /* linux */ + + +/* If we don't know how on this system, just return an error. */ +#ifndef GETETHER +getether(ifname, eap) + char *ifname, *eap; +{ + return -1; +} + +#endif /* !GETETHER */ + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/getif.c b/libexec/bootpd/getif.c new file mode 100644 index 0000000..6cc1649 --- /dev/null +++ b/libexec/bootpd/getif.c @@ -0,0 +1,145 @@ +/* + * getif.c : get an interface structure + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> + +#if defined(SUNOS) || defined(SVR4) +#include <sys/sockio.h> +#endif +#ifdef SVR4 +#include <sys/stropts.h> +#endif + +#include <net/if.h> /* for struct ifreq */ +#include <netinet/in.h> + +#ifndef NO_UNISTD +#include <unistd.h> +#endif +#include <syslog.h> +#include <errno.h> +#include <assert.h> + +#include "getif.h" +#include "report.h" + +#ifdef __bsdi__ +#define BSD 43 +#endif + +static struct ifreq ifreq[10]; /* Holds interface configuration */ +static struct ifconf ifconf; /* points to ifreq */ + +static int nmatch(); + +/* Return a pointer to the interface struct for the passed address. */ +struct ifreq * +getif(s, addrp) + int s; /* socket file descriptor */ + struct in_addr *addrp; /* destination address on interface */ +{ + int maxmatch; + int len, m, incr; + struct ifreq *ifrq, *ifrmax; + struct sockaddr_in *sip; + char *p; + + /* If no address was supplied, just return NULL. */ + if (!addrp) + return (struct ifreq *) 0; + + /* Get the interface config if not done already. */ + if (ifconf.ifc_len == 0) { +#ifdef SVR4 + /* + * SysVr4 returns garbage if you do this the obvious way! + * This one took a while to figure out... -gwr + */ + struct strioctl ioc; + ioc.ic_cmd = SIOCGIFCONF; + ioc.ic_timout = 0; + ioc.ic_len = sizeof(ifreq); + ioc.ic_dp = (char *) ifreq; + m = ioctl(s, I_STR, (char *) &ioc); + ifconf.ifc_len = ioc.ic_len; + ifconf.ifc_req = ifreq; +#else /* SVR4 */ + ifconf.ifc_len = sizeof(ifreq); + ifconf.ifc_req = ifreq; + m = ioctl(s, SIOCGIFCONF, (caddr_t) & ifconf); +#endif /* SVR4 */ + if ((m < 0) || (ifconf.ifc_len <= 0)) { + report(LOG_ERR, "ioctl SIOCGIFCONF"); + return (struct ifreq *) 0; + } + } + maxmatch = 7; /* this many bits or less... */ + ifrmax = (struct ifreq *) 0;/* ... is not a valid match */ + p = (char *) ifreq; + len = ifconf.ifc_len; + while (len > 0) { + ifrq = (struct ifreq *) p; + sip = (struct sockaddr_in *) &ifrq->ifr_addr; + m = nmatch(addrp, &(sip->sin_addr)); + if (m > maxmatch) { + maxmatch = m; + ifrmax = ifrq; + } + /* XXX - Could this be just #ifndef IFNAMSIZ instead? -gwr */ +#if (BSD - 0) < 43 + /* BSD not defined or earlier than 4.3 */ + incr = sizeof(*ifrq); +#else /* NetBSD */ + incr = ifrq->ifr_addr.sa_len + IFNAMSIZ; +#endif /* NetBSD */ + + p += incr; + len -= incr; + } + + return ifrmax; +} + +/* + * Return the number of leading bits matching in the + * internet addresses supplied. + */ +static int +nmatch(ca, cb) + u_char *ca, *cb; /* ptrs to IP address, network order */ +{ + u_int m = 0; /* count of matching bits */ + u_int n = 4; /* bytes left, then bitmask */ + + /* Count matching bytes. */ + while (n && (*ca == *cb)) { + ca++; + cb++; + m += 8; + n--; + } + /* Now count matching bits. */ + if (n) { + n = 0x80; + while (n && ((*ca & n) == (*cb & n))) { + m++; + n >>= 1; + } + } + return (m); +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/getif.h b/libexec/bootpd/getif.h new file mode 100644 index 0000000..c51dafd --- /dev/null +++ b/libexec/bootpd/getif.h @@ -0,0 +1,7 @@ +/* getif.h */ + +#ifdef __STDC__ +extern struct ifreq *getif(int, struct in_addr *); +#else +extern struct ifreq *getif(); +#endif diff --git a/libexec/bootpd/hash.c b/libexec/bootpd/hash.c new file mode 100644 index 0000000..2ecda63 --- /dev/null +++ b/libexec/bootpd/hash.c @@ -0,0 +1,425 @@ +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + +#ifndef lint +static char rcsid[] = "$Id: hash.c,v 1.1.1.1 1994/09/10 14:44:55 csgr Exp $"; +#endif + + +/* + * Generalized hash table ADT + * + * Provides multiple, dynamically-allocated, variable-sized hash tables on + * various data and keys. + * + * This package attempts to follow some of the coding conventions suggested + * by Bob Sidebotham and the AFS Clean Code Committee of the + * Information Technology Center at Carnegie Mellon. + */ + + +#include <sys/types.h> +#include <stdlib.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#define bcopy(a,b,c) memcpy(b,a,c) +#define bzero(p,l) memset(p,0,l) +#define bcmp(a,b,c) memcmp(a,b,c) +#endif + +#include "hash.h" + +#define TRUE 1 +#define FALSE 0 +#ifndef NULL +#define NULL 0 +#endif + +/* + * This can be changed to make internal routines visible to debuggers, etc. + */ +#ifndef PRIVATE +#define PRIVATE static +#endif + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +PRIVATE void hashi_FreeMembers P((hash_member *, hash_freefp)); + +#undef P + + + +/* + * Hash table initialization routine. + * + * This routine creates and intializes a hash table of size "tablesize" + * entries. Successful calls return a pointer to the hash table (which must + * be passed to other hash routines to identify the hash table). Failed + * calls return NULL. + */ + +hash_tbl * +hash_Init(tablesize) + unsigned tablesize; +{ + register hash_tbl *hashtblptr; + register unsigned totalsize; + + if (tablesize > 0) { + totalsize = sizeof(hash_tbl) + + sizeof(hash_member *) * (tablesize - 1); + hashtblptr = (hash_tbl *) malloc(totalsize); + if (hashtblptr) { + bzero((char *) hashtblptr, totalsize); + hashtblptr->size = tablesize; /* Success! */ + hashtblptr->bucketnum = 0; + hashtblptr->member = (hashtblptr->table)[0]; + } + } else { + hashtblptr = NULL; /* Disallow zero-length tables */ + } + return hashtblptr; /* NULL if failure */ +} + + + +/* + * Frees an entire linked list of bucket members (used in the open + * hashing scheme). Does nothing if the passed pointer is NULL. + */ + +PRIVATE void +hashi_FreeMembers(bucketptr, free_data) + hash_member *bucketptr; + hash_freefp free_data; +{ + hash_member *nextbucket; + while (bucketptr) { + nextbucket = bucketptr->next; + (*free_data) (bucketptr->data); + free((char *) bucketptr); + bucketptr = nextbucket; + } +} + + + + +/* + * This routine re-initializes the hash table. It frees all the allocated + * memory and resets all bucket pointers to NULL. + */ + +void +hash_Reset(hashtable, free_data) + hash_tbl *hashtable; + hash_freefp free_data; +{ + hash_member **bucketptr; + unsigned i; + + bucketptr = hashtable->table; + for (i = 0; i < hashtable->size; i++) { + hashi_FreeMembers(*bucketptr, free_data); + *bucketptr++ = NULL; + } + hashtable->bucketnum = 0; + hashtable->member = (hashtable->table)[0]; +} + + + +/* + * Generic hash function to calculate a hash code from the given string. + * + * For each byte of the string, this function left-shifts the value in an + * accumulator and then adds the byte into the accumulator. The contents of + * the accumulator is returned after the entire string has been processed. + * It is assumed that this result will be used as the "hashcode" parameter in + * calls to other functions in this package. These functions automatically + * adjust the hashcode for the size of each hashtable. + * + * This algorithm probably works best when the hash table size is a prime + * number. + * + * Hopefully, this function is better than the previous one which returned + * the sum of the squares of all the bytes. I'm still open to other + * suggestions for a default hash function. The programmer is more than + * welcome to supply his/her own hash function as that is one of the design + * features of this package. + */ + +unsigned +hash_HashFunction(string, len) + unsigned char *string; + register unsigned len; +{ + register unsigned accum; + + accum = 0; + for (; len > 0; len--) { + accum <<= 1; + accum += (unsigned) (*string++ & 0xFF); + } + return accum; +} + + + +/* + * Returns TRUE if at least one entry for the given key exists; FALSE + * otherwise. + */ + +int +hash_Exists(hashtable, hashcode, compare, key) + hash_tbl *hashtable; + unsigned hashcode; + hash_cmpfp compare; + hash_datum *key; +{ + register hash_member *memberptr; + + memberptr = (hashtable->table)[hashcode % (hashtable->size)]; + while (memberptr) { + if ((*compare) (key, memberptr->data)) { + return TRUE; /* Entry does exist */ + } + memberptr = memberptr->next; + } + return FALSE; /* Entry does not exist */ +} + + + +/* + * Insert the data item "element" into the hash table using "hashcode" + * to determine the bucket number, and "compare" and "key" to determine + * its uniqueness. + * + * If the insertion is successful 0 is returned. If a matching entry + * already exists in the given bucket of the hash table, or some other error + * occurs, -1 is returned and the insertion is not done. + */ + +int +hash_Insert(hashtable, hashcode, compare, key, element) + hash_tbl *hashtable; + unsigned hashcode; + hash_cmpfp compare; + hash_datum *key, *element; +{ + hash_member *temp; + + hashcode %= hashtable->size; + if (hash_Exists(hashtable, hashcode, compare, key)) { + return -1; /* At least one entry already exists */ + } + temp = (hash_member *) malloc(sizeof(hash_member)); + if (!temp) + return -1; /* malloc failed! */ + + temp->data = element; + temp->next = (hashtable->table)[hashcode]; + (hashtable->table)[hashcode] = temp; + return 0; /* Success */ +} + + + +/* + * Delete all data elements which match the given key. If at least one + * element is found and the deletion is successful, 0 is returned. + * If no matching elements can be found in the hash table, -1 is returned. + */ + +int +hash_Delete(hashtable, hashcode, compare, key, free_data) + hash_tbl *hashtable; + unsigned hashcode; + hash_cmpfp compare; + hash_datum *key; + hash_freefp free_data; +{ + hash_member *memberptr, *tempptr; + hash_member *previous = NULL; + int retval; + + retval = -1; + hashcode %= hashtable->size; + + /* + * Delete the first member of the list if it matches. Since this moves + * the second member into the first position we have to keep doing this + * over and over until it no longer matches. + */ + memberptr = (hashtable->table)[hashcode]; + while (memberptr && (*compare) (key, memberptr->data)) { + (hashtable->table)[hashcode] = memberptr->next; + /* + * Stop hashi_FreeMembers() from deleting the whole list! + */ + memberptr->next = NULL; + hashi_FreeMembers(memberptr, free_data); + memberptr = (hashtable->table)[hashcode]; + retval = 0; + } + + /* + * Now traverse the rest of the list + */ + if (memberptr) { + previous = memberptr; + memberptr = memberptr->next; + } + while (memberptr) { + if ((*compare) (key, memberptr->data)) { + tempptr = memberptr; + previous->next = memberptr = memberptr->next; + /* + * Put the brakes on hashi_FreeMembers(). . . . + */ + tempptr->next = NULL; + hashi_FreeMembers(tempptr, free_data); + retval = 0; + } else { + previous = memberptr; + memberptr = memberptr->next; + } + } + return retval; +} + + + +/* + * Locate and return the data entry associated with the given key. + * + * If the data entry is found, a pointer to it is returned. Otherwise, + * NULL is returned. + */ + +hash_datum * +hash_Lookup(hashtable, hashcode, compare, key) + hash_tbl *hashtable; + unsigned hashcode; + hash_cmpfp compare; + hash_datum *key; +{ + hash_member *memberptr; + + memberptr = (hashtable->table)[hashcode % (hashtable->size)]; + while (memberptr) { + if ((*compare) (key, memberptr->data)) { + return (memberptr->data); + } + memberptr = memberptr->next; + } + return NULL; +} + + + +/* + * Return the next available entry in the hashtable for a linear search + */ + +hash_datum * +hash_NextEntry(hashtable) + hash_tbl *hashtable; +{ + register unsigned bucket; + register hash_member *memberptr; + + /* + * First try to pick up where we left off. + */ + memberptr = hashtable->member; + if (memberptr) { + hashtable->member = memberptr->next; /* Set up for next call */ + return memberptr->data; /* Return the data */ + } + /* + * We hit the end of a chain, so look through the array of buckets + * until we find a new chain (non-empty bucket) or run out of buckets. + */ + bucket = hashtable->bucketnum + 1; + while ((bucket < hashtable->size) && + !(memberptr = (hashtable->table)[bucket])) { + bucket++; + } + + /* + * Check to see if we ran out of buckets. + */ + if (bucket >= hashtable->size) { + /* + * Reset to top of table for next call. + */ + hashtable->bucketnum = 0; + hashtable->member = (hashtable->table)[0]; + /* + * But return end-of-table indication to the caller this time. + */ + return NULL; + } + /* + * Must have found a non-empty bucket. + */ + hashtable->bucketnum = bucket; + hashtable->member = memberptr->next; /* Set up for next call */ + return memberptr->data; /* Return the data */ +} + + + +/* + * Return the first entry in a hash table for a linear search + */ + +hash_datum * +hash_FirstEntry(hashtable) + hash_tbl *hashtable; +{ + hashtable->bucketnum = 0; + hashtable->member = (hashtable->table)[0]; + return hash_NextEntry(hashtable); +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/hash.h b/libexec/bootpd/hash.h new file mode 100644 index 0000000..51d0a5e --- /dev/null +++ b/libexec/bootpd/hash.h @@ -0,0 +1,158 @@ +#ifndef HASH_H +#define HASH_H +/* hash.h */ +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + +/* + * Generalized hash table ADT + * + * Provides multiple, dynamically-allocated, variable-sized hash tables on + * various data and keys. + * + * This package attempts to follow some of the coding conventions suggested + * by Bob Sidebotham and the AFS Clean Code Committee. + */ + + +/* + * The user must supply the following: + * + * 1. A comparison function which is declared as: + * + * int compare(data1, data2) + * hash_datum *data1, *data2; + * + * This function must compare the desired fields of data1 and + * data2 and return TRUE (1) if the data should be considered + * equivalent (i.e. have the same key value) or FALSE (0) + * otherwise. This function is called through a pointer passed to + * the various hashtable functions (thus pointers to different + * functions may be passed to effect different tests on different + * hash tables). + * + * Internally, all the functions of this package always call the + * compare function with the "key" parameter as the first parameter, + * and a full data element as the second parameter. Thus, the key + * and element arguments to functions such as hash_Lookup() may + * actually be of different types and the programmer may provide a + * compare function which compares the two different object types + * as desired. + * + * Example: + * + * int compare(key, element) + * char *key; + * struct some_complex_structure *element; + * { + * return !strcmp(key, element->name); + * } + * + * key = "John C. Doe" + * element = &some_complex_structure + * hash_Lookup(table, hashcode, compare, key); + * + * 2. A hash function yielding an unsigned integer value to be used + * as the hashcode (index into the hashtable). Thus, the user + * may hash on whatever data is desired and may use several + * different hash functions for various different hash tables. + * The actual hash table index will be the passed hashcode modulo + * the hash table size. + * + * A generalized hash function, hash_HashFunction(), is included + * with this package to make things a little easier. It is not + * guarenteed to use the best hash algorithm in existence. . . . + */ + + + +/* + * Various hash table definitions + */ + + +/* + * Define "hash_datum" as a universal data type + */ +#ifdef __STDC__ +typedef void hash_datum; +#else +typedef char hash_datum; +#endif + +typedef struct hash_memberstruct hash_member; +typedef struct hash_tblstruct hash_tbl; +typedef struct hash_tblstruct_hdr hash_tblhdr; + +struct hash_memberstruct { + hash_member *next; + hash_datum *data; +}; + +struct hash_tblstruct_hdr { + unsigned size, bucketnum; + hash_member *member; +}; + +struct hash_tblstruct { + unsigned size, bucketnum; + hash_member *member; /* Used for linear dump */ + hash_member *table[1]; /* Dynamically extended */ +}; + +/* ANSI function prototypes or empty arg list? */ +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +typedef int (*hash_cmpfp) P((hash_datum *, hash_datum *)); +typedef void (*hash_freefp) P((hash_datum *)); + +extern hash_tbl *hash_Init P((u_int tablesize)); + +extern void hash_Reset P((hash_tbl *tbl, hash_freefp)); + +extern unsigned hash_HashFunction P((u_char *str, u_int len)); + +extern int hash_Exists P((hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key)); + +extern int hash_Insert P((hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key, + hash_datum *element)); + +extern int hash_Delete P((hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key, + hash_freefp)); + +extern hash_datum *hash_Lookup P((hash_tbl *, u_int code, + hash_cmpfp, hash_datum *key)); + +extern hash_datum *hash_FirstEntry P((hash_tbl *)); + +extern hash_datum *hash_NextEntry P((hash_tbl *)); + +#undef P + +#endif /* HASH_H */ diff --git a/libexec/bootpd/hwaddr.c b/libexec/bootpd/hwaddr.c new file mode 100644 index 0000000..840b953 --- /dev/null +++ b/libexec/bootpd/hwaddr.c @@ -0,0 +1,294 @@ +/* + * hwaddr.c - routines that deal with hardware addresses. + * (i.e. Ethernet) + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/ioctl.h> + +#if defined(SUNOS) || defined(SVR4) +#include <sys/sockio.h> +#endif +#ifdef SVR4 +#include <sys/stream.h> +#include <stropts.h> +#include <fcntl.h> +#endif + +#include <net/if_arp.h> +#include <netinet/in.h> +#include <stdio.h> +#ifndef NO_UNISTD +#include <unistd.h> +#endif +#include <syslog.h> + +#ifndef USE_BFUNCS +/* Yes, memcpy is OK here (no overlapped copies). */ +#include <memory.h> +#define bcopy(a,b,c) memcpy(b,a,c) +#define bzero(p,l) memset(p,0,l) +#define bcmp(a,b,c) memcmp(a,b,c) +#endif + +/* For BSD 4.4, set arp entry by writing to routing socket */ +#if defined(BSD) +#if BSD >= 199306 +extern int bsd_arp_set __P((struct in_addr *, char *, int)); +#endif +#endif + +#include "bptypes.h" +#include "hwaddr.h" +#include "report.h" + +extern int debug; + +/* + * Hardware address lengths (in bytes) and network name based on hardware + * type code. List in order specified by Assigned Numbers RFC; Array index + * is hardware type code. Entries marked as zero are unknown to the author + * at this time. . . . + */ + +struct hwinfo hwinfolist[] = +{ + {0, "Reserved"}, /* Type 0: Reserved (don't use this) */ + {6, "Ethernet"}, /* Type 1: 10Mb Ethernet (48 bits) */ + {1, "3Mb Ethernet"}, /* Type 2: 3Mb Ethernet (8 bits) */ + {0, "AX.25"}, /* Type 3: Amateur Radio AX.25 */ + {1, "ProNET"}, /* Type 4: Proteon ProNET Token Ring */ + {0, "Chaos"}, /* Type 5: Chaos */ + {6, "IEEE 802"}, /* Type 6: IEEE 802 Networks */ + {0, "ARCNET"} /* Type 7: ARCNET */ +}; +int hwinfocnt = sizeof(hwinfolist) / sizeof(hwinfolist[0]); + + +/* + * Setup the arp cache so that IP address 'ia' will be temporarily + * bound to hardware address 'ha' of length 'len'. + */ +void +setarp(s, ia, ha, len) + int s; /* socket fd */ + struct in_addr *ia; + u_char *ha; + int len; +{ +#ifdef SIOCSARP + struct arpreq arpreq; /* Arp request ioctl block */ + struct sockaddr_in *si; +#ifdef SVR4 + int fd; + struct strioctl iocb; +#endif /* SVR4 */ + + bzero((caddr_t) & arpreq, sizeof(arpreq)); + arpreq.arp_flags = ATF_INUSE | ATF_COM; + + /* Set up the protocol address. */ + arpreq.arp_pa.sa_family = AF_INET; + si = (struct sockaddr_in *) &arpreq.arp_pa; + si->sin_addr = *ia; + + /* Set up the hardware address. */ + bcopy(ha, arpreq.arp_ha.sa_data, len); + +#ifdef SVR4 + /* + * And now the stuff for System V Rel 4.x which does not + * appear to allow SIOCxxx ioctls on a socket descriptor. + * Thanks to several people: (all sent the same fix) + * Barney Wolff <barney@databus.com>, + * bear@upsys.se (Bj|rn Sj|holm), + * Michael Kuschke <Michael.Kuschke@Materna.DE>, + */ + if ((fd=open("/dev/arp", O_RDWR)) < 0) { + report(LOG_ERR, "open /dev/arp: %s\n", get_errmsg()); + } + iocb.ic_cmd = SIOCSARP; + iocb.ic_timout = 0; + iocb.ic_dp = (char *)&arpreq; + iocb.ic_len = sizeof(arpreq); + if (ioctl(fd, I_STR, (caddr_t)&iocb) < 0) { + report(LOG_ERR, "ioctl I_STR: %s\n", get_errmsg()); + } + close (fd); + +#else /* SVR4 */ + /* + * On SunOS, the ioctl sometimes returns ENXIO, and it + * appears to happen when the ARP cache entry you tried + * to add is already in the cache. (Sigh...) + * XXX - Should this error simply be ignored? -gwr + */ + if (ioctl(s, SIOCSARP, (caddr_t) & arpreq) < 0) { + report(LOG_ERR, "ioctl SIOCSARP: %s", get_errmsg()); + } +#endif /* SVR4 */ +#else /* SIOCSARP */ +#if defined(BSD) && (BSD >= 199306) + bsd_arp_set(ia, ha, len); +#else /* Not BSD 4.4, and SIOCSARP not defined */ + /* + * Oh well, SIOCSARP is not defined. Just run arp(8). + * XXX - Gag! + */ + char buf[256]; + int status; + + sprintf(buf, "arp -s %s %s temp", + inet_ntoa(*ia), haddrtoa(ha, len)); + if (debug > 2) + report(LOG_INFO, buf); + status = system(buf); + if (status) + report(LOG_ERR, "arp failed, exit code=0x%x", status); + return; +#endif /* ! 4.4 BSD */ +#endif /* SIOCSARP */ +} + + +/* + * Convert a hardware address to an ASCII string. + */ +char * +haddrtoa(haddr, hlen) + u_char *haddr; + int hlen; +{ + static char haddrbuf[3 * MAXHADDRLEN + 1]; + char *bufptr; + + if (hlen > MAXHADDRLEN) + hlen = MAXHADDRLEN; + + bufptr = haddrbuf; + while (hlen > 0) { + sprintf(bufptr, "%02X:", (unsigned) (*haddr++ & 0xFF)); + bufptr += 3; + hlen--; + } + bufptr[-1] = 0; + return (haddrbuf); +} + + +/* + * haddr_conv802() + * -------------- + * + * Converts a backwards address to a canonical address and a canonical address + * to a backwards address. + * + * INPUTS: + * adr_in - pointer to six byte string to convert (unsigned char *) + * addr_len - how many bytes to convert + * + * OUTPUTS: + * addr_out - The string is updated to contain the converted address. + * + * CALLER: + * many + * + * DATA: + * Uses conv802table to bit-reverse the address bytes. + */ + +static u_char conv802table[256] = +{ + /* 0x00 */ 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + /* 0x08 */ 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + /* 0x10 */ 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + /* 0x18 */ 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + /* 0x20 */ 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + /* 0x28 */ 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + /* 0x30 */ 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + /* 0x38 */ 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + /* 0x40 */ 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + /* 0x48 */ 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + /* 0x50 */ 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + /* 0x58 */ 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + /* 0x60 */ 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + /* 0x68 */ 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + /* 0x70 */ 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + /* 0x78 */ 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + /* 0x80 */ 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + /* 0x88 */ 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + /* 0x90 */ 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + /* 0x98 */ 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + /* 0xA0 */ 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + /* 0xA8 */ 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + /* 0xB0 */ 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + /* 0xB8 */ 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + /* 0xC0 */ 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + /* 0xC8 */ 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + /* 0xD0 */ 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + /* 0xD8 */ 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + /* 0xE0 */ 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + /* 0xE8 */ 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + /* 0xF0 */ 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + /* 0xF8 */ 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF, +}; + +void +haddr_conv802(addr_in, addr_out, len) + register u_char *addr_in, *addr_out; + int len; +{ + u_char *lim; + + lim = addr_out + len; + while (addr_out < lim) + *addr_out++ = conv802table[*addr_in++]; +} + +#if 0 +/* + * For the record, here is a program to generate the + * bit-reverse table above. + */ +static int +bitrev(n) + int n; +{ + int i, r; + + r = 0; + for (i = 0; i < 8; i++) { + r <<= 1; + r |= (n & 1); + n >>= 1; + } + return r; +} + +main() +{ + int i; + for (i = 0; i <= 0xFF; i++) { + if ((i & 7) == 0) + printf("/* 0x%02X */", i); + printf(" 0x%02X,", bitrev(i)); + if ((i & 7) == 7) + printf("\n"); + } +} + +#endif + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/hwaddr.h b/libexec/bootpd/hwaddr.h new file mode 100644 index 0000000..dea7158 --- /dev/null +++ b/libexec/bootpd/hwaddr.h @@ -0,0 +1,39 @@ +/* hwaddr.h */ +#ifndef HWADDR_H +#define HWADDR_H + +#define MAXHADDRLEN 8 /* Max hw address length in bytes */ + +/* + * This structure holds information about a specific network type. The + * length of the network hardware address is stored in "hlen". + * The string pointed to by "name" is the cononical name of the network. + */ +struct hwinfo { + unsigned int hlen; + char *name; +}; + +extern struct hwinfo hwinfolist[]; +extern int hwinfocnt; + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern void setarp P((int, struct in_addr *, u_char *, int)); +extern char *haddrtoa P((u_char *, int)); +extern void haddr_conv802 P((u_char *, u_char *, int)); + +#undef P + +/* + * Return the length in bytes of a hardware address of the given type. + * Return the canonical name of the network of the given type. + */ +#define haddrlength(type) ((hwinfolist[(int) (type)]).hlen) +#define netname(type) ((hwinfolist[(int) (type)]).name) + +#endif /* HWADDR_H */ diff --git a/libexec/bootpd/lookup.c b/libexec/bootpd/lookup.c new file mode 100644 index 0000000..2a30a59 --- /dev/null +++ b/libexec/bootpd/lookup.c @@ -0,0 +1,126 @@ +/* + * lookup.c - Lookup IP address, HW address, netmask + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <netinet/in.h> + +#ifdef ETC_ETHERS +#include <netinet/if_ether.h> +extern int ether_hostton(); +#endif + +#include <netdb.h> +#include <syslog.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#define bcopy(a,b,c) memcpy(b,a,c) +#endif + +#include "bootp.h" +#include "lookup.h" +#include "report.h" + +/* + * Lookup an Ethernet address and return it. + * Return NULL if addr not found. + */ +u_char * +lookup_hwa(hostname, htype) + char *hostname; + int htype; +{ + switch (htype) { + + /* XXX - How is this done on other systems? -gwr */ +#ifdef ETC_ETHERS + case HTYPE_ETHERNET: + case HTYPE_IEEE802: + { + static struct ether_addr ea; + /* This does a lookup in /etc/ethers */ + if (ether_hostton(hostname, &ea)) { + report(LOG_ERR, "no HW addr for host \"%s\"", + hostname); + return (u_char *) 0; + } + return (u_char *) & ea; + } +#endif /* ETC_ETHERS */ + + default: + report(LOG_ERR, "no lookup for HW addr type %d", htype); + } /* switch */ + + /* If the system can't do it, just return an error. */ + return (u_char *) 0; +} + + +/* + * Lookup an IP address. + * Return non-zero on failure. + */ +int +lookup_ipa(hostname, result) + char *hostname; + u_int32 *result; +{ + struct hostent *hp; + hp = gethostbyname(hostname); + if (!hp) + return -1; + bcopy(hp->h_addr, result, sizeof(*result)); + return 0; +} + + +/* + * Lookup a netmask + * Return non-zero on failure. + * + * XXX - This is OK as a default, but to really make this automatic, + * we would need to get the subnet mask from the ether interface. + * If this is wrong, specify the correct value in the bootptab. + */ +int +lookup_netmask(addr, result) + u_int32 addr; /* both in network order */ + u_int32 *result; +{ + int32 m, a; + + a = ntohl(addr); + m = 0; + + if (IN_CLASSA(a)) + m = IN_CLASSA_NET; + + if (IN_CLASSB(a)) + m = IN_CLASSB_NET; + + if (IN_CLASSC(a)) + m = IN_CLASSC_NET; + + if (!m) + return -1; + *result = htonl(m); + return 0; +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/lookup.h b/libexec/bootpd/lookup.h new file mode 100644 index 0000000..04805d8 --- /dev/null +++ b/libexec/bootpd/lookup.h @@ -0,0 +1,15 @@ +/* lookup.h */ + +#include "bptypes.h" /* for int32, u_int32 */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern u_char *lookup_hwa P((char *hostname, int htype)); +extern int lookup_ipa P((char *hostname, u_int32 *addr)); +extern int lookup_netmask P((u_int32 addr, u_int32 *mask)); + +#undef P diff --git a/libexec/bootpd/patchlevel.h b/libexec/bootpd/patchlevel.h new file mode 100644 index 0000000..782959e --- /dev/null +++ b/libexec/bootpd/patchlevel.h @@ -0,0 +1,3 @@ +/* patchlevel.h */ +#define VERSION "2.4" +#define PATCHLEVEL 1 diff --git a/libexec/bootpd/readfile.c b/libexec/bootpd/readfile.c new file mode 100644 index 0000000..4a9d993 --- /dev/null +++ b/libexec/bootpd/readfile.c @@ -0,0 +1,2097 @@ +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + +#ifndef lint +static char rcsid[] = "$Id: readfile.c,v 1.1.1.1 1994/09/10 14:44:55 csgr Exp $"; +#endif + + +/* + * bootpd configuration file reading code. + * + * The routines in this file deal with reading, interpreting, and storing + * the information found in the bootpd configuration file (usually + * /etc/bootptab). + */ + + +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/time.h> +#include <netinet/in.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include <syslog.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#define bcopy(a,b,c) memcpy(b,a,c) +#define bzero(p,l) memset(p,0,l) +#define bcmp(a,b,c) memcmp(a,b,c) +#endif + +#include "bootp.h" +#include "hash.h" +#include "hwaddr.h" +#include "lookup.h" +#include "readfile.h" +#include "report.h" +#include "tzone.h" +#include "bootpd.h" + +#define HASHTABLESIZE 257 /* Hash table size (prime) */ + +/* Non-standard hardware address type (see bootp.h) */ +#define HTYPE_DIRECT 0 + +/* Error codes returned by eval_symbol: */ +#define SUCCESS 0 +#define E_END_OF_ENTRY (-1) +#define E_SYNTAX_ERROR (-2) +#define E_UNKNOWN_SYMBOL (-3) +#define E_BAD_IPADDR (-4) +#define E_BAD_HWADDR (-5) +#define E_BAD_LONGWORD (-6) +#define E_BAD_HWATYPE (-7) +#define E_BAD_PATHNAME (-8) +#define E_BAD_VALUE (-9) + +/* Tag idendities. */ +#define SYM_NULL 0 +#define SYM_BOOTFILE 1 +#define SYM_COOKIE_SERVER 2 +#define SYM_DOMAIN_SERVER 3 +#define SYM_GATEWAY 4 +#define SYM_HWADDR 5 +#define SYM_HOMEDIR 6 +#define SYM_HTYPE 7 +#define SYM_IMPRESS_SERVER 8 +#define SYM_IPADDR 9 +#define SYM_LOG_SERVER 10 +#define SYM_LPR_SERVER 11 +#define SYM_NAME_SERVER 12 +#define SYM_RLP_SERVER 13 +#define SYM_SUBNET_MASK 14 +#define SYM_TIME_OFFSET 15 +#define SYM_TIME_SERVER 16 +#define SYM_VENDOR_MAGIC 17 +#define SYM_SIMILAR_ENTRY 18 +#define SYM_NAME_SWITCH 19 +#define SYM_BOOTSIZE 20 +#define SYM_BOOT_SERVER 22 +#define SYM_TFTPDIR 23 +#define SYM_DUMP_FILE 24 +#define SYM_DOMAIN_NAME 25 +#define SYM_SWAP_SERVER 26 +#define SYM_ROOT_PATH 27 +#define SYM_EXTEN_FILE 28 +#define SYM_REPLY_ADDR 29 +#define SYM_NIS_DOMAIN 30 /* RFC 1533 */ +#define SYM_NIS_SERVER 31 /* RFC 1533 */ +#define SYM_NTP_SERVER 32 /* RFC 1533 */ +#define SYM_EXEC_FILE 33 /* YORK_EX_OPTION */ +#define SYM_MSG_SIZE 34 +#define SYM_MIN_WAIT 35 +/* XXX - Add new tags here */ + +#define OP_ADDITION 1 /* Operations on tags */ +#define OP_DELETION 2 +#define OP_BOOLEAN 3 + +#define MAXINADDRS 16 /* Max size of an IP address list */ +#define MAXBUFLEN 256 /* Max temp buffer space */ +#define MAXENTRYLEN 2048 /* Max size of an entire entry */ + + + +/* + * Structure used to map a configuration-file symbol (such as "ds") to a + * unique integer. + */ + +struct symbolmap { + char *symbol; + int symbolcode; +}; + + +struct htypename { + char *name; + byte htype; +}; + + +PRIVATE int nhosts; /* Number of hosts (/w hw or IP address) */ +PRIVATE int nentries; /* Total number of entries */ +PRIVATE int32 modtime = 0; /* Last modification time of bootptab */ +PRIVATE char *current_hostname; /* Name of the current entry. */ +PRIVATE char current_tagname[8]; + +/* + * List of symbolic names used in the bootptab file. The order and actual + * values of the symbol codes (SYM_. . .) are unimportant, but they must + * all be unique. + */ + +PRIVATE struct symbolmap symbol_list[] = { + {"bf", SYM_BOOTFILE}, + {"bs", SYM_BOOTSIZE}, + {"cs", SYM_COOKIE_SERVER}, + {"df", SYM_DUMP_FILE}, + {"dn", SYM_DOMAIN_NAME}, + {"ds", SYM_DOMAIN_SERVER}, + {"ef", SYM_EXTEN_FILE}, + {"ex", SYM_EXEC_FILE}, /* YORK_EX_OPTION */ + {"gw", SYM_GATEWAY}, + {"ha", SYM_HWADDR}, + {"hd", SYM_HOMEDIR}, + {"hn", SYM_NAME_SWITCH}, + {"ht", SYM_HTYPE}, + {"im", SYM_IMPRESS_SERVER}, + {"ip", SYM_IPADDR}, + {"lg", SYM_LOG_SERVER}, + {"lp", SYM_LPR_SERVER}, + {"ms", SYM_MSG_SIZE}, + {"mw", SYM_MIN_WAIT}, + {"ns", SYM_NAME_SERVER}, + {"nt", SYM_NTP_SERVER}, + {"ra", SYM_REPLY_ADDR}, + {"rl", SYM_RLP_SERVER}, + {"rp", SYM_ROOT_PATH}, + {"sa", SYM_BOOT_SERVER}, + {"sm", SYM_SUBNET_MASK}, + {"sw", SYM_SWAP_SERVER}, + {"tc", SYM_SIMILAR_ENTRY}, + {"td", SYM_TFTPDIR}, + {"to", SYM_TIME_OFFSET}, + {"ts", SYM_TIME_SERVER}, + {"vm", SYM_VENDOR_MAGIC}, + {"yd", SYM_NIS_DOMAIN}, + {"ys", SYM_NIS_SERVER}, + /* XXX - Add new tags here */ +}; + + +/* + * List of symbolic names for hardware types. Name translates into + * hardware type code listed with it. Names must begin with a letter + * and must be all lowercase. This is searched linearly, so put + * commonly-used entries near the beginning. + */ + +PRIVATE struct htypename htnamemap[] = { + {"ethernet", HTYPE_ETHERNET}, + {"ethernet3", HTYPE_EXP_ETHERNET}, + {"ether", HTYPE_ETHERNET}, + {"ether3", HTYPE_EXP_ETHERNET}, + {"ieee802", HTYPE_IEEE802}, + {"tr", HTYPE_IEEE802}, + {"token-ring", HTYPE_IEEE802}, + {"pronet", HTYPE_PRONET}, + {"chaos", HTYPE_CHAOS}, + {"arcnet", HTYPE_ARCNET}, + {"ax.25", HTYPE_AX25}, + {"direct", HTYPE_DIRECT}, + {"serial", HTYPE_DIRECT}, + {"slip", HTYPE_DIRECT}, + {"ppp", HTYPE_DIRECT} +}; + + + +/* + * Externals and forward declarations. + */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern boolean iplookcmp(); +boolean nmcmp P((hash_datum *, hash_datum *)); + +PRIVATE void + adjust P((char **)); +PRIVATE void + del_string P((struct shared_string *)); +PRIVATE void + del_bindata P((struct shared_bindata *)); +PRIVATE void + del_iplist P((struct in_addr_list *)); +PRIVATE void + eat_whitespace P((char **)); +PRIVATE int + eval_symbol P((char **, struct host *)); +PRIVATE void + fill_defaults P((struct host *, char **)); +PRIVATE void + free_host P((hash_datum *)); +PRIVATE struct in_addr_list * + get_addresses P((char **)); +PRIVATE struct shared_string * + get_shared_string P((char **)); +PRIVATE char * + get_string P((char **, char *, u_int *)); +PRIVATE u_int32 + get_u_long P((char **)); +PRIVATE boolean + goodname P((char *)); +PRIVATE boolean + hwinscmp P((hash_datum *, hash_datum *)); +PRIVATE int + interp_byte P((char **, byte *)); +PRIVATE void + makelower P((char *)); +PRIVATE boolean + nullcmp P((hash_datum *, hash_datum *)); +PRIVATE int + process_entry P((struct host *, char *)); +PRIVATE int + process_generic P((char **, struct shared_bindata **, u_int)); +PRIVATE byte * + prs_haddr P((char **, u_int)); +PRIVATE int + prs_inetaddr P((char **, u_int32 *)); +PRIVATE void + read_entry P((FILE *, char *, u_int *)); +PRIVATE char * + smalloc P((u_int)); + +#undef P + + +/* + * Vendor magic cookies for CMU and RFC1048 + */ +u_char vm_cmu[4] = VM_CMU; +u_char vm_rfc1048[4] = VM_RFC1048; + +/* + * Main hash tables + */ +hash_tbl *hwhashtable; +hash_tbl *iphashtable; +hash_tbl *nmhashtable; + +/* + * Allocate hash tables for hardware address, ip address, and hostname + * (shared by bootpd and bootpef) + */ +void +rdtab_init() +{ + hwhashtable = hash_Init(HASHTABLESIZE); + iphashtable = hash_Init(HASHTABLESIZE); + nmhashtable = hash_Init(HASHTABLESIZE); + if (!(hwhashtable && iphashtable && nmhashtable)) { + report(LOG_ERR, "Unable to allocate hash tables."); + exit(1); + } +} + + +/* + * Read bootptab database file. Avoid rereading the file if the + * write date hasn't changed since the last time we read it. + */ + +void +readtab(force) + int force; +{ + struct host *hp; + FILE *fp; + struct stat st; + unsigned hashcode, buflen; + static char buffer[MAXENTRYLEN]; + + /* + * Check the last modification time. + */ + if (stat(bootptab, &st) < 0) { + report(LOG_ERR, "stat on \"%s\": %s", + bootptab, get_errmsg()); + return; + } +#ifdef DEBUG + if (debug > 3) { + char timestr[28]; + strcpy(timestr, ctime(&(st.st_mtime))); + /* zap the newline */ + timestr[24] = '\0'; + report(LOG_INFO, "bootptab mtime: %s", + timestr); + } +#endif + if ((force == 0) && + (st.st_mtime == modtime) && + st.st_nlink) { + /* + * hasn't been modified or deleted yet. + */ + return; + } + if (debug) + report(LOG_INFO, "reading %s\"%s\"", + (modtime != 0L) ? "new " : "", + bootptab); + + /* + * Open bootptab file. + */ + if ((fp = fopen(bootptab, "r")) == NULL) { + report(LOG_ERR, "error opening \"%s\": %s", bootptab, get_errmsg()); + return; + } + /* + * Record file modification time. + */ + if (fstat(fileno(fp), &st) < 0) { + report(LOG_ERR, "fstat: %s", get_errmsg()); + fclose(fp); + return; + } + modtime = st.st_mtime; + + /* + * Entirely erase all hash tables. + */ + hash_Reset(hwhashtable, free_host); + hash_Reset(iphashtable, free_host); + hash_Reset(nmhashtable, free_host); + + nhosts = 0; + nentries = 0; + while (TRUE) { + buflen = sizeof(buffer); + read_entry(fp, buffer, &buflen); + if (buflen == 0) { /* More entries? */ + break; + } + hp = (struct host *) smalloc(sizeof(struct host)); + bzero((char *) hp, sizeof(*hp)); + /* the link count it zero */ + + /* + * Get individual info + */ + if (process_entry(hp, buffer) < 0) { + hp->linkcount = 1; + free_host((hash_datum *) hp); + continue; + } + /* + * If this is not a dummy entry, and the IP or HW + * address is not yet set, try to get them here. + * Dummy entries have . as first char of name. + */ + if (goodname(hp->hostname->string)) { + char *hn = hp->hostname->string; + u_int32 value; + if (hp->flags.iaddr == 0) { + if (lookup_ipa(hn, &value)) { + report(LOG_ERR, "can not get IP addr for %s", hn); + report(LOG_ERR, "(dummy names should start with '.')"); + } else { + hp->iaddr.s_addr = value; + hp->flags.iaddr = TRUE; + } + } + /* Set default subnet mask. */ + if (hp->flags.subnet_mask == 0) { + if (lookup_netmask(hp->iaddr.s_addr, &value)) { + report(LOG_ERR, "can not get netmask for %s", hn); + } else { + hp->subnet_mask.s_addr = value; + hp->flags.subnet_mask = TRUE; + } + } + } + if (hp->flags.iaddr) { + nhosts++; + } + /* Register by HW addr if known. */ + if (hp->flags.htype && hp->flags.haddr) { + /* We will either insert it or free it. */ + hp->linkcount++; + hashcode = hash_HashFunction(hp->haddr, haddrlength(hp->htype)); + if (hash_Insert(hwhashtable, hashcode, hwinscmp, hp, hp) < 0) { + report(LOG_NOTICE, "duplicate %s address: %s", + netname(hp->htype), + haddrtoa(hp->haddr, hp->htype)); + free_host((hash_datum *) hp); + continue; + } + } + /* Register by IP addr if known. */ + if (hp->flags.iaddr) { + hashcode = hash_HashFunction((u_char *) & (hp->iaddr.s_addr), 4); + if (hash_Insert(iphashtable, hashcode, nullcmp, hp, hp) < 0) { + report(LOG_ERR, + "hash_Insert() failed on IP address insertion"); + } else { + /* Just inserted the host struct in a new hash list. */ + hp->linkcount++; + } + } + /* Register by Name (always known) */ + hashcode = hash_HashFunction((u_char *) hp->hostname->string, + strlen(hp->hostname->string)); + if (hash_Insert(nmhashtable, hashcode, nullcmp, + hp->hostname->string, hp) < 0) { + report(LOG_ERR, + "hash_Insert() failed on insertion of hostname: \"%s\"", + hp->hostname->string); + } else { + /* Just inserted the host struct in a new hash list. */ + hp->linkcount++; + } + + nentries++; + } + + fclose(fp); + if (debug) + report(LOG_INFO, "read %d entries (%d hosts) from \"%s\"", + nentries, nhosts, bootptab); + return; +} + + + +/* + * Read an entire host entry from the file pointed to by "fp" and insert it + * into the memory pointed to by "buffer". Leading whitespace and comments + * starting with "#" are ignored (removed). Backslashes (\) always quote + * the next character except that newlines preceeded by a backslash cause + * line-continuation onto the next line. The entry is terminated by a + * newline character which is not preceeded by a backslash. Sequences + * surrounded by double quotes are taken literally (including newlines, but + * not backslashes). + * + * The "bufsiz" parameter points to an unsigned int which specifies the + * maximum permitted buffer size. Upon return, this value will be replaced + * with the actual length of the entry (not including the null terminator). + * + * This code is a little scary. . . . I don't like using gotos in C + * either, but I first wrote this as an FSM diagram and gotos seemed like + * the easiest way to implement it. Maybe later I'll clean it up. + */ + +PRIVATE void +read_entry(fp, buffer, bufsiz) + FILE *fp; + char *buffer; + unsigned *bufsiz; +{ + int c, length; + + length = 0; + + /* + * Eat whitespace, blank lines, and comment lines. + */ + top: + c = fgetc(fp); + if (c < 0) { + goto done; /* Exit if end-of-file */ + } + if (isspace(c)) { + goto top; /* Skip over whitespace */ + } + if (c == '#') { + while (TRUE) { /* Eat comments after # */ + c = fgetc(fp); + if (c < 0) { + goto done; /* Exit if end-of-file */ + } + if (c == '\n') { + goto top; /* Try to read the next line */ + } + } + } + ungetc(c, fp); /* Other character, push it back to reprocess it */ + + + /* + * Now we're actually reading a data entry. Get each character and + * assemble it into the data buffer, processing special characters like + * double quotes (") and backslashes (\). + */ + + mainloop: + c = fgetc(fp); + switch (c) { + case EOF: + case '\n': + goto done; /* Exit on EOF or newline */ + case '\\': + c = fgetc(fp); /* Backslash, read a new character */ + if (c < 0) { + goto done; /* Exit on EOF */ + } + *buffer++ = c; /* Store the literal character */ + length++; + if (length < *bufsiz - 1) { + goto mainloop; + } else { + goto done; + } + case '"': + *buffer++ = '"'; /* Store double-quote */ + length++; + if (length >= *bufsiz - 1) { + goto done; + } + while (TRUE) { /* Special quote processing loop */ + c = fgetc(fp); + switch (c) { + case EOF: + goto done; /* Exit on EOF . . . */ + case '"': + *buffer++ = '"';/* Store matching quote */ + length++; + if (length < *bufsiz - 1) { + goto mainloop; /* And continue main loop */ + } else { + goto done; + } + case '\\': + if ((c = fgetc(fp)) < 0) { /* Backslash */ + goto done; /* EOF. . . .*/ + } /* else fall through */ + default: + *buffer++ = c; /* Other character, store it */ + length++; + if (length >= *bufsiz - 1) { + goto done; + } + } + } + case ':': + *buffer++ = c; /* Store colons */ + length++; + if (length >= *bufsiz - 1) { + goto done; + } + do { /* But remove whitespace after them */ + c = fgetc(fp); + if ((c < 0) || (c == '\n')) { + goto done; + } + } while (isspace(c)); /* Skip whitespace */ + + if (c == '\\') { /* Backslash quotes next character */ + c = fgetc(fp); + if (c < 0) { + goto done; + } + if (c == '\n') { + goto top; /* Backslash-newline continuation */ + } + } + /* fall through if "other" character */ + default: + *buffer++ = c; /* Store other characters */ + length++; + if (length >= *bufsiz - 1) { + goto done; + } + } + goto mainloop; /* Keep going */ + + done: + *buffer = '\0'; /* Terminate string */ + *bufsiz = length; /* Tell the caller its length */ +} + + + +/* + * Parse out all the various tags and parameters in the host entry pointed + * to by "src". Stuff all the data into the appropriate fields of the + * host structure pointed to by "host". If there is any problem with the + * entry, an error message is reported via report(), no further processing + * is done, and -1 is returned. Successful calls return 0. + * + * (Some errors probably shouldn't be so completely fatal. . . .) + */ + +PRIVATE int +process_entry(host, src) + struct host *host; + char *src; +{ + int retval; + char *msg; + + if (!host || *src == '\0') { + return -1; + } + host->hostname = get_shared_string(&src); +#if 0 + /* Be more liberal for the benefit of dummy tag names. */ + if (!goodname(host->hostname->string)) { + report(LOG_ERR, "bad hostname: \"%s\"", host->hostname->string); + del_string(host->hostname); + return -1; + } +#endif + current_hostname = host->hostname->string; + adjust(&src); + while (TRUE) { + retval = eval_symbol(&src, host); + if (retval == SUCCESS) { + adjust(&src); + continue; + } + if (retval == E_END_OF_ENTRY) { + /* The default subnet mask is set in readtab() */ + return 0; + } + /* Some kind of error. */ + switch (retval) { + case E_SYNTAX_ERROR: + msg = "bad syntax"; + break; + case E_UNKNOWN_SYMBOL: + msg = "unknown symbol"; + break; + case E_BAD_IPADDR: + msg = "bad INET address"; + break; + case E_BAD_HWADDR: + msg = "bad hardware address"; + break; + case E_BAD_LONGWORD: + msg = "bad longword value"; + break; + case E_BAD_HWATYPE: + msg = "bad HW address type"; + break; + case E_BAD_PATHNAME: + msg = "bad pathname (need leading '/')"; + case E_BAD_VALUE: + msg = "bad value"; + default: + msg = "unkown error"; + break; + } /* switch */ + report(LOG_ERR, "in entry named \"%s\", symbol \"%s\": %s", + current_hostname, current_tagname, msg); + return -1; + } +} + + +/* + * Macros for use in the function below: + */ + +/* Parse one INET address stored directly in MEMBER. */ +#define PARSE_IA1(MEMBER) do \ +{ \ + if (optype == OP_BOOLEAN) \ + return E_SYNTAX_ERROR; \ + hp->flags.MEMBER = FALSE; \ + if (optype == OP_ADDITION) { \ + if (prs_inetaddr(symbol, &value) < 0) \ + return E_BAD_IPADDR; \ + hp->MEMBER.s_addr = value; \ + hp->flags.MEMBER = TRUE; \ + } \ +} while (0) + +/* Parse a list of INET addresses pointed to by MEMBER */ +#define PARSE_IAL(MEMBER) do \ +{ \ + if (optype == OP_BOOLEAN) \ + return E_SYNTAX_ERROR; \ + if (hp->flags.MEMBER) { \ + hp->flags.MEMBER = FALSE; \ + assert(hp->MEMBER); \ + del_iplist(hp->MEMBER); \ + hp->MEMBER = NULL; \ + } \ + if (optype == OP_ADDITION) { \ + hp->MEMBER = get_addresses(symbol); \ + if (hp->MEMBER == NULL) \ + return E_SYNTAX_ERROR; \ + hp->flags.MEMBER = TRUE; \ + } \ +} while (0) + +/* Parse a shared string pointed to by MEMBER */ +#define PARSE_STR(MEMBER) do \ +{ \ + if (optype == OP_BOOLEAN) \ + return E_SYNTAX_ERROR; \ + if (hp->flags.MEMBER) { \ + hp->flags.MEMBER = FALSE; \ + assert(hp->MEMBER); \ + del_string(hp->MEMBER); \ + hp->MEMBER = NULL; \ + } \ + if (optype == OP_ADDITION) { \ + hp->MEMBER = get_shared_string(symbol); \ + if (hp->MEMBER == NULL) \ + return E_SYNTAX_ERROR; \ + hp->flags.MEMBER = TRUE; \ + } \ +} while (0) + +/* Parse an integer value for MEMBER */ +#define PARSE_INT(MEMBER) do \ +{ \ + if (optype == OP_BOOLEAN) \ + return E_SYNTAX_ERROR; \ + hp->flags.MEMBER = FALSE; \ + if (optype == OP_ADDITION) { \ + value = get_u_long(symbol); \ + hp->MEMBER = value; \ + hp->flags.MEMBER = TRUE; \ + } \ +} while (0) + +/* + * Evaluate the two-character tag symbol pointed to by "symbol" and place + * the data in the structure pointed to by "hp". The pointer pointed to + * by "symbol" is updated to point past the source string (but may not + * point to the next tag entry). + * + * Obviously, this need a few more comments. . . . + */ +PRIVATE int +eval_symbol(symbol, hp) + char **symbol; + struct host *hp; +{ + char tmpstr[MAXSTRINGLEN]; + byte *tmphaddr; + struct shared_string *ss; + struct symbolmap *symbolptr; + u_int32 value; + int32 timeoff; + int i, numsymbols; + unsigned len; + int optype; /* Indicates boolean, addition, or deletion */ + + eat_whitespace(symbol); + + /* Make sure this is set before returning. */ + current_tagname[0] = (*symbol)[0]; + current_tagname[1] = (*symbol)[1]; + current_tagname[2] = 0; + + if ((*symbol)[0] == '\0') { + return E_END_OF_ENTRY; + } + if ((*symbol)[0] == ':') { + return SUCCESS; + } + if ((*symbol)[0] == 'T') { /* generic symbol */ + (*symbol)++; + value = get_u_long(symbol); + sprintf(current_tagname, "T%d", value); + eat_whitespace(symbol); + if ((*symbol)[0] != '=') { + return E_SYNTAX_ERROR; + } + (*symbol)++; + if (!(hp->generic)) { + hp->generic = (struct shared_bindata *) + smalloc(sizeof(struct shared_bindata)); + } + if (process_generic(symbol, &(hp->generic), (byte) (value & 0xFF))) + return E_SYNTAX_ERROR; + hp->flags.generic = TRUE; + return SUCCESS; + } + /* + * Determine the type of operation to be done on this symbol + */ + switch ((*symbol)[2]) { + case '=': + optype = OP_ADDITION; + break; + case '@': + optype = OP_DELETION; + break; + case ':': + case '\0': + optype = OP_BOOLEAN; + break; + default: + return E_SYNTAX_ERROR; + } + + symbolptr = symbol_list; + numsymbols = sizeof(symbol_list) / sizeof(struct symbolmap); + for (i = 0; i < numsymbols; i++) { + if (((symbolptr->symbol)[0] == (*symbol)[0]) && + ((symbolptr->symbol)[1] == (*symbol)[1])) { + break; + } + symbolptr++; + } + if (i >= numsymbols) { + return E_UNKNOWN_SYMBOL; + } + /* + * Skip past the = or @ character (to point to the data) if this + * isn't a boolean operation. For boolean operations, just skip + * over the two-character tag symbol (and nothing else. . . .). + */ + (*symbol) += (optype == OP_BOOLEAN) ? 2 : 3; + + eat_whitespace(symbol); + + /* The cases below are in order by symbolcode value. */ + switch (symbolptr->symbolcode) { + + case SYM_BOOTFILE: + PARSE_STR(bootfile); + break; + + case SYM_COOKIE_SERVER: + PARSE_IAL(cookie_server); + break; + + case SYM_DOMAIN_SERVER: + PARSE_IAL(domain_server); + break; + + case SYM_GATEWAY: + PARSE_IAL(gateway); + break; + + case SYM_HWADDR: + if (optype == OP_BOOLEAN) + return E_SYNTAX_ERROR; + hp->flags.haddr = FALSE; + if (optype == OP_ADDITION) { + /* Default the HW type to Ethernet */ + if (hp->flags.htype == 0) { + hp->flags.htype = TRUE; + hp->htype = HTYPE_ETHERNET; + } + tmphaddr = prs_haddr(symbol, hp->htype); + if (!tmphaddr) + return E_BAD_HWADDR; + bcopy(tmphaddr, hp->haddr, haddrlength(hp->htype)); + hp->flags.haddr = TRUE; + } + break; + + case SYM_HOMEDIR: + PARSE_STR(homedir); + break; + + case SYM_HTYPE: + if (optype == OP_BOOLEAN) + return E_SYNTAX_ERROR; + hp->flags.htype = FALSE; + if (optype == OP_ADDITION) { + value = 0L; /* Assume an illegal value */ + eat_whitespace(symbol); + if (isdigit(**symbol)) { + value = get_u_long(symbol); + } else { + len = sizeof(tmpstr); + (void) get_string(symbol, tmpstr, &len); + makelower(tmpstr); + numsymbols = sizeof(htnamemap) / + sizeof(struct htypename); + for (i = 0; i < numsymbols; i++) { + if (!strcmp(htnamemap[i].name, tmpstr)) { + break; + } + } + if (i < numsymbols) { + value = htnamemap[i].htype; + } + } + if (value >= hwinfocnt) { + return E_BAD_HWATYPE; + } + hp->htype = (byte) (value & 0xFF); + hp->flags.htype = TRUE; + } + break; + + case SYM_IMPRESS_SERVER: + PARSE_IAL(impress_server); + break; + + case SYM_IPADDR: + PARSE_IA1(iaddr); + break; + + case SYM_LOG_SERVER: + PARSE_IAL(log_server); + break; + + case SYM_LPR_SERVER: + PARSE_IAL(lpr_server); + break; + + case SYM_NAME_SERVER: + PARSE_IAL(name_server); + break; + + case SYM_RLP_SERVER: + PARSE_IAL(rlp_server); + break; + + case SYM_SUBNET_MASK: + PARSE_IA1(subnet_mask); + break; + + case SYM_TIME_OFFSET: + if (optype == OP_BOOLEAN) + return E_SYNTAX_ERROR; + hp->flags.time_offset = FALSE; + if (optype == OP_ADDITION) { + len = sizeof(tmpstr); + (void) get_string(symbol, tmpstr, &len); + if (!strncmp(tmpstr, "auto", 4)) { + hp->time_offset = secondswest; + } else { + if (sscanf(tmpstr, "%d", &timeoff) != 1) + return E_BAD_LONGWORD; + hp->time_offset = timeoff; + } + hp->flags.time_offset = TRUE; + } + break; + + case SYM_TIME_SERVER: + PARSE_IAL(time_server); + break; + + case SYM_VENDOR_MAGIC: + if (optype == OP_BOOLEAN) + return E_SYNTAX_ERROR; + hp->flags.vm_cookie = FALSE; + if (optype == OP_ADDITION) { + if (strncmp(*symbol, "auto", 4)) { + /* The string is not "auto" */ + if (!strncmp(*symbol, "rfc", 3)) { + bcopy(vm_rfc1048, hp->vm_cookie, 4); + } else if (!strncmp(*symbol, "cmu", 3)) { + bcopy(vm_cmu, hp->vm_cookie, 4); + } else { + if (!isdigit(**symbol)) + return E_BAD_IPADDR; + if (prs_inetaddr(symbol, &value) < 0) + return E_BAD_IPADDR; + bcopy(&value, hp->vm_cookie, 4); + } + hp->flags.vm_cookie = TRUE; + } + } + break; + + case SYM_SIMILAR_ENTRY: + switch (optype) { + case OP_ADDITION: + fill_defaults(hp, symbol); + break; + default: + return E_SYNTAX_ERROR; + } + break; + + case SYM_NAME_SWITCH: + switch (optype) { + case OP_ADDITION: + return E_SYNTAX_ERROR; + case OP_DELETION: + hp->flags.send_name = FALSE; + hp->flags.name_switch = FALSE; + break; + case OP_BOOLEAN: + hp->flags.send_name = TRUE; + hp->flags.name_switch = TRUE; + break; + } + break; + + case SYM_BOOTSIZE: + switch (optype) { + case OP_ADDITION: + if (!strncmp(*symbol, "auto", 4)) { + hp->flags.bootsize = TRUE; + hp->flags.bootsize_auto = TRUE; + } else { + hp->bootsize = (unsigned int) get_u_long(symbol); + hp->flags.bootsize = TRUE; + hp->flags.bootsize_auto = FALSE; + } + break; + case OP_DELETION: + hp->flags.bootsize = FALSE; + break; + case OP_BOOLEAN: + hp->flags.bootsize = TRUE; + hp->flags.bootsize_auto = TRUE; + break; + } + break; + + case SYM_BOOT_SERVER: + PARSE_IA1(bootserver); + break; + + case SYM_TFTPDIR: + PARSE_STR(tftpdir); + if ((hp->tftpdir != NULL) && + (hp->tftpdir->string[0] != '/')) + return E_BAD_PATHNAME; + break; + + case SYM_DUMP_FILE: + PARSE_STR(dump_file); + break; + + case SYM_DOMAIN_NAME: + PARSE_STR(domain_name); + break; + + case SYM_SWAP_SERVER: + PARSE_IA1(swap_server); + break; + + case SYM_ROOT_PATH: + PARSE_STR(root_path); + break; + + case SYM_EXTEN_FILE: + PARSE_STR(exten_file); + break; + + case SYM_REPLY_ADDR: + PARSE_IA1(reply_addr); + break; + + case SYM_NIS_DOMAIN: + PARSE_STR(nis_domain); + break; + + case SYM_NIS_SERVER: + PARSE_IAL(nis_server); + break; + + case SYM_NTP_SERVER: + PARSE_IAL(ntp_server); + break; + +#ifdef YORK_EX_OPTION + case SYM_EXEC_FILE: + PARSE_STR(exec_file); + break; +#endif + + case SYM_MSG_SIZE: + PARSE_INT(msg_size); + if (hp->msg_size < BP_MINPKTSZ || + hp->msg_size > MAX_MSG_SIZE) + return E_BAD_VALUE; + break; + + case SYM_MIN_WAIT: + PARSE_INT(min_wait); + if (hp->min_wait < 0) + return E_BAD_VALUE; + break; + + /* XXX - Add new tags here */ + + default: + return E_UNKNOWN_SYMBOL; + + } /* switch symbolcode */ + + return SUCCESS; +} +#undef PARSE_IA1 +#undef PARSE_IAL +#undef PARSE_STR + + + + +/* + * Read a string from the buffer indirectly pointed to through "src" and + * move it into the buffer pointed to by "dest". A pointer to the maximum + * allowable length of the string (including null-terminator) is passed as + * "length". The actual length of the string which was read is returned in + * the unsigned integer pointed to by "length". This value is the same as + * that which would be returned by applying the strlen() function on the + * destination string (i.e the terminating null is not counted as a + * character). Trailing whitespace is removed from the string. For + * convenience, the function returns the new value of "dest". + * + * The string is read until the maximum number of characters, an unquoted + * colon (:), or a null character is read. The return string in "dest" is + * null-terminated. + */ + +PRIVATE char * +get_string(src, dest, length) + char **src, *dest; + unsigned *length; +{ + int n, len, quoteflag; + + quoteflag = FALSE; + n = 0; + len = *length - 1; + while ((n < len) && (**src)) { + if (!quoteflag && (**src == ':')) { + break; + } + if (**src == '"') { + (*src)++; + quoteflag = !quoteflag; + continue; + } + if (**src == '\\') { + (*src)++; + if (!**src) { + break; + } + } + *dest++ = *(*src)++; + n++; + } + + /* + * Remove that troublesome trailing whitespace. . . + */ + while ((n > 0) && isspace(dest[-1])) { + dest--; + n--; + } + + *dest = '\0'; + *length = n; + return dest; +} + + + +/* + * Read the string indirectly pointed to by "src", update the caller's + * pointer, and return a pointer to a malloc'ed shared_string structure + * containing the string. + * + * The string is read using the same rules as get_string() above. + */ + +PRIVATE struct shared_string * +get_shared_string(src) + char **src; +{ + char retstring[MAXSTRINGLEN]; + struct shared_string *s; + unsigned length; + + length = sizeof(retstring); + (void) get_string(src, retstring, &length); + + s = (struct shared_string *) smalloc(sizeof(struct shared_string) + + length); + s->linkcount = 1; + strcpy(s->string, retstring); + + return s; +} + + + +/* + * Load RFC1048 generic information directly into a memory buffer. + * + * "src" indirectly points to the ASCII representation of the generic data. + * "dest" points to a string structure which is updated to point to a new + * string with the new data appended to the old string. The old string is + * freed. + * + * The given tag value is inserted with the new data. + * + * The data may be represented as either a stream of hexadecimal numbers + * representing bytes (any or all bytes may optionally start with '0x' and + * be separated with periods ".") or as a quoted string of ASCII + * characters (the quotes are required). + */ + +PRIVATE int +process_generic(src, dest, tagvalue) + char **src; + struct shared_bindata **dest; + u_int tagvalue; +{ + byte tmpbuf[MAXBUFLEN]; + byte *str; + struct shared_bindata *bdata; + u_int newlength, oldlength; + + str = tmpbuf; + *str++ = (tagvalue & 0xFF); /* Store tag value */ + str++; /* Skip over length field */ + if ((*src)[0] == '"') { /* ASCII data */ + newlength = sizeof(tmpbuf) - 2; /* Set maximum allowed length */ + (void) get_string(src, (char *) str, &newlength); + newlength++; /* null terminator */ + } else { /* Numeric data */ + newlength = 0; + while (newlength < sizeof(tmpbuf) - 2) { + if (interp_byte(src, str++) < 0) + break; + newlength++; + if (**src == '.') { + (*src)++; + } + } + } + if ((*src)[0] != ':') + return -1; + + tmpbuf[1] = (newlength & 0xFF); + oldlength = ((*dest)->length); + bdata = (struct shared_bindata *) smalloc(sizeof(struct shared_bindata) + + oldlength + newlength + 1); + if (oldlength > 0) { + bcopy((*dest)->data, bdata->data, oldlength); + } + bcopy(tmpbuf, bdata->data + oldlength, newlength + 2); + bdata->length = oldlength + newlength + 2; + bdata->linkcount = 1; + if (*dest) { + del_bindata(*dest); + } + *dest = bdata; + return 0; +} + + + +/* + * Verify that the given string makes sense as a hostname (according to + * Appendix 1, page 29 of RFC882). + * + * Return TRUE for good names, FALSE otherwise. + */ + +PRIVATE boolean +goodname(hostname) + register char *hostname; +{ + do { + if (!isalpha(*hostname++)) { /* First character must be a letter */ + return FALSE; + } + while (isalnum(*hostname) || + (*hostname == '-') || + (*hostname == '_') ) + { + hostname++; /* Alphanumeric or a hyphen */ + } + if (!isalnum(hostname[-1])) { /* Last must be alphanumeric */ + return FALSE; + } + if (*hostname == '\0') {/* Done? */ + return TRUE; + } + } while (*hostname++ == '.'); /* Dot, loop for next label */ + + return FALSE; /* If it's not a dot, lose */ +} + + + +/* + * Null compare function -- always returns FALSE so an element is always + * inserted into a hash table (i.e. there is never a collision with an + * existing element). + */ + +PRIVATE boolean +nullcmp(d1, d2) + hash_datum *d1, *d2; +{ + return FALSE; +} + + +/* + * Function for comparing a string with the hostname field of a host + * structure. + */ + +boolean +nmcmp(d1, d2) + hash_datum *d1, *d2; +{ + char *name = (char *) d1; /* XXX - OK? */ + struct host *hp = (struct host *) d2; + + return !strcmp(name, hp->hostname->string); +} + + +/* + * Compare function to determine whether two hardware addresses are + * equivalent. Returns TRUE if "host1" and "host2" are equivalent, FALSE + * otherwise. + * + * If the hardware addresses of "host1" and "host2" are identical, but + * they are on different IP subnets, this function returns FALSE. + * + * This function is used when inserting elements into the hardware address + * hash table. + */ + +PRIVATE boolean +hwinscmp(d1, d2) + hash_datum *d1, *d2; +{ + struct host *host1 = (struct host *) d1; + struct host *host2 = (struct host *) d2; + + if (host1->htype != host2->htype) { + return FALSE; + } + if (bcmp(host1->haddr, host2->haddr, haddrlength(host1->htype))) { + return FALSE; + } + /* XXX - Is the subnet_mask field set yet? */ + if ((host1->subnet_mask.s_addr) == (host2->subnet_mask.s_addr)) { + if (((host1->iaddr.s_addr) & (host1->subnet_mask.s_addr)) != + ((host2->iaddr.s_addr) & (host2->subnet_mask.s_addr))) + { + return FALSE; + } + } + return TRUE; +} + + +/* + * Macros for use in the function below: + */ + +#define DUP_COPY(MEMBER) do \ +{ \ + if (!hp->flags.MEMBER) { \ + if ((hp->flags.MEMBER = hp2->flags.MEMBER) != 0) { \ + hp->MEMBER = hp2->MEMBER; \ + } \ + } \ +} while (0) + +#define DUP_LINK(MEMBER) do \ +{ \ + if (!hp->flags.MEMBER) { \ + if ((hp->flags.MEMBER = hp2->flags.MEMBER) != 0) { \ + assert(hp2->MEMBER); \ + hp->MEMBER = hp2->MEMBER; \ + (hp->MEMBER->linkcount)++; \ + } \ + } \ +} while (0) + +/* + * Process the "similar entry" symbol. + * + * The host specified as the value of the "tc" symbol is used as a template + * for the current host entry. Symbol values not explicitly set in the + * current host entry are inferred from the template entry. + */ +PRIVATE void +fill_defaults(hp, src) + struct host *hp; + char **src; +{ + unsigned int tlen, hashcode; + struct host *hp2; + char tstring[MAXSTRINGLEN]; + + tlen = sizeof(tstring); + (void) get_string(src, tstring, &tlen); + hashcode = hash_HashFunction((u_char *) tstring, tlen); + hp2 = (struct host *) hash_Lookup(nmhashtable, hashcode, nmcmp, tstring); + + if (hp2 == NULL) { + report(LOG_ERR, "can't find tc=\"%s\"", tstring); + return; + } + DUP_LINK(bootfile); + DUP_LINK(cookie_server); + DUP_LINK(domain_server); + DUP_LINK(gateway); + /* haddr not copied */ + DUP_LINK(homedir); + DUP_COPY(htype); + + DUP_LINK(impress_server); + /* iaddr not copied */ + DUP_LINK(log_server); + DUP_LINK(lpr_server); + DUP_LINK(name_server); + DUP_LINK(rlp_server); + + DUP_COPY(subnet_mask); + DUP_COPY(time_offset); + DUP_LINK(time_server); + + if (!hp->flags.vm_cookie) { + if ((hp->flags.vm_cookie = hp2->flags.vm_cookie)) { + bcopy(hp2->vm_cookie, hp->vm_cookie, 4); + } + } + if (!hp->flags.name_switch) { + if ((hp->flags.name_switch = hp2->flags.name_switch)) { + hp->flags.send_name = hp2->flags.send_name; + } + } + if (!hp->flags.bootsize) { + if ((hp->flags.bootsize = hp2->flags.bootsize)) { + hp->flags.bootsize_auto = hp2->flags.bootsize_auto; + hp->bootsize = hp2->bootsize; + } + } + DUP_COPY(bootserver); + + DUP_LINK(tftpdir); + DUP_LINK(dump_file); + DUP_LINK(domain_name); + + DUP_COPY(swap_server); + DUP_LINK(root_path); + DUP_LINK(exten_file); + + DUP_COPY(reply_addr); + + DUP_LINK(nis_domain); + DUP_LINK(nis_server); + DUP_LINK(ntp_server); + +#ifdef YORK_EX_OPTION + DUP_LINK(exec_file); +#endif + + DUP_COPY(msg_size); + DUP_COPY(min_wait); + + /* XXX - Add new tags here */ + + DUP_LINK(generic); + +} +#undef DUP_COPY +#undef DUP_LINK + + + +/* + * This function adjusts the caller's pointer to point just past the + * first-encountered colon. If it runs into a null character, it leaves + * the pointer pointing to it. + */ + +PRIVATE void +adjust(s) + char **s; +{ + register char *t; + + t = *s; + while (*t && (*t != ':')) { + t++; + } + if (*t) { + t++; + } + *s = t; +} + + + + +/* + * This function adjusts the caller's pointer to point to the first + * non-whitespace character. If it runs into a null character, it leaves + * the pointer pointing to it. + */ + +PRIVATE void +eat_whitespace(s) + char **s; +{ + register char *t; + + t = *s; + while (*t && isspace(*t)) { + t++; + } + *s = t; +} + + + +/* + * This function converts the given string to all lowercase. + */ + +PRIVATE void +makelower(s) + char *s; +{ + while (*s) { + if (isupper(*s)) { + *s = tolower(*s); + } + s++; + } +} + + + +/* + * + * N O T E : + * + * In many of the functions which follow, a parameter such as "src" or + * "symbol" is passed as a pointer to a pointer to something. This is + * done for the purpose of letting the called function update the + * caller's copy of the parameter (i.e. to effect call-by-reference + * parameter passing). The value of the actual parameter is only used + * to locate the real parameter of interest and then update this indirect + * parameter. + * + * I'm sure somebody out there won't like this. . . . + * (Yea, because it usually makes code slower... -gwr) + * + */ + + + +/* + * "src" points to a character pointer which points to an ASCII string of + * whitespace-separated IP addresses. A pointer to an in_addr_list + * structure containing the list of addresses is returned. NULL is + * returned if no addresses were found at all. The pointer pointed to by + * "src" is updated to point to the first non-address (illegal) character. + */ + +PRIVATE struct in_addr_list * +get_addresses(src) + char **src; +{ + struct in_addr tmpaddrlist[MAXINADDRS]; + struct in_addr *address1, *address2; + struct in_addr_list *result; + unsigned addrcount, totalsize; + + address1 = tmpaddrlist; + for (addrcount = 0; addrcount < MAXINADDRS; addrcount++) { + while (isspace(**src) || (**src == ',')) { + (*src)++; + } + if (!**src) { /* Quit if nothing more */ + break; + } + if (prs_inetaddr(src, &(address1->s_addr)) < 0) { + break; + } + address1++; /* Point to next address slot */ + } + if (addrcount < 1) { + result = NULL; + } else { + totalsize = sizeof(struct in_addr_list) + + (addrcount - 1) * sizeof(struct in_addr); + result = (struct in_addr_list *) smalloc(totalsize); + result->linkcount = 1; + result->addrcount = addrcount; + address1 = tmpaddrlist; + address2 = result->addr; + for (; addrcount > 0; addrcount--) { + address2->s_addr = address1->s_addr; + address1++; + address2++; + } + } + return result; +} + + + +/* + * prs_inetaddr(src, result) + * + * "src" is a value-result parameter; the pointer it points to is updated + * to point to the next data position. "result" points to an unsigned long + * in which an address is returned. + * + * This function parses the IP address string in ASCII "dot notation" pointed + * to by (*src) and places the result (in network byte order) in the unsigned + * long pointed to by "result". For malformed addresses, -1 is returned, + * (*src) points to the first illegal character, and the unsigned long pointed + * to by "result" is unchanged. Successful calls return 0. + */ + +PRIVATE int +prs_inetaddr(src, result) + char **src; + u_int32 *result; +{ + char tmpstr[MAXSTRINGLEN]; + register u_int32 value; + u_int32 parts[4], *pp; + int n; + char *s, *t; + +#if 1 /* XXX - experimental */ + /* Leading alpha char causes IP addr lookup. */ + if (isalpha(**src)) { + /* Lookup IP address. */ + s = *src; + t = tmpstr; + while ((isalnum(*s) || (*s == '.') || + (*s == '-') || (*s == '_') ) && + (t < &tmpstr[MAXSTRINGLEN - 1]) ) + *t++ = *s++; + *t = '\0'; + *src = s; + + n = lookup_ipa(tmpstr, result); + if (n < 0) + report(LOG_ERR, "can not get IP addr for %s", tmpstr); + return n; + } +#endif + + /* + * Parse an address in Internet format: + * a.b.c.d + * a.b.c (with c treated as 16-bits) + * a.b (with b treated as 24 bits) + */ + pp = parts; + loop: + /* If it's not a digit, return error. */ + if (!isdigit(**src)) + return -1; + *pp++ = get_u_long(src); + if (**src == '.') { + if (pp < (parts + 4)) { + (*src)++; + goto loop; + } + return (-1); + } +#if 0 + /* This is handled by the caller. */ + if (**src && !(isspace(**src) || (**src == ':'))) { + return (-1); + } +#endif + + /* + * Construct the address according to + * the number of parts specified. + */ + n = pp - parts; + switch (n) { + case 1: /* a -- 32 bits */ + value = parts[0]; + break; + case 2: /* a.b -- 8.24 bits */ + value = (parts[0] << 24) | (parts[1] & 0xFFFFFF); + break; + case 3: /* a.b.c -- 8.8.16 bits */ + value = (parts[0] << 24) | ((parts[1] & 0xFF) << 16) | + (parts[2] & 0xFFFF); + break; + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + value = (parts[0] << 24) | ((parts[1] & 0xFF) << 16) | + ((parts[2] & 0xFF) << 8) | (parts[3] & 0xFF); + break; + default: + return (-1); + } + *result = htonl(value); + return (0); +} + + + +/* + * "src" points to a pointer which in turn points to a hexadecimal ASCII + * string. This string is interpreted as a hardware address and returned + * as a pointer to the actual hardware address, represented as an array of + * bytes. + * + * The ASCII string must have the proper number of digits for the specified + * hardware type (e.g. twelve digits for a 48-bit Ethernet address). + * Two-digit sequences (bytes) may be separated with periods (.) and/or + * prefixed with '0x' for readability, but this is not required. + * + * For bad addresses, the pointer which "src" points to is updated to point + * to the start of the first two-digit sequence which was bad, and the + * function returns a NULL pointer. + */ + +PRIVATE byte * +prs_haddr(src, htype) + char **src; + u_int htype; +{ + static byte haddr[MAXHADDRLEN]; + byte *hap; + char tmpstr[MAXSTRINGLEN]; + u_int tmplen; + unsigned hal; + char *p; + + hal = haddrlength(htype); /* Get length of this address type */ + if (hal <= 0) { + report(LOG_ERR, "Invalid addr type for HW addr parse"); + return NULL; + } + tmplen = sizeof(tmpstr); + get_string(src, tmpstr, &tmplen); + p = tmpstr; + +#if 1 /* XXX - experimental */ + /* If it's a valid host name, try to lookup the HW address. */ + if (goodname(p)) { + /* Lookup Hardware Address for hostname. */ + if ((hap = lookup_hwa(p, htype)) != NULL) + return hap; /* success */ + report(LOG_ERR, "Add 0x prefix if hex value starts with A-F"); + /* OK, assume it must be numeric. */ + } +#endif + + hap = haddr; + while (hap < haddr + hal) { + if (*p == '.') + p++; + if (interp_byte(&p, hap++) < 0) { + return NULL; + } + } + return haddr; +} + + + +/* + * "src" is a pointer to a character pointer which in turn points to a + * hexadecimal ASCII representation of a byte. This byte is read, the + * character pointer is updated, and the result is deposited into the + * byte pointed to by "retbyte". + * + * The usual '0x' notation is allowed but not required. The number must be + * a two digit hexadecimal number. If the number is invalid, "src" and + * "retbyte" are left untouched and -1 is returned as the function value. + * Successful calls return 0. + */ + +PRIVATE int +interp_byte(src, retbyte) + char **src; + byte *retbyte; +{ + int v; + + if ((*src)[0] == '0' && + ((*src)[1] == 'x' || + (*src)[1] == 'X')) { + (*src) += 2; /* allow 0x for hex, but don't require it */ + } + if (!isxdigit((*src)[0]) || !isxdigit((*src)[1])) { + return -1; + } + if (sscanf(*src, "%2x", &v) != 1) { + return -1; + } + (*src) += 2; + *retbyte = (byte) (v & 0xFF); + return 0; +} + + + +/* + * The parameter "src" points to a character pointer which points to an + * ASCII string representation of an unsigned number. The number is + * returned as an unsigned long and the character pointer is updated to + * point to the first illegal character. + */ + +PRIVATE u_int32 +get_u_long(src) + char **src; +{ + register u_int32 value, base; + char c; + + /* + * Collect number up to first illegal character. Values are specified + * as for C: 0x=hex, 0=octal, other=decimal. + */ + value = 0; + base = 10; + if (**src == '0') { + base = 8; + (*src)++; + } + if (**src == 'x' || **src == 'X') { + base = 16; + (*src)++; + } + while ((c = **src)) { + if (isdigit(c)) { + value = (value * base) + (c - '0'); + (*src)++; + continue; + } + if (base == 16 && isxdigit(c)) { + value = (value << 4) + ((c & ~32) + 10 - 'A'); + (*src)++; + continue; + } + break; + } + return value; +} + + + +/* + * Routines for deletion of data associated with the main data structure. + */ + + +/* + * Frees the entire host data structure given. Does nothing if the passed + * pointer is NULL. + */ + +PRIVATE void +free_host(hmp) + hash_datum *hmp; +{ + struct host *hostptr = (struct host *) hmp; + if (hostptr == NULL) + return; + assert(hostptr->linkcount > 0); + if (--(hostptr->linkcount)) + return; /* Still has references */ + del_iplist(hostptr->cookie_server); + del_iplist(hostptr->domain_server); + del_iplist(hostptr->gateway); + del_iplist(hostptr->impress_server); + del_iplist(hostptr->log_server); + del_iplist(hostptr->lpr_server); + del_iplist(hostptr->name_server); + del_iplist(hostptr->rlp_server); + del_iplist(hostptr->time_server); + del_iplist(hostptr->nis_server); + del_iplist(hostptr->ntp_server); + + /* + * XXX - Add new tags here + * (if the value is an IP list) + */ + + del_string(hostptr->hostname); + del_string(hostptr->homedir); + del_string(hostptr->bootfile); + del_string(hostptr->tftpdir); + del_string(hostptr->root_path); + del_string(hostptr->domain_name); + del_string(hostptr->dump_file); + del_string(hostptr->exten_file); + del_string(hostptr->nis_domain); + +#ifdef YORK_EX_OPTION + del_string(hostptr->exec_file); +#endif + + /* + * XXX - Add new tags here + * (if it is a shared string) + */ + + del_bindata(hostptr->generic); + free((char *) hostptr); +} + + + +/* + * Decrements the linkcount on the given IP address data structure. If the + * linkcount goes to zero, the memory associated with the data is freed. + */ + +PRIVATE void +del_iplist(iplist) + struct in_addr_list *iplist; +{ + if (iplist) { + if (!(--(iplist->linkcount))) { + free((char *) iplist); + } + } +} + + + +/* + * Decrements the linkcount on a string data structure. If the count + * goes to zero, the memory associated with the string is freed. Does + * nothing if the passed pointer is NULL. + */ + +PRIVATE void +del_string(stringptr) + struct shared_string *stringptr; +{ + if (stringptr) { + if (!(--(stringptr->linkcount))) { + free((char *) stringptr); + } + } +} + + + +/* + * Decrements the linkcount on a shared_bindata data structure. If the + * count goes to zero, the memory associated with the data is freed. Does + * nothing if the passed pointer is NULL. + */ + +PRIVATE void +del_bindata(dataptr) + struct shared_bindata *dataptr; +{ + if (dataptr) { + if (!(--(dataptr->linkcount))) { + free((char *) dataptr); + } + } +} + + + + +/* smalloc() -- safe malloc() + * + * Always returns a valid pointer (if it returns at all). The allocated + * memory is initialized to all zeros. If malloc() returns an error, a + * message is printed using the report() function and the program aborts + * with a status of 1. + */ + +PRIVATE char * +smalloc(nbytes) + unsigned nbytes; +{ + char *retvalue; + + retvalue = malloc(nbytes); + if (!retvalue) { + report(LOG_ERR, "malloc() failure -- exiting"); + exit(1); + } + bzero(retvalue, nbytes); + return retvalue; +} + + +/* + * Compare function to determine whether two hardware addresses are + * equivalent. Returns TRUE if "host1" and "host2" are equivalent, FALSE + * otherwise. + * + * This function is used when retrieving elements from the hardware address + * hash table. + */ + +boolean +hwlookcmp(d1, d2) + hash_datum *d1, *d2; +{ + struct host *host1 = (struct host *) d1; + struct host *host2 = (struct host *) d2; + + if (host1->htype != host2->htype) { + return FALSE; + } + if (bcmp(host1->haddr, host2->haddr, haddrlength(host1->htype))) { + return FALSE; + } + return TRUE; +} + + +/* + * Compare function for doing IP address hash table lookup. + */ + +boolean +iplookcmp(d1, d2) + hash_datum *d1, *d2; +{ + struct host *host1 = (struct host *) d1; + struct host *host2 = (struct host *) d2; + + return (host1->iaddr.s_addr == host2->iaddr.s_addr); +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/readfile.h b/libexec/bootpd/readfile.h new file mode 100644 index 0000000..3913455 --- /dev/null +++ b/libexec/bootpd/readfile.h @@ -0,0 +1,19 @@ +/* readfile.h */ + +#include "bptypes.h" +#include "hash.h" + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern boolean hwlookcmp P((hash_datum *, hash_datum *)); +extern boolean iplookcmp P((hash_datum *, hash_datum *)); +extern boolean nmcmp P((hash_datum *, hash_datum *)); +extern void readtab P((int)); +extern void rdtab_init P((void)); + +#undef P + diff --git a/libexec/bootpd/report.c b/libexec/bootpd/report.c new file mode 100644 index 0000000..4f7f036 --- /dev/null +++ b/libexec/bootpd/report.c @@ -0,0 +1,154 @@ +/* + * report() - calls syslog + */ + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include <stdio.h> +#include <syslog.h> + +#include "report.h" + +#ifndef LOG_NDELAY +#define LOG_NDELAY 0 +#endif +#ifndef LOG_DAEMON +#define LOG_DAEMON 0 +#endif +#ifndef LOG_BOOTP +#define LOG_BOOTP LOG_DAEMON +#endif + +extern int debug; +extern char *progname; + +/* + * This is initialized so you get stderr until you call + * report_init() + */ +static int stderr_only = 1; + +void +report_init(nolog) + int nolog; +{ + stderr_only = nolog; +#ifdef SYSLOG + if (!stderr_only) { + openlog(progname, LOG_PID | LOG_NDELAY, LOG_BOOTP); + } +#endif +} + +/* + * This routine reports errors and such via stderr and syslog() if + * appopriate. It just helps avoid a lot of "#ifdef SYSLOG" constructs + * from being scattered throughout the code. + * + * The syntax is identical to syslog(3), but %m is not considered special + * for output to stderr (i.e. you'll see "%m" in the output. . .). Also, + * control strings should normally end with \n since newlines aren't + * automatically generated for stderr output (whereas syslog strips out all + * newlines and adds its own at the end). + */ + +static char *levelnames[] = { +#ifdef LOG_SALERT + "level(0): ", + "alert(1): ", + "alert(2): ", + "emerg(3): ", + "error(4): ", + "crit(5): ", + "warn(6): ", + "note(7): ", + "info(8): ", + "debug(9): ", + "level(?): " +#else + "emerg(0): ", + "alert(1): ", + "crit(2): ", + "error(3): ", + "warn(4): ", + "note(5): ", + "info(6): ", + "debug(7): ", + "level(?): " +#endif +}; +static int numlevels = sizeof(levelnames) / sizeof(levelnames[0]); + + +/* + * Print a log message using syslog(3) and/or stderr. + * The message passed in should not include a newline. + */ +#ifdef __STDC__ +void +report(int priority, char *fmt,...) +#else +/*VARARGS2*/ +void +report(priority, fmt, va_alist) + int priority; + char *fmt; + va_dcl +#endif +{ + va_list ap; + static char buf[128]; + + if ((priority < 0) || (priority >= numlevels)) { + priority = numlevels - 1; + } +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vsprintf(buf, fmt, ap); + va_end(ap); + + /* + * Print the message + */ + if (stderr_only || (debug > 2)) { + fprintf(stderr, "%s: %s %s\n", + progname, levelnames[priority], buf); + } +#ifdef SYSLOG + if (!stderr_only) + syslog((priority | LOG_BOOTP), "%s", buf); +#endif +} + + + +/* + * Return pointer to static string which gives full filesystem error message. + */ +char * +get_errmsg() +{ + extern int errno; + extern char *strerror(); + + return strerror(errno); +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/report.h b/libexec/bootpd/report.h new file mode 100644 index 0000000..0bf63d6 --- /dev/null +++ b/libexec/bootpd/report.h @@ -0,0 +1,13 @@ +/* report.h */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern void report_init P((int nolog)); +extern void report P((int, char *, ...)); +extern char *get_errmsg P((void)); + +#undef P diff --git a/libexec/bootpd/rtmsg.c b/libexec/bootpd/rtmsg.c new file mode 100644 index 0000000..5942b22 --- /dev/null +++ b/libexec/bootpd/rtmsg.c @@ -0,0 +1,252 @@ +/* + * Copyright (c) 1984, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1994 + * Geoffrey M. Rehmet, All rights reserved. + * + * This code is derived from software which forms part of the 4.4-Lite + * Berkeley software distribution, which was in derived from software + * contributed to Berkeley by Sun Microsystems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * from arp.c 8.2 (Berkeley) 1/2/94 + * $Id: rtmsg.c,v 1.3 1995/01/30 11:11:43 dfr Exp $ + */ + +#include <sys/param.h> +/* + * Verify that we are at least 4.4 BSD + */ +#if defined(BSD) +#if BSD >= 199306 + +#include <sys/socket.h> +#include <sys/filio.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/route.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <arpa/inet.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "report.h" + + +static int rtmsg __P((int)); + +static int s = -1; /* routing socket */ + + +/* + * Open the routing socket + */ +static void getsocket () { + if (s < 0) { + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) { + report(LOG_ERR, "socket %s", strerror(errno)); + exit(1); + } + } else { + /* + * Drain the socket of any unwanted routing messages. + */ + int n; + char buf[512]; + + ioctl(s, FIONREAD, &n); + while (n > 0) { + read(s, buf, sizeof buf); + ioctl(s, FIONREAD, &n); + } + } +} + +static struct sockaddr_in so_mask = {8, 0, 0, { 0xffffffff}}; +static struct sockaddr_inarp blank_sin = {sizeof(blank_sin), AF_INET }, sin_m; +static struct sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m; +static int expire_time, flags, export_only, doing_proxy; +static struct { + struct rt_msghdr m_rtm; + char m_space[512]; +} m_rtmsg; + +/* + * Set an individual arp entry + */ +int bsd_arp_set(ia, eaddr, len) + struct in_addr *ia; + char *eaddr; + int len; +{ + register struct sockaddr_inarp *sin = &sin_m; + register struct sockaddr_dl *sdl; + register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); + u_char *ea; + struct timeval time; + int op = RTM_ADD; + + getsocket(); + sdl_m = blank_sdl; + sin_m = blank_sin; + sin->sin_addr = *ia; + + ea = (u_char *)LLADDR(&sdl_m); + bcopy(eaddr, ea, len); + sdl_m.sdl_alen = len; + doing_proxy = flags = export_only = expire_time = 0; + + /* make arp entry temporary */ + gettimeofday(&time, 0); + expire_time = time.tv_sec + 20 * 60; + +tryagain: + if (rtmsg(RTM_GET) < 0) { + report(LOG_WARNING, "rtmget: %s", strerror(errno)); + return (1); + } + sin = (struct sockaddr_inarp *)(rtm + 1); + sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin); + if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) { + if (sdl->sdl_family == AF_LINK && + (rtm->rtm_flags & RTF_LLINFO) && + !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) { + case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023: + case IFT_ISO88024: case IFT_ISO88025: + op = RTM_CHANGE; + goto overwrite; + } + if (doing_proxy == 0) { + report(LOG_WARNING, "set: can only proxy for %s\n", + inet_ntoa(sin->sin_addr)); + return (1); + } + if (sin_m.sin_other & SIN_PROXY) { + report(LOG_WARNING, + "set: proxy entry exists for non 802 device\n"); + return(1); + } + sin_m.sin_other = SIN_PROXY; + export_only = 1; + goto tryagain; + } +overwrite: + if (sdl->sdl_family != AF_LINK) { + report(LOG_WARNING, + "cannot intuit interface index and type for %s\n", + inet_ntoa(sin->sin_addr)); + return (1); + } + sdl_m.sdl_type = sdl->sdl_type; + sdl_m.sdl_index = sdl->sdl_index; + return (rtmsg(op)); +} + + +static int rtmsg(cmd) + int cmd; +{ + static int seq; + int rlen; + register struct rt_msghdr *rtm = &m_rtmsg.m_rtm; + register char *cp = m_rtmsg.m_space; + register int l; + + errno = 0; + bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); + rtm->rtm_flags = flags; + rtm->rtm_version = RTM_VERSION; + + switch (cmd) { + default: + report(LOG_ERR, "set_arp: internal wrong cmd - exiting"); + exit(1); + case RTM_ADD: + case RTM_CHANGE: + rtm->rtm_addrs |= RTA_GATEWAY; + rtm->rtm_rmx.rmx_expire = expire_time; + rtm->rtm_inits = RTV_EXPIRE; + rtm->rtm_flags |= (RTF_HOST | RTF_STATIC); + sin_m.sin_other = 0; + if (doing_proxy) { + if (export_only) + sin_m.sin_other = SIN_PROXY; + else { + rtm->rtm_addrs |= RTA_NETMASK; + rtm->rtm_flags &= ~RTF_HOST; + } + } + /* FALLTHROUGH */ + case RTM_GET: + rtm->rtm_addrs |= RTA_DST; + } +#define NEXTADDR(w, s) \ + if (rtm->rtm_addrs & (w)) { \ + bcopy((char *)&s, cp, sizeof(s)); cp += sizeof(s);} + + NEXTADDR(RTA_DST, sin_m); + NEXTADDR(RTA_GATEWAY, sdl_m); + NEXTADDR(RTA_NETMASK, so_mask); + + rtm->rtm_msglen = cp - (char *)&m_rtmsg; + + l = rtm->rtm_msglen; + rtm->rtm_seq = ++seq; + rtm->rtm_type = cmd; + if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) { + if ((errno != ESRCH) && !(errno == EEXIST && cmd == RTM_ADD)){ + report(LOG_WARNING, "writing to routing socket: %s", + strerror(errno)); + return (-1); + } + } + do { + l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); + } while (l > 0 && (rtm->rtm_type != cmd || rtm->rtm_seq != seq || rtm->rtm_pid != getpid())); + if (l < 0) + report(LOG_WARNING, "arp: read from routing socket: %s\n", + strerror(errno)); + return (0); +} + +#endif /* BSD */ +#endif /* BSD >= 199306 */ diff --git a/libexec/bootpd/syslog.conf b/libexec/bootpd/syslog.conf new file mode 100644 index 0000000..2c135af --- /dev/null +++ b/libexec/bootpd/syslog.conf @@ -0,0 +1,63 @@ +# +# syslog configuration file for SunOS 4.X +# (modified to do local2 separately) +# +# This file is processed by m4 so be careful to quote (`') names +# that match m4 reserved words. Also, within ifdef's, arguments +# containing commas must be quoted. +# +# Note: Have to exclude user from most lines so that user.alert +# and user.emerg are not included, because old sendmails +# will generate them for debugging information. If you +# have no 4.2BSD based systems doing network logging, you +# can remove all the special cases for "user" logging. + +#*.err;kern.debug;auth.notice;user.none /dev/console +kern.debug;user,mail.crit;auth.notice /dev/console +daemon,syslog,lpr,news,uucp,cron.err /dev/console + +#*.err;kern.debug;daemon,auth.notice;mail.crit;user.none /var/adm/messages +kern.debug;user,mail.crit;auth.notice /var/adm/messages +daemon.notice;syslog,news,uucp,cron.err /var/adm/messages + +lpr.debug /var/adm/lpd-errs + +*.alert;kern.err;daemon.err;user.none operator +*.alert;user.none root + +*.emerg;user.none * + +# for loghost machines, to have authentication messages (su, login, etc.) +# logged to a file, un-comment out the following line and adjust the file name +# as appropriate. +# +# if a non-loghost machine chooses to have such messages +# sent to the loghost machine, un-comment out the following line. +# +#auth.notice ifdef(`LOGHOST', /var/log/authlog, @loghost) + +mail.debug ifdef(`LOGHOST', /var/log/syslog, @loghost) + +# following line for compatibility with old sendmails. they will send +# messages with no facility code, which will be turned into "user" messages +# by the local syslog daemon. only the "loghost" machine needs the following +# line, to cause these old sendmail log messages to be logged in the +# mail syslog file. +# +ifdef(`LOGHOST', +user.alert /var/log/syslog +) +# +# non-loghost machines will use the following lines to cause "user" +# log messages to be logged locally. +# +ifdef(`LOGHOST', , +user.err /dev/console +user.err /var/adm/messages +user.alert `root, operator' +user.emerg * +) + +# Local2: (bootpd, pppd) +local2.debug /dev/console +#local2.debug /var/log/local2 diff --git a/libexec/bootpd/tools/Makefile b/libexec/bootpd/tools/Makefile new file mode 100644 index 0000000..b1f38e7 --- /dev/null +++ b/libexec/bootpd/tools/Makefile @@ -0,0 +1,6 @@ +# Makefile +# $Id$ + +SUBDIR= bootpef bootptest + +.include <bsd.subdir.mk> diff --git a/libexec/bootpd/tools/Makefile.inc b/libexec/bootpd/tools/Makefile.inc new file mode 100644 index 0000000..0a2ab66 --- /dev/null +++ b/libexec/bootpd/tools/Makefile.inc @@ -0,0 +1,4 @@ +# Makefile.inc +# $Id$ + +BINDIR=/usr/sbin diff --git a/libexec/bootpd/tools/bootpef/Makefile b/libexec/bootpd/tools/bootpef/Makefile new file mode 100644 index 0000000..7d17606 --- /dev/null +++ b/libexec/bootpd/tools/bootpef/Makefile @@ -0,0 +1,13 @@ +# Makefile +# $Id$ + +PROG= bootpef +MAN8= bootpef.8 +SRCS= bootpef.c dovend.c readfile.c hash.c dumptab.c lookup.c \ + hwaddr.c report.c tzone.c rtmsg.c + +SRCDIR= ${.CURDIR}/../.. +CFLAGS+=-I${SRCDIR} +.PATH: ${SRCDIR} + +.include <bsd.prog.mk> diff --git a/libexec/bootpd/tools/bootpef/bootpef.8 b/libexec/bootpd/tools/bootpef/bootpef.8 new file mode 100644 index 0000000..0f0b1fc --- /dev/null +++ b/libexec/bootpd/tools/bootpef/bootpef.8 @@ -0,0 +1,52 @@ +.\" bootpef.8 +.TH BOOTPEF 8 "4 Dec 1993" "MAINTENANCE COMMANDS" +.SH NAME +bootpef \- BOOTP Extension File compiler +.SH SYNOPSIS +.LP +.B bootpef +.RI [ "-c chdir" ] +.RI [ "-d debug-level" ] +.RI [ "-f config-file" ] +.RI [ client-name " [...]]" +.SH DESCRIPTION +.B bootpef +builds the +.I Extension Path +files described by RFC 1497 (tag 18). +If any +.I client-name +arguments are specified, then +.I bootpef +compiles the extension files for only those clients. +.SH OPTIONS +.TP +.BI \-c \ chdir\-path +Sets the current directory used by +.I bootpef +while creating extension files. This is useful when the +extension file names are specified as relative pathnames, and +.I bootpef +needs to use the same current directory as the TFTP server +(typically /tftpboot). +.TP +.BI \-d \ debug\-level +Sets the +.I debug\-level +variable that controls the amount of debugging messages generated. +For example, -d4 or -d 4 will set the debugging level to 4. +.TP +.BI \-f \ config\-file +Set the name of the config file that specifies the option +data to be sent to each client. +.SH "SEE ALSO" +bootpd(8), tftpd(8) +.SH REFERENCES +.TP +RFC951 +BOOTSTRAP PROTOCOL (BOOTP) +.TP +RFC1497 +BOOTP Vendor Information Extensions + + diff --git a/libexec/bootpd/tools/bootpef/bootpef.c b/libexec/bootpd/tools/bootpef/bootpef.c new file mode 100644 index 0000000..eb18935 --- /dev/null +++ b/libexec/bootpd/tools/bootpef/bootpef.c @@ -0,0 +1,347 @@ +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + +#ifndef lint +static char rcsid[] = "$Id: bootpef.c,v 1.1.1.1 1994/09/10 14:44:54 csgr Exp $"; +#endif + + +/* + * bootpef - BOOTP Extension File generator + * Makes an "Extension File" for each host entry that + * defines an and Extension File. (See RFC1497, tag 18.) + * + * HISTORY + * See ./Changes + * + * BUGS + * See ./ToDo + */ + + + +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include <sys/types.h> +#include <sys/time.h> + +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#ifndef NO_UNISTD +#include <unistd.h> +#endif +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <syslog.h> + +#ifndef USE_BFUNCS +#include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +#define bcopy(a,b,c) memcpy(b,a,c) +#define bzero(p,l) memset(p,0,l) +#define bcmp(a,b,c) memcmp(a,b,c) +#endif + +#include "bootp.h" +#include "hash.h" +#include "hwaddr.h" +#include "bootpd.h" +#include "dovend.h" +#include "readfile.h" +#include "report.h" +#include "tzone.h" +#include "patchlevel.h" + +#define BUFFERSIZE 0x4000 + +#ifndef CONFIG_FILE +#define CONFIG_FILE "/etc/bootptab" +#endif + + + +/* + * Externals, forward declarations, and global variables + */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +static void dovend_rfc1048 P((struct bootp *, struct host *, int32)); +static void mktagfile P((struct host *)); +static void usage P((void)); + +#undef P + + +/* + * General + */ + +char *progname; +char *chdir_path; +int debug = 0; /* Debugging flag (level) */ +byte *buffer; + +/* + * Globals below are associated with the bootp database file (bootptab). + */ + +char *bootptab = CONFIG_FILE; + + +/* + * Print "usage" message and exit + */ +static void +usage() +{ + fprintf(stderr, + "usage: $s [ -c chdir ] [-d level] [-f configfile] [host...]\n"); + fprintf(stderr, "\t -c n\tset current directory\n"); + fprintf(stderr, "\t -d n\tset debug level\n"); + fprintf(stderr, "\t -f n\tconfig file name\n"); + exit(1); +} + + +/* + * Initialization such as command-line processing is done and then the + * main server loop is started. + */ +void +main(argc, argv) + int argc; + char **argv; +{ + struct host *hp; + char *stmp; + int n; + + progname = strrchr(argv[0], '/'); + if (progname) progname++; + else progname = argv[0]; + + /* Get work space for making tag 18 files. */ + buffer = (byte *) malloc(BUFFERSIZE); + if (!buffer) { + report(LOG_ERR, "malloc failed"); + exit(1); + } + /* + * Set defaults that might be changed by option switches. + */ + stmp = NULL; + + /* + * Read switches. + */ + for (argc--, argv++; argc > 0; argc--, argv++) { + if (argv[0][0] != '-') + break; + switch (argv[0][1]) { + + case 'c': /* chdir_path */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (stmp[0] != '/')) { + fprintf(stderr, + "bootpd: invalid chdir specification\n"); + break; + } + chdir_path = stmp; + break; + + case 'd': /* debug */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else if (argv[1] && argv[1][0] == '-') { + /* + * Backwards-compatible behavior: + * no parameter, so just increment the debug flag. + */ + debug++; + break; + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { + fprintf(stderr, + "bootpd: invalid debug level\n"); + break; + } + debug = n; + break; + + case 'f': /* config file */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + bootptab = stmp; + break; + + default: + fprintf(stderr, "bootpd: unknown switch: -%c\n", + argv[0][1]); + usage(); + break; + } + } + + /* Get the timezone. */ + tzone_init(); + + /* Allocate hash tables. */ + rdtab_init(); + + /* + * Read the bootptab file. + */ + readtab(1); /* force read */ + + /* Set the cwd (i.e. to /tftpboot) */ + if (chdir_path) { + if (chdir(chdir_path) < 0) + report(LOG_ERR, "%s: chdir failed", chdir_path); + } + /* If there are host names on the command line, do only those. */ + if (argc > 0) { + unsigned int tlen, hashcode; + + while (argc) { + tlen = strlen(argv[0]); + hashcode = hash_HashFunction((u_char *)argv[0], tlen); + hp = (struct host *) hash_Lookup(nmhashtable, + hashcode, + nmcmp, argv[0]); + if (!hp) { + printf("%s: no matching entry\n", argv[0]); + exit(1); + } + if (!hp->flags.exten_file) { + printf("%s: no extension file\n", argv[0]); + exit(1); + } + mktagfile(hp); + argv++; + argc--; + } + exit(0); + } + /* No host names specified. Do them all. */ + hp = (struct host *) hash_FirstEntry(nmhashtable); + while (hp != NULL) { + mktagfile(hp); + hp = (struct host *) hash_NextEntry(nmhashtable); + } +} + + + +/* + * Make a "TAG 18" file for this host. + * (Insert the RFC1497 options.) + */ + +static void +mktagfile(hp) + struct host *hp; +{ + FILE *fp; + int bytesleft, len; + byte *vp; + char *tmpstr; + + if (!hp->flags.exten_file) + return; + + vp = buffer; + bytesleft = BUFFERSIZE; + bcopy(vm_rfc1048, vp, 4); /* Copy in the magic cookie */ + vp += 4; + bytesleft -= 4; + + /* + * The "extension file" options are appended by the following + * function (which is shared with bootpd.c). + */ + len = dovend_rfc1497(hp, vp, bytesleft); + vp += len; + bytesleft -= len; + + if (bytesleft < 1) { + report(LOG_ERR, "%s: too much option data", + hp->exten_file->string); + return; + } + *vp++ = TAG_END; + bytesleft--; + + /* Write the buffer to the extension file. */ + printf("Updating \"%s\"\n", hp->exten_file->string); + if ((fp = fopen(hp->exten_file->string, "w")) == NULL) { + report(LOG_ERR, "error opening \"%s\": %s", + hp->exten_file->string, get_errmsg()); + return; + } + len = vp - buffer; + if (len != fwrite(buffer, 1, len, fp)) { + report(LOG_ERR, "write failed on \"%s\" : %s", + hp->exten_file->string, get_errmsg()); + } + fclose(fp); + +} /* dovend_rfc1048 */ + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/tools/bootptest/Makefile b/libexec/bootpd/tools/bootptest/Makefile new file mode 100644 index 0000000..d137e73 --- /dev/null +++ b/libexec/bootpd/tools/bootptest/Makefile @@ -0,0 +1,12 @@ +# Makefile +# $Id: Makefile,v 1.2 1995/05/30 05:45:51 rgrimes Exp $ + +PROG= bootptest +MAN8= bootptest.8 +SRCS= bootptest.c getether.c getif.c print-bootp.c report.c + +SRCDIR= ${.CURDIR}/../.. +CFLAGS+=-I${SRCDIR} +.PATH: ${SRCDIR} + +.include <bsd.prog.mk> diff --git a/libexec/bootpd/tools/bootptest/bootptest.8 b/libexec/bootpd/tools/bootptest/bootptest.8 new file mode 100644 index 0000000..d076c8b --- /dev/null +++ b/libexec/bootpd/tools/bootptest/bootptest.8 @@ -0,0 +1,74 @@ +.\" bootptest.8 +.TH BOOTPTEST 8 "10 June 1993" "MAINTENANCE COMMANDS" +.SH NAME +bootptest \- send BOOTP queries and print responses +.SH SYNOPSIS +.LP +.B bootptest +[ +.B \-f +.I bootfile +] +[ +.B \-h +] +[ +.B \-m +.I magic_number +] +.I server\-name +.RI [ template-file ] +.SH DESCRIPTION +.B bootptest +sends BOOTP requests to the host specified as +.I server\-name +at one\-second intervals until either a response is received, +or until ten requests have gone unanswered. +After a response is received, +.B bootptest +will wait one more second listening for additional responses. +.SH OPTIONS +.TP +.B \-f +.I bootfile +Fill in the boot file field of the request with +.IR bootfile . +.TP +.B \-h +Use the hardware (Ethernet) address to identify the client. +By default, the IP address is copied into the request +indicating that this client already knows its IP address. +.TP +.B \-m +.I magic_number +Initialize the first word of the vendor options field with +.IR magic_number . +.LP +A +.I template-file +may be specified, in which case +.B bootptest +uses the (binary) contents of this file to initialize the +.I options +area of the request packet. +.SH CREDITS +.LP +The bootptest program is a combination of original and derived works. +The main program module (bootptest.c) is original work by +Gordon W. Ross <gwr@mc.com>. +The packet printing module (print-bootp.c) is a slightly modified +version of a file from the BSD tcpdump program. +.LP +This program includes software developed by the University of +California, Lawrence Berkeley Laboratory and its contributors. +(See the copyright notice in print-bootp.c) +.SH "SEE ALSO" +.LP +bootpd(8) +.SH REFERENCES +.TP +RFC951 +BOOTSTRAP PROTOCOL (BOOTP) +.TP +RFC1048 +BOOTP Vendor Information Extensions diff --git a/libexec/bootpd/tools/bootptest/bootptest.c b/libexec/bootpd/tools/bootptest/bootptest.c new file mode 100644 index 0000000..bc235ac --- /dev/null +++ b/libexec/bootpd/tools/bootptest/bootptest.c @@ -0,0 +1,500 @@ +/* + * bootptest.c - Test out a bootp server. + * + * This simple program was put together from pieces taken from + * various places, including the CMU BOOTP client and server. + * The packet printing routine is from the Berkeley "tcpdump" + * program with some enhancements I added. The print-bootp.c + * file was shared with my copy of "tcpdump" and therefore uses + * some unusual utility routines that would normally be provided + * by various parts of the tcpdump program. Gordon W. Ross + * + * Boilerplate: + * + * This program includes software developed by the University of + * California, Lawrence Berkeley Laboratory and its contributors. + * (See the copyright notice in print-bootp.c) + * + * The remainder of this program is public domain. You may do + * whatever you like with it except claim that you wrote it. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * HISTORY: + * + * 12/02/93 Released version 1.4 (with bootp-2.3.2) + * 11/05/93 Released version 1.3 + * 10/14/93 Released version 1.2 + * 10/11/93 Released version 1.1 + * 09/28/93 Released version 1.0 + * 09/93 Original developed by Gordon W. Ross <gwr@mc.com> + */ + +char *usage = "bootptest [-h] server-name [vendor-data-template-file]"; + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#include <stdlib.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <netdb.h> +#include <assert.h> + +#include "bootp.h" +#include "bootptest.h" +#include "getif.h" +#include "patchlevel.h" + +#define LOG_ERR 1 +#define BUFLEN 1024 +#define WAITSECS 1 +#define MAXWAIT 10 + +int vflag = 1; +int tflag = 0; +int thiszone; +char *progname; +unsigned char *packetp; +unsigned char *snapend; +int snaplen; + + +/* + * IP port numbers for client and server obtained from /etc/services + */ + +u_short bootps_port, bootpc_port; + + +/* + * Internet socket and interface config structures + */ + +struct sockaddr_in sin_server; /* where to send requests */ +struct sockaddr_in sin_client; /* for bind and listen */ +struct sockaddr_in sin_from; /* Packet source */ +u_char eaddr[16]; /* Ethernet address */ + +/* + * General + */ + +int debug = 1; /* Debugging flag (level) */ +char hostname[64]; +char *sndbuf; /* Send packet buffer */ +char *rcvbuf; /* Receive packet buffer */ + +/* + * Vendor magic cookies for CMU and RFC1048 + */ + +unsigned char vm_cmu[4] = VM_CMU; +unsigned char vm_rfc1048[4] = VM_RFC1048; +short secs; /* How long client has waited */ + +char *get_errmsg(); +extern void bootp_print(); + +/* + * Initialization such as command-line processing is done, then + * the receiver loop is started. Die when interrupted. + */ + +main(argc, argv) + int argc; + char **argv; +{ + struct bootp *bp; + struct servent *sep; + struct hostent *hep; + + char *servername = NULL; + char *vendor_file = NULL; + char *bp_file = NULL; + int32 server_addr; /* inet addr, network order */ + int s; /* Socket file descriptor */ + int n, tolen, fromlen, recvcnt; + int use_hwa = 0; + int32 vend_magic; + int32 xid; + + progname = strrchr(argv[0], '/'); + if (progname) + progname++; + else + progname = argv[0]; + argc--; + argv++; + + if (debug) + printf("%s: version %s.%d\n", progname, VERSION, PATCHLEVEL); + + /* + * Verify that "struct bootp" has the correct official size. + * (Catch evil compilers that do struct padding.) + */ + assert(sizeof(struct bootp) == BP_MINPKTSZ); + + sndbuf = malloc(BUFLEN); + rcvbuf = malloc(BUFLEN); + if (!sndbuf || !rcvbuf) { + printf("malloc failed\n"); + exit(1); + } + + /* default magic number */ + bcopy(vm_rfc1048, (char*)&vend_magic, 4); + + /* Handle option switches. */ + while (argc > 0) { + if (argv[0][0] != '-') + break; + switch (argv[0][1]) { + + case 'f': /* File name to reqest. */ + if (argc < 2) + goto error; + argc--; argv++; + bp_file = *argv; + break; + + case 'h': /* Use hardware address. */ + use_hwa = 1; + break; + + case 'm': /* Magic number value. */ + if (argc < 2) + goto error; + argc--; argv++; + vend_magic = inet_addr(*argv); + break; + + error: + default: + puts(usage); + exit(1); + + } + argc--; + argv++; + } + + /* Get server name (or address) for query. */ + if (argc > 0) { + servername = *argv; + argc--; + argv++; + } + /* Get optional vendor-data-template-file. */ + if (argc > 0) { + vendor_file = *argv; + argc--; + argv++; + } + if (!servername) { + printf("missing server name.\n"); + puts(usage); + exit(1); + } + /* + * Create a socket. + */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket"); + exit(1); + } + /* + * Get server's listening port number + */ + sep = getservbyname("bootps", "udp"); + if (sep) { + bootps_port = ntohs((u_short) sep->s_port); + } else { + fprintf(stderr, "udp/bootps: unknown service -- using port %d\n", + IPPORT_BOOTPS); + bootps_port = (u_short) IPPORT_BOOTPS; + } + + /* + * Set up server socket address (for send) + */ + if (servername) { + if (isdigit(servername[0])) + server_addr = inet_addr(servername); + else { + hep = gethostbyname(servername); + if (!hep) { + fprintf(stderr, "%s: unknown host\n", servername); + exit(1); + } + bcopy(hep->h_addr, &server_addr, sizeof(server_addr)); + } + } else { + /* Get broadcast address */ + /* XXX - not yet */ + server_addr = INADDR_ANY; + } + sin_server.sin_family = AF_INET; + sin_server.sin_port = htons(bootps_port); + sin_server.sin_addr.s_addr = server_addr; + + /* + * Get client's listening port number + */ + sep = getservbyname("bootpc", "udp"); + if (sep) { + bootpc_port = ntohs(sep->s_port); + } else { + fprintf(stderr, "udp/bootpc: unknown service -- using port %d\n", + IPPORT_BOOTPC); + bootpc_port = (u_short) IPPORT_BOOTPC; + } + + /* + * Set up client socket address (for listen) + */ + sin_client.sin_family = AF_INET; + sin_client.sin_port = htons(bootpc_port); + sin_client.sin_addr.s_addr = INADDR_ANY; + + /* + * Bind client socket to BOOTPC port. + */ + if (bind(s, (struct sockaddr *) &sin_client, sizeof(sin_client)) < 0) { + perror("bind BOOTPC port"); + if (errno == EACCES) + fprintf(stderr, "You need to run this as root\n"); + exit(1); + } + /* + * Build a request. + */ + bp = (struct bootp *) sndbuf; + bzero(bp, sizeof(*bp)); + bp->bp_op = BOOTREQUEST; + xid = (int32) getpid(); + bp->bp_xid = (u_int32) htonl(xid); + if (bp_file) + strncpy(bp->bp_file, bp_file, BP_FILE_LEN); + + /* + * Fill in the hardware address (or client IP address) + */ + if (use_hwa) { + struct ifreq *ifr; + + ifr = getif(s, &sin_server.sin_addr); + if (!ifr) { + printf("No interface for %s\n", servername); + exit(1); + } + if (getether(ifr->ifr_name, eaddr)) { + printf("Can not get ether addr for %s\n", ifr->ifr_name); + exit(1); + } + /* Copy Ethernet address into request packet. */ + bp->bp_htype = 1; + bp->bp_hlen = 6; + bcopy(eaddr, bp->bp_chaddr, bp->bp_hlen); + } else { + /* Fill in the client IP address. */ + gethostname(hostname, sizeof(hostname)); + hep = gethostbyname(hostname); + if (!hep) { + printf("Can not get my IP address\n"); + exit(1); + } + bcopy(hep->h_addr, &bp->bp_ciaddr, hep->h_length); + } + + /* + * Copy in the default vendor data. + */ + bcopy((char*)&vend_magic, bp->bp_vend, 4); + if (vend_magic) + bp->bp_vend[4] = TAG_END; + + /* + * Read in the "options" part of the request. + * This also determines the size of the packet. + */ + snaplen = sizeof(*bp); + if (vendor_file) { + int fd = open(vendor_file, 0); + if (fd < 0) { + perror(vendor_file); + exit(1); + } + /* Compute actual space for options. */ + n = BUFLEN - sizeof(*bp) + BP_VEND_LEN; + n = read(fd, bp->bp_vend, n); + close(fd); + if (n < 0) { + perror(vendor_file); + exit(1); + } + printf("read %d bytes of vendor template\n", n); + if (n > BP_VEND_LEN) { + printf("warning: extended options in use (len > %d)\n", + BP_VEND_LEN); + snaplen += (n - BP_VEND_LEN); + } + } + /* + * Set globals needed by print_bootp + * (called by send_request) + */ + packetp = (unsigned char *) eaddr; + snapend = (unsigned char *) sndbuf + snaplen; + + /* Send a request once per second while waiting for replies. */ + recvcnt = 0; + bp->bp_secs = secs = 0; + send_request(s); + while (1) { + struct timeval tv; + int readfds; + + tv.tv_sec = WAITSECS; + tv.tv_usec = 0L; + readfds = (1 << s); + n = select(s + 1, (fd_set *) & readfds, NULL, NULL, &tv); + if (n < 0) { + perror("select"); + break; + } + if (n == 0) { + /* + * We have not received a response in the last second. + * If we have ever received any responses, exit now. + * Otherwise, bump the "wait time" field and re-send. + */ + if (recvcnt > 0) + exit(0); + secs += WAITSECS; + if (secs > MAXWAIT) + break; + bp->bp_secs = htons(secs); + send_request(s); + continue; + } + fromlen = sizeof(sin_from); + n = recvfrom(s, rcvbuf, BUFLEN, 0, + (struct sockaddr *) &sin_from, &fromlen); + if (n <= 0) { + continue; + } + if (n < sizeof(struct bootp)) { + printf("received short packet\n"); + continue; + } + recvcnt++; + + /* Print the received packet. */ + printf("Recvd from %s", inet_ntoa(sin_from.sin_addr)); + /* set globals needed by bootp_print() */ + snaplen = n; + snapend = (unsigned char *) rcvbuf + snaplen; + bootp_print(rcvbuf, n, sin_from.sin_port, 0); + putchar('\n'); + /* + * This no longer exits immediately after receiving + * one response because it is useful to know if the + * client might get multiple responses. This code + * will now listen for one second after a response. + */ + } + fprintf(stderr, "no response from %s\n", servername); + exit(1); +} + +send_request(s) + int s; +{ + /* Print the request packet. */ + printf("Sending to %s", inet_ntoa(sin_server.sin_addr)); + bootp_print(sndbuf, snaplen, sin_from.sin_port, 0); + putchar('\n'); + + /* Send the request packet. */ + if (sendto(s, sndbuf, snaplen, 0, + (struct sockaddr *) &sin_server, + sizeof(sin_server)) < 0) + { + perror("sendto server"); + exit(1); + } +} + +/* + * Print out a filename (or other ascii string). + * Return true if truncated. + */ +int +printfn(s, ep) + register u_char *s, *ep; +{ + register u_char c; + + putchar('"'); + while (c = *s++) { + if (s > ep) { + putchar('"'); + return (1); + } + if (!isascii(c)) { + c = toascii(c); + putchar('M'); + putchar('-'); + } + if (!isprint(c)) { + c ^= 0x40; /* DEL to ?, others to alpha */ + putchar('^'); + } + putchar(c); + } + putchar('"'); + return (0); +} + +/* + * Convert an IP addr to a string. + * (like inet_ntoa, but ina is a pointer) + */ +char * +ipaddr_string(ina) + struct in_addr *ina; +{ + static char b[24]; + u_char *p; + + p = (u_char *) ina; + sprintf(b, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + return (b); +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/tools/bootptest/bootptest.h b/libexec/bootpd/tools/bootptest/bootptest.h new file mode 100644 index 0000000..27f78ba --- /dev/null +++ b/libexec/bootpd/tools/bootptest/bootptest.h @@ -0,0 +1,30 @@ +/* bootptest.h */ +/* + * Hacks for sharing print-bootp.c between tcpdump and bootptest. + */ +#define ESRC(p) (p) +#define EDST(p) (p) + +#ifndef USE_BFUNCS +/* Use mem/str functions */ +/* There are no overlapped copies, so memcpy is OK. */ +#define bcopy(a,b,c) memcpy(b,a,c) +#define bzero(p,l) memset(p,0,l) +#define bcmp(a,b,c) memcmp(a,b,c) +#endif + +extern int vflag; /* verbose flag */ + +/* global pointers to beginning and end of current packet (during printing) */ +extern unsigned char *packetp; +extern unsigned char *snapend; + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +extern char *ipaddr_string P((struct in_addr *)); + +#undef P diff --git a/libexec/bootpd/tools/bootptest/print-bootp.c b/libexec/bootpd/tools/bootptest/print-bootp.c new file mode 100644 index 0000000..f416998 --- /dev/null +++ b/libexec/bootpd/tools/bootptest/print-bootp.c @@ -0,0 +1,493 @@ +/* + * Copyright (c) 1988-1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the University of California, + * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Format and print bootp packets. + * + * This file was copied from tcpdump-2.1.1 and modified. + * There is an e-mail list for tcpdump: <tcpdump@ee.lbl.gov> + */ +#ifndef lint +static char rcsid[] = "$Id: print-bootp.c,v 1.1.1.1 1994/09/10 14:44:55 csgr Exp $"; +/* 93/10/10 <gwr@mc.com> New data-driven option print routine. */ +#endif + +#include <stdio.h> + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <string.h> +#include <ctype.h> + +#include "bootp.h" +#include "bootptest.h" + +/* These decode the vendor data. */ +static void rfc1048_print(); +static void cmu_print(); +static void other_print(); +static void dump_hex(); + +/* + * Print bootp requests + */ +void +bootp_print(bp, length, sport, dport) + struct bootp *bp; + int length; + u_short sport, dport; +{ + static char tstr[] = " [|bootp]"; + static unsigned char vm_cmu[4] = VM_CMU; + static unsigned char vm_rfc1048[4] = VM_RFC1048; + u_char *ep; + int vdlen; + +#define TCHECK(var, l) if ((u_char *)&(var) > ep - l) goto trunc + + /* Note funny sized packets */ + if (length != sizeof(struct bootp)) + (void) printf(" [len=%d]", length); + + /* 'ep' points to the end of avaible data. */ + ep = (u_char *) snapend; + + switch (bp->bp_op) { + + case BOOTREQUEST: + /* Usually, a request goes from a client to a server */ + if (sport != IPPORT_BOOTPC || dport != IPPORT_BOOTPS) + printf(" (request)"); + break; + + case BOOTREPLY: + /* Usually, a reply goes from a server to a client */ + if (sport != IPPORT_BOOTPS || dport != IPPORT_BOOTPC) + printf(" (reply)"); + break; + + default: + printf(" bootp-#%d", bp->bp_op); + } + + /* The usual hardware address type is 1 (10Mb Ethernet) */ + if (bp->bp_htype != 1) + printf(" htype:%d", bp->bp_htype); + + /* The usual length for 10Mb Ethernet address is 6 bytes */ + if (bp->bp_hlen != 6) + printf(" hlen:%d", bp->bp_hlen); + + /* Client's Hardware address */ + if (bp->bp_hlen) { + register struct ether_header *eh; + register char *e; + + TCHECK(bp->bp_chaddr[0], 6); + eh = (struct ether_header *) packetp; + if (bp->bp_op == BOOTREQUEST) + e = (char *) ESRC(eh); + else if (bp->bp_op == BOOTREPLY) + e = (char *) EDST(eh); + else + e = 0; + if (e == 0 || bcmp((char *) bp->bp_chaddr, e, 6)) + dump_hex(bp->bp_chaddr, bp->bp_hlen); + } + /* Only print interesting fields */ + if (bp->bp_hops) + printf(" hops:%d", bp->bp_hops); + + if (bp->bp_xid) + printf(" xid:%d", ntohl(bp->bp_xid)); + + if (bp->bp_secs) + printf(" secs:%d", ntohs(bp->bp_secs)); + + /* Client's ip address */ + TCHECK(bp->bp_ciaddr, sizeof(bp->bp_ciaddr)); + if (bp->bp_ciaddr.s_addr) + printf(" C:%s", ipaddr_string(&bp->bp_ciaddr)); + + /* 'your' ip address (bootp client) */ + TCHECK(bp->bp_yiaddr, sizeof(bp->bp_yiaddr)); + if (bp->bp_yiaddr.s_addr) + printf(" Y:%s", ipaddr_string(&bp->bp_yiaddr)); + + /* Server's ip address */ + TCHECK(bp->bp_siaddr, sizeof(bp->bp_siaddr)); + if (bp->bp_siaddr.s_addr) + printf(" S:%s", ipaddr_string(&bp->bp_siaddr)); + + /* Gateway's ip address */ + TCHECK(bp->bp_giaddr, sizeof(bp->bp_giaddr)); + if (bp->bp_giaddr.s_addr) + printf(" G:%s", ipaddr_string(&bp->bp_giaddr)); + + TCHECK(bp->bp_sname[0], sizeof(bp->bp_sname)); + if (*bp->bp_sname) { + printf(" sname:"); + if (printfn(bp->bp_sname, ep)) { + fputs(tstr + 1, stdout); + return; + } + } + TCHECK(bp->bp_file[0], sizeof(bp->bp_file)); + if (*bp->bp_file) { + printf(" file:"); + if (printfn(bp->bp_file, ep)) { + fputs(tstr + 1, stdout); + return; + } + } + /* Don't try to decode the vendor buffer unless we're verbose */ + if (vflag <= 0) + return; + + vdlen = sizeof(bp->bp_vend); + /* Vendor data can extend to the end of the packet. */ + if (vdlen < (ep - bp->bp_vend)) + vdlen = (ep - bp->bp_vend); + + TCHECK(bp->bp_vend[0], vdlen); + printf(" vend"); + if (!bcmp(bp->bp_vend, vm_rfc1048, sizeof(u_int32))) + rfc1048_print(bp->bp_vend, vdlen); + else if (!bcmp(bp->bp_vend, vm_cmu, sizeof(u_int32))) + cmu_print(bp->bp_vend, vdlen); + else + other_print(bp->bp_vend, vdlen); + + return; + trunc: + fputs(tstr, stdout); +#undef TCHECK +} + +/* + * Option description data follows. + * These are decribed in: RFC-1048, RFC-1395, RFC-1497, RFC-1533 + * + * The first char of each option string encodes the data format: + * ?: unknown + * a: ASCII + * b: byte (8-bit) + * i: inet address + * l: int32 + * s: short (16-bit) + */ +char * +rfc1048_opts[] = { + /* Originally from RFC-1048: */ + "?PAD", /* 0: Padding - special, no data. */ + "iSM", /* 1: subnet mask (RFC950)*/ + "lTZ", /* 2: time offset, seconds from UTC */ + "iGW", /* 3: gateways (or routers) */ + "iTS", /* 4: time servers (RFC868) */ + "iINS", /* 5: IEN name servers (IEN116) */ + "iDNS", /* 6: domain name servers (RFC1035)(1034?) */ + "iLOG", /* 7: MIT log servers */ + "iCS", /* 8: cookie servers (RFC865) */ + "iLPR", /* 9: lpr server (RFC1179) */ + "iIPS", /* 10: impress servers (Imagen) */ + "iRLP", /* 11: resource location servers (RFC887) */ + "aHN", /* 12: host name (ASCII) */ + "sBFS", /* 13: boot file size (in 512 byte blocks) */ + + /* Added by RFC-1395: */ + "aDUMP", /* 14: Merit Dump File */ + "aDNAM", /* 15: Domain Name (for DNS) */ + "iSWAP", /* 16: Swap Server */ + "aROOT", /* 17: Root Path */ + + /* Added by RFC-1497: */ + "aEXTF", /* 18: Extensions Path (more options) */ + + /* Added by RFC-1533: (many, many options...) */ +#if 1 /* These might not be worth recognizing by name. */ + + /* IP Layer Parameters, per-host (RFC-1533, sect. 4) */ + "bIP-forward", /* 19: IP Forwarding flag */ + "bIP-srcroute", /* 20: IP Source Routing Enable flag */ + "iIP-filters", /* 21: IP Policy Filter (addr pairs) */ + "sIP-maxudp", /* 22: IP Max-UDP reassembly size */ + "bIP-ttlive", /* 23: IP Time to Live */ + "lIP-pmtuage", /* 24: IP Path MTU aging timeout */ + "sIP-pmtutab", /* 25: IP Path MTU plateau table */ + + /* IP parameters, per-interface (RFC-1533, sect. 5) */ + "sIP-mtu-sz", /* 26: IP MTU size */ + "bIP-mtu-sl", /* 27: IP MTU all subnets local */ + "bIP-bcast1", /* 28: IP Broadcast Addr ones flag */ + "bIP-mask-d", /* 29: IP do mask discovery */ + "bIP-mask-s", /* 30: IP do mask supplier */ + "bIP-rt-dsc", /* 31: IP do router discovery */ + "iIP-rt-sa", /* 32: IP router solicitation addr */ + "iIP-routes", /* 33: IP static routes (dst,router) */ + + /* Link Layer parameters, per-interface (RFC-1533, sect. 6) */ + "bLL-trailer", /* 34: do tralier encapsulation */ + "lLL-arp-tmo", /* 35: ARP cache timeout */ + "bLL-ether2", /* 36: Ethernet version 2 (IEEE 802.3) */ + + /* TCP parameters (RFC-1533, sect. 7) */ + "bTCP-def-ttl", /* 37: default time to live */ + "lTCP-KA-tmo", /* 38: keepalive time interval */ + "bTCP-KA-junk", /* 39: keepalive sends extra junk */ + + /* Application and Service Parameters (RFC-1533, sect. 8) */ + "aNISDOM", /* 40: NIS Domain (Sun YP) */ + "iNISSRV", /* 41: NIS Servers */ + "iNTPSRV", /* 42: NTP (time) Servers (RFC 1129) */ + "?VSINFO", /* 43: Vendor Specific Info (encapsulated) */ + "iNBiosNS", /* 44: NetBIOS Name Server (RFC-1001,1..2) */ + "iNBiosDD", /* 45: NetBIOS Datagram Dist. Server. */ + "bNBiosNT", /* 46: NetBIOS Note Type */ + "?NBiosS", /* 47: NetBIOS Scope */ + "iXW-FS", /* 48: X Window System Font Servers */ + "iXW-DM", /* 49: X Window System Display Managers */ + + /* DHCP extensions (RFC-1533, sect. 9) */ +#endif +}; +#define KNOWN_OPTIONS (sizeof(rfc1048_opts) / sizeof(rfc1048_opts[0])) + +static void print_string(); + +static void +rfc1048_print(bp, length) + register u_char *bp; + int length; +{ + u_char tag; + u_char *ep; + register int len, j; + u_int32 ul; + u_short us; + struct in_addr ia; + char *optstr; + + printf("-rfc1395"); + + /* Step over magic cookie */ + bp += sizeof(int32); + /* Setup end pointer */ + ep = bp + length; + while (bp < ep) { + tag = *bp++; + /* Check for tags with no data first. */ + if (tag == TAG_PAD) + continue; + if (tag == TAG_END) + return; + if (tag < KNOWN_OPTIONS) { + optstr = rfc1048_opts[tag]; + printf(" %s:", optstr + 1); + } else { + printf(" T%d:", tag); + optstr = "?"; + } + /* Now scan the length byte. */ + len = *bp++; + if (bp + len > ep) { + /* truncated option */ + printf(" |(%d>%d)", len, ep - bp); + return; + } + /* Print the option value(s). */ + switch (optstr[0]) { + + case 'a': /* ASCII string */ + printfn(bp, bp + len); + bp += len; + len = 0; + break; + + case 's': /* Word formats */ + while (len >= 2) { + bcopy((char *) bp, (char *) &us, 2); + printf("%d", ntohs(us)); + bp += 2; + len -= 2; + if (len) printf(","); + } + if (len) printf("(junk=%d)", len); + break; + + case 'l': /* Long words */ + while (len >= 4) { + bcopy((char *) bp, (char *) &ul, 4); + printf("%d", ntohl(ul)); + bp += 4; + len -= 4; + if (len) printf(","); + } + if (len) printf("(junk=%d)", len); + break; + + case 'i': /* INET addresses */ + while (len >= 4) { + bcopy((char *) bp, (char *) &ia, 4); + printf("%s", ipaddr_string(&ia)); + bp += 4; + len -= 4; + if (len) printf(","); + } + if (len) printf("(junk=%d)", len); + break; + + case 'b': + default: + break; + + } /* switch */ + + /* Print as characters, if appropriate. */ + if (len) { + dump_hex(bp, len); + if (isascii(*bp) && isprint(*bp)) { + printf("("); + printfn(bp, bp + len); + printf(")"); + } + bp += len; + len = 0; + } + } /* while bp < ep */ +} + +static void +cmu_print(bp, length) + register u_char *bp; + int length; +{ + struct cmu_vend *v; + u_char *ep; + + printf("-cmu"); + + v = (struct cmu_vend *) bp; + if (length < sizeof(*v)) { + printf(" |L=%d", length); + return; + } + /* Setup end pointer */ + ep = bp + length; + + /* Subnet mask */ + if (v->v_flags & VF_SMASK) { + printf(" SM:%s", ipaddr_string(&v->v_smask)); + } + /* Default gateway */ + if (v->v_dgate.s_addr) + printf(" GW:%s", ipaddr_string(&v->v_dgate)); + + /* Domain name servers */ + if (v->v_dns1.s_addr) + printf(" DNS1:%s", ipaddr_string(&v->v_dns1)); + if (v->v_dns2.s_addr) + printf(" DNS2:%s", ipaddr_string(&v->v_dns2)); + + /* IEN-116 name servers */ + if (v->v_ins1.s_addr) + printf(" INS1:%s", ipaddr_string(&v->v_ins1)); + if (v->v_ins2.s_addr) + printf(" INS2:%s", ipaddr_string(&v->v_ins2)); + + /* Time servers */ + if (v->v_ts1.s_addr) + printf(" TS1:%s", ipaddr_string(&v->v_ts1)); + if (v->v_ts2.s_addr) + printf(" TS2:%s", ipaddr_string(&v->v_ts2)); + +} + + +/* + * Print out arbitrary, unknown vendor data. + */ + +static void +other_print(bp, length) + register u_char *bp; + int length; +{ + u_char *ep; /* end pointer */ + u_char *zp; /* points one past last non-zero byte */ + register int i, j; + + /* Setup end pointer */ + ep = bp + length; + + /* Find the last non-zero byte. */ + for (zp = ep; zp > bp; zp--) { + if (zp[-1] != 0) + break; + } + + /* Print the all-zero case in a compact representation. */ + if (zp == bp) { + printf("-all-zero"); + return; + } + printf("-unknown"); + + /* Are there enough trailing zeros to make "00..." worthwhile? */ + if (zp + 2 > ep) + zp = ep; /* print them all normally */ + + /* Now just print all the non-zero data. */ + while (bp < zp) { + printf(".%02X", *bp); + bp++; + } + + if (zp < ep) + printf(".00..."); + + return; +} + +static void +dump_hex(bp, len) + u_char *bp; + int len; +{ + while (len > 0) { + printf("%02X", *bp); + bp++; + len--; + if (len) printf("."); + } +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bootpd/trygetea.c b/libexec/bootpd/trygetea.c new file mode 100644 index 0000000..e9314ae --- /dev/null +++ b/libexec/bootpd/trygetea.c @@ -0,0 +1,46 @@ +/* + * trygetea.c - test program for getether.c + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#if defined(SUNOS) || defined(SVR4) +#include <sys/sockio.h> +#endif + +#include <net/if.h> /* for struct ifreq */ +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#include <netdb.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> + +int debug = 0; +char *progname; + +main(argc, argv) + char **argv; +{ + u_char ea[16]; /* Ethernet address */ + int i; + + progname = argv[0]; /* for report */ + + if (argc < 2) { + printf("need interface name\n"); + exit(1); + } + if ((i = getether(argv[1], ea)) < 0) { + printf("Could not get Ethernet address (rc=%d)\n", i); + exit(1); + } + printf("Ether-addr"); + for (i = 0; i < 6; i++) + printf(":%x", ea[i] & 0xFF); + printf("\n"); + + exit(0); +} diff --git a/libexec/bootpd/trygetif.c b/libexec/bootpd/trygetif.c new file mode 100644 index 0000000..c6bb098 --- /dev/null +++ b/libexec/bootpd/trygetif.c @@ -0,0 +1,68 @@ +/* + * trygetif.c - test program for getif.c + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#if defined(SUNOS) || defined(SVR4) +#include <sys/sockio.h> +#endif + +#include <net/if.h> /* for struct ifreq */ +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#include <netdb.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> + +#include "getif.h" + +int debug = 0; +char *progname; + +main(argc, argv) + char **argv; +{ + struct hostent *hep; + struct sockaddr ea; /* Ethernet address */ + struct sockaddr_in *sip; /* Interface address */ + struct ifreq *ifr; + struct in_addr dst_addr; + struct in_addr *dap; + int i, s; + + progname = argv[0]; /* for report */ + + dap = NULL; + if (argc > 1) { + dap = &dst_addr; + if (isdigit(argv[1][0])) + dst_addr.s_addr = inet_addr(argv[1]); + else { + hep = gethostbyname(argv[1]); + if (!hep) { + printf("gethostbyname(%s)\n", argv[1]); + exit(1); + } + memcpy(&dst_addr, hep->h_addr, sizeof(dst_addr)); + } + } + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket open"); + exit(1); + } + ifr = getif(s, dap); + if (!ifr) { + printf("no interface for address\n"); + exit(1); + } + printf("Intf-name:%s\n", ifr->ifr_name); + sip = (struct sockaddr_in *) &(ifr->ifr_addr); + printf("Intf-addr:%s\n", inet_ntoa(sip->sin_addr)); + + exit(0); +} diff --git a/libexec/bootpd/trylook.c b/libexec/bootpd/trylook.c new file mode 100644 index 0000000..40652a2 --- /dev/null +++ b/libexec/bootpd/trylook.c @@ -0,0 +1,50 @@ +/* + * trylook.c - test program for lookup.c + */ + +#include <sys/types.h> +#include <netinet/in.h> +#include <stdio.h> + +#include "report.h" +#include "lookup.h" + +extern char *ether_ntoa(); +extern char *inet_ntoa(); + +int debug = 0; +char *progname; + +main(argc, argv) + char **argv; +{ + int i; + struct in_addr in; + char *a; + u_char *hwa; + + progname = argv[0]; /* for report */ + + for (i = 1; i < argc; i++) { + + /* Host name */ + printf("%s:", argv[i]); + + /* IP addr */ + if (lookup_ipa(argv[i], &in.s_addr)) + a = "?"; + else + a = inet_ntoa(in); + printf(" ipa=%s", a); + + /* Ether addr */ + hwa = lookup_hwa(argv[i], 1); + if (!hwa) + a = "?"; + else + a = ether_ntoa(hwa); + printf(" hwa=%s\n", a); + + } + exit(0); +} diff --git a/libexec/bootpd/tzone.c b/libexec/bootpd/tzone.c new file mode 100644 index 0000000..4adc4ae --- /dev/null +++ b/libexec/bootpd/tzone.c @@ -0,0 +1,44 @@ +/* + * tzone.c - get the timezone + * + * This is shared by bootpd and bootpef + */ + +#ifdef SVR4 +/* XXX - Is this really SunOS specific? -gwr */ +/* This is in <time.h> but only visible if (__STDC__ == 1). */ +extern long timezone; +#else /* SVR4 */ +/* BSD or SunOS */ +# include <sys/time.h> +# include <syslog.h> +#endif /* SVR4 */ + +#include "bptypes.h" +#include "report.h" +#include "tzone.h" + +/* This is what other modules use. */ +int32 secondswest; + +/* + * Get our timezone offset so we can give it to clients if the + * configuration file doesn't specify one. + */ +void +tzone_init() +{ +#ifdef SVR4 + /* XXX - Is this really SunOS specific? -gwr */ + secondswest = timezone; +#else /* SVR4 */ + struct timezone tzp; /* Time zone offset for clients */ + struct timeval tp; /* Time (extra baggage) */ + if (gettimeofday(&tp, &tzp) < 0) { + secondswest = 0; /* Assume GMT for lack of anything better */ + report(LOG_ERR, "gettimeofday: %s", get_errmsg()); + } else { + secondswest = 60L * tzp.tz_minuteswest; /* Convert to seconds */ + } +#endif /* SVR4 */ +} diff --git a/libexec/bootpd/tzone.h b/libexec/bootpd/tzone.h new file mode 100644 index 0000000..ddd67c4 --- /dev/null +++ b/libexec/bootpd/tzone.h @@ -0,0 +1,3 @@ +/* tzone.h */ +extern int32 secondswest; +extern void tzone_init(); diff --git a/libexec/bootpgw/Makefile b/libexec/bootpgw/Makefile new file mode 100644 index 0000000..a155e0e --- /dev/null +++ b/libexec/bootpgw/Makefile @@ -0,0 +1,12 @@ +# Makefile +# $Id$ + +PROG= bootpgw +NOMAN= true +SRCS= bootpgw.c getif.c hwaddr.c report.c rtmsg.c + +SRCDIR= ${.CURDIR}/../bootpd +CFLAGS+=-I${SRCDIR} +.PATH: ${SRCDIR} + +.include <bsd.prog.mk> diff --git a/libexec/bootpgw/bootpgw.c b/libexec/bootpgw/bootpgw.c new file mode 100644 index 0000000..4e7538b --- /dev/null +++ b/libexec/bootpgw/bootpgw.c @@ -0,0 +1,675 @@ +/* + * bootpgw.c - BOOTP GateWay + * This program forwards BOOTP Request packets to a BOOTP server. + */ + +/************************************************************************ + Copyright 1988, 1991 by Carnegie Mellon University + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the name of Carnegie Mellon University not be used +in advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. +************************************************************************/ + +#ifndef lint +static char rcsid[] = "$Id: bootpgw.c,v 1.1.1.1 1994/09/10 14:44:54 csgr Exp $"; +#endif + +/* + * BOOTPGW is typically used to forward BOOTP client requests from + * one subnet to a BOOTP server on a different subnet. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ + +#ifndef NO_UNISTD +#include <unistd.h> +#endif +#include <stdlib.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <netdb.h> +#include <syslog.h> +#include <assert.h> + +#ifdef NO_SETSID +# include <fcntl.h> /* for O_RDONLY, etc */ +#endif + +#ifndef USE_BFUNCS +# include <memory.h> +/* Yes, memcpy is OK here (no overlapped copies). */ +# define bcopy(a,b,c) memcpy(b,a,c) +# define bzero(p,l) memset(p,0,l) +# define bcmp(a,b,c) memcmp(a,b,c) +#endif + +#include "bootp.h" +#include "getif.h" +#include "hwaddr.h" +#include "report.h" +#include "patchlevel.h" + +/* Local definitions: */ +#define MAX_MSG_SIZE (3*512) /* Maximum packet size */ +#define TRUE 1 +#define FALSE 0 +#define get_network_errmsg get_errmsg + + + +/* + * Externals, forward declarations, and global variables + */ + +#ifdef __STDC__ +#define P(args) args +#else +#define P(args) () +#endif + +static void usage P((void)); +static void handle_reply P((void)); +static void handle_request P((void)); + +#undef P + +/* + * IP port numbers for client and server obtained from /etc/services + */ + +u_short bootps_port, bootpc_port; + + +/* + * Internet socket and interface config structures + */ + +struct sockaddr_in bind_addr; /* Listening */ +struct sockaddr_in recv_addr; /* Packet source */ +struct sockaddr_in send_addr; /* destination */ + + +/* + * option defaults + */ +int debug = 0; /* Debugging flag (level) */ +struct timeval actualtimeout = +{ /* fifteen minutes */ + 15 * 60L, /* tv_sec */ + 0 /* tv_usec */ +}; +u_int maxhops = 4; /* Number of hops allowed for requests. */ +u_int minwait = 3; /* Number of seconds client must wait before + its bootrequest packets are forwarded. */ + +/* + * General + */ + +int s; /* Socket file descriptor */ +char *pktbuf; /* Receive packet buffer */ +int pktlen; +char *progname; +char *servername; +int32 server_ipa; /* Real server IP address, network order. */ + +char myhostname[64]; +struct in_addr my_ip_addr; + + + + +/* + * Initialization such as command-line processing is done and then the + * main server loop is started. + */ + +void +main(argc, argv) + int argc; + char **argv; +{ + struct timeval *timeout; + struct bootp *bp; + struct servent *servp; + struct hostent *hep; + char *stmp; + int n, ba_len, ra_len; + int nfound, readfds; + int standalone; + + progname = strrchr(argv[0], '/'); + if (progname) progname++; + else progname = argv[0]; + + /* + * Initialize logging. + */ + report_init(0); /* uses progname */ + + /* + * Log startup + */ + report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL); + + /* Debugging for compilers with struct padding. */ + assert(sizeof(struct bootp) == BP_MINPKTSZ); + + /* Get space for receiving packets and composing replies. */ + pktbuf = malloc(MAX_MSG_SIZE); + if (!pktbuf) { + report(LOG_ERR, "malloc failed"); + exit(1); + } + bp = (struct bootp *) pktbuf; + + /* + * Check to see if a socket was passed to us from inetd. + * + * Use getsockname() to determine if descriptor 0 is indeed a socket + * (and thus we are probably a child of inetd) or if it is instead + * something else and we are running standalone. + */ + s = 0; + ba_len = sizeof(bind_addr); + bzero((char *) &bind_addr, ba_len); + errno = 0; + standalone = TRUE; + if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) { + /* + * Descriptor 0 is a socket. Assume we are a child of inetd. + */ + if (bind_addr.sin_family == AF_INET) { + standalone = FALSE; + bootps_port = ntohs(bind_addr.sin_port); + } else { + /* Some other type of socket? */ + report(LOG_INFO, "getsockname: not an INET socket"); + } + } + /* + * Set defaults that might be changed by option switches. + */ + stmp = NULL; + timeout = &actualtimeout; + gethostname(myhostname, sizeof(myhostname)); + hep = gethostbyname(myhostname); + if (!hep) { + printf("Can not get my IP address\n"); + exit(1); + } + bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); + + /* + * Read switches. + */ + for (argc--, argv++; argc > 0; argc--, argv++) { + if (argv[0][0] != '-') + break; + switch (argv[0][1]) { + + case 'd': /* debug level */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else if (argv[1] && argv[1][0] == '-') { + /* + * Backwards-compatible behavior: + * no parameter, so just increment the debug flag. + */ + debug++; + break; + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { + fprintf(stderr, + "%s: invalid debug level\n", progname); + break; + } + debug = n; + break; + + case 'h': /* hop count limit */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || + (n < 0) || (n > 16)) + { + fprintf(stderr, + "bootpgw: invalid hop count limit\n"); + break; + } + maxhops = (u_int)n; + break; + + case 'i': /* inetd mode */ + standalone = FALSE; + break; + + case 's': /* standalone mode */ + standalone = TRUE; + break; + + case 't': /* timeout */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { + fprintf(stderr, + "%s: invalid timeout specification\n", progname); + break; + } + actualtimeout.tv_sec = (int32) (60 * n); + /* + * If the actual timeout is zero, pass a NULL pointer + * to select so it blocks indefinitely, otherwise, + * point to the actual timeout value. + */ + timeout = (n > 0) ? &actualtimeout : NULL; + break; + + case 'w': /* wait time */ + if (argv[0][2]) { + stmp = &(argv[0][2]); + } else { + argc--; + argv++; + stmp = argv[0]; + } + if (!stmp || (sscanf(stmp, "%d", &n) != 1) || + (n < 0) || (n > 60)) + { + fprintf(stderr, + "bootpgw: invalid wait time\n"); + break; + } + minwait = (u_int)n; + break; + + default: + fprintf(stderr, "%s: unknown switch: -%c\n", + progname, argv[0][1]); + usage(); + break; + + } /* switch */ + } /* for args */ + + /* Make sure server name argument is suplied. */ + servername = argv[0]; + if (!servername) { + fprintf(stderr, "bootpgw: missing server name\n"); + usage(); + } + /* + * Get address of real bootp server. + */ + if (isdigit(servername[0])) + server_ipa = inet_addr(servername); + else { + hep = gethostbyname(servername); + if (!hep) { + fprintf(stderr, "bootpgw: can't get addr for %s\n", servername); + exit(1); + } + bcopy(hep->h_addr, (char *)&server_ipa, sizeof(server_ipa)); + } + + if (standalone) { + /* + * Go into background and disassociate from controlling terminal. + * XXX - This is not the POSIX way (Should use setsid). -gwr + */ + if (debug < 3) { + if (fork()) + exit(0); +#ifdef NO_SETSID + setpgrp(0,0); +#ifdef TIOCNOTTY + n = open("/dev/tty", O_RDWR); + if (n >= 0) { + ioctl(n, TIOCNOTTY, (char *) 0); + (void) close(n); + } +#endif /* TIOCNOTTY */ +#else /* SETSID */ + if (setsid() < 0) + perror("setsid"); +#endif /* SETSID */ + } /* if debug < 3 */ + /* + * Nuke any timeout value + */ + timeout = NULL; + + /* + * Here, bootpd would do: + * chdir + * tzone_init + * rdtab_init + * readtab + */ + + /* + * Create a socket. + */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + report(LOG_ERR, "socket: %s", get_network_errmsg()); + exit(1); + } + /* + * Get server's listening port number + */ + servp = getservbyname("bootps", "udp"); + if (servp) { + bootps_port = ntohs((u_short) servp->s_port); + } else { + bootps_port = (u_short) IPPORT_BOOTPS; + report(LOG_ERR, + "udp/bootps: unknown service -- assuming port %d", + bootps_port); + } + + /* + * Bind socket to BOOTPS port. + */ + bind_addr.sin_family = AF_INET; + bind_addr.sin_port = htons(bootps_port); + bind_addr.sin_addr.s_addr = INADDR_ANY; + if (bind(s, (struct sockaddr *) &bind_addr, + sizeof(bind_addr)) < 0) + { + report(LOG_ERR, "bind: %s", get_network_errmsg()); + exit(1); + } + } /* if standalone */ + /* + * Get destination port number so we can reply to client + */ + servp = getservbyname("bootpc", "udp"); + if (servp) { + bootpc_port = ntohs(servp->s_port); + } else { + report(LOG_ERR, + "udp/bootpc: unknown service -- assuming port %d", + IPPORT_BOOTPC); + bootpc_port = (u_short) IPPORT_BOOTPC; + } + + /* no signal catchers */ + + /* + * Process incoming requests. + */ + for (;;) { + readfds = 1 << s; + nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, timeout); + if (nfound < 0) { + if (errno != EINTR) { + report(LOG_ERR, "select: %s", get_errmsg()); + } + continue; + } + if (!(readfds & (1 << s))) { + report(LOG_INFO, "exiting after %ld minutes of inactivity", + actualtimeout.tv_sec / 60); + exit(0); + } + ra_len = sizeof(recv_addr); + n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, + (struct sockaddr *) &recv_addr, &ra_len); + if (n <= 0) { + continue; + } + if (debug > 3) { + report(LOG_INFO, "recvd pkt from IP addr %s", + inet_ntoa(recv_addr.sin_addr)); + } + if (n < sizeof(struct bootp)) { + if (debug) { + report(LOG_INFO, "received short packet"); + } + continue; + } + pktlen = n; + + switch (bp->bp_op) { + case BOOTREQUEST: + handle_request(); + break; + case BOOTREPLY: + handle_reply(); + break; + } + } +} + + + + +/* + * Print "usage" message and exit + */ + +static void +usage() +{ + fprintf(stderr, + "usage: bootpgw [-d level] [-i] [-s] [-t timeout] server\n"); + fprintf(stderr, "\t -d n\tset debug level\n"); + fprintf(stderr, "\t -h n\tset max hop count\n"); + fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); + fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); + fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); + fprintf(stderr, "\t -w n\tset min wait time (secs)\n"); + exit(1); +} + + + +/* + * Process BOOTREQUEST packet. + * + * Note, this just forwards the request to a real server. + */ +static void +handle_request() +{ + struct bootp *bp = (struct bootp *) pktbuf; + struct ifreq *ifr; + u_short secs, hops; + + /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ + + if (debug) { + report(LOG_INFO, "request from %s", + inet_ntoa(recv_addr.sin_addr)); + } + /* Has the client been waiting long enough? */ + secs = ntohs(bp->bp_secs); + if (secs < minwait) + return; + + /* Has this packet hopped too many times? */ + hops = ntohs(bp->bp_hops); + if (++hops > maxhops) { + report(LOG_NOTICE, "reqest from %s reached hop limit", + inet_ntoa(recv_addr.sin_addr)); + return; + } + bp->bp_hops = htons(hops); + + /* + * Here one might discard a request from the same subnet as the + * real server, but we can assume that the real server will send + * a reply to the client before it waits for minwait seconds. + */ + + /* If gateway address is not set, put in local interface addr. */ + if (bp->bp_giaddr.s_addr == 0) { +#if 0 /* BUG */ + struct sockaddr_in *sip; + /* + * XXX - This picks the wrong interface when the receive addr + * is the broadcast address. There is no portable way to + * find out which interface a broadcast was received on. -gwr + * (Thanks to <walker@zk3.dec.com> for finding this bug!) + */ + ifr = getif(s, &recv_addr.sin_addr); + if (!ifr) { + report(LOG_NOTICE, "no interface for request from %s", + inet_ntoa(recv_addr.sin_addr)); + return; + } + sip = (struct sockaddr_in *) &(ifr->ifr_addr); + bp->bp_giaddr = sip->sin_addr; +#else /* BUG */ + /* + * XXX - Just set "giaddr" to our "official" IP address. + * RFC 1532 says giaddr MUST be set to the address of the + * interface on which the request was received. Setting + * it to our "default" IP address is not strictly correct, + * but is good enough to allow the real BOOTP server to + * get the reply back here. Then, before we forward the + * reply to the client, the giaddr field is corrected. + * (In case the client uses giaddr, which it should not.) + * See handle_reply() + */ + bp->bp_giaddr = my_ip_addr; +#endif /* BUG */ + + /* + * XXX - DHCP says to insert a subnet mask option into the + * options area of the request (if vendor magic == std). + */ + } + /* Set up socket address for send. */ + send_addr.sin_family = AF_INET; + send_addr.sin_port = htons(bootps_port); + send_addr.sin_addr.s_addr = server_ipa; + + /* Send reply with same size packet as request used. */ + if (sendto(s, pktbuf, pktlen, 0, + (struct sockaddr *) &send_addr, + sizeof(send_addr)) < 0) + { + report(LOG_ERR, "sendto: %s", get_network_errmsg()); + } +} + + + +/* + * Process BOOTREPLY packet. + */ +static void +handle_reply() +{ + struct bootp *bp = (struct bootp *) pktbuf; + struct ifreq *ifr; + struct sockaddr_in *sip; + u_char canon_haddr[MAXHADDRLEN]; + unsigned char *ha; + int len; + + if (debug) { + report(LOG_INFO, " reply for %s", + inet_ntoa(bp->bp_yiaddr)); + } + /* Make sure client is directly accessible. */ + ifr = getif(s, &(bp->bp_yiaddr)); + if (!ifr) { + report(LOG_NOTICE, "no interface for reply to %s", + inet_ntoa(bp->bp_yiaddr)); + return; + } +#if 1 /* Experimental (see BUG above) */ +/* #ifdef CATER_TO_OLD_CLIENTS ? */ + /* + * The giaddr field has been set to our "default" IP address + * which might not be on the same interface as the client. + * In case the client looks at giaddr, (which it should not) + * giaddr is now set to the address of the correct interface. + */ + sip = (struct sockaddr_in *) &(ifr->ifr_addr); + bp->bp_giaddr = sip->sin_addr; +#endif + + /* Set up socket address for send to client. */ + send_addr.sin_family = AF_INET; + send_addr.sin_addr = bp->bp_yiaddr; + send_addr.sin_port = htons(bootpc_port); + + /* Create an ARP cache entry for the client. */ + ha = bp->bp_chaddr; + len = bp->bp_hlen; + if (len > MAXHADDRLEN) + len = MAXHADDRLEN; + if (bp->bp_htype == HTYPE_IEEE802) { + haddr_conv802(ha, canon_haddr, len); + ha = canon_haddr; + } + if (debug > 1) + report(LOG_INFO, "setarp %s - %s", + inet_ntoa(bp->bp_yiaddr), haddrtoa(ha, len)); + setarp(s, &bp->bp_yiaddr, ha, len); + + /* Send reply with same size packet as request used. */ + if (sendto(s, pktbuf, pktlen, 0, + (struct sockaddr *) &send_addr, + sizeof(send_addr)) < 0) + { + report(LOG_ERR, "sendto: %s", get_network_errmsg()); + } +} + +/* + * Local Variables: + * tab-width: 4 + * c-indent-level: 4 + * c-argdecl-indent: 4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: -4 + * c-label-offset: -4 + * c-brace-offset: 0 + * End: + */ diff --git a/libexec/bugfiler/Makefile b/libexec/bugfiler/Makefile new file mode 100644 index 0000000..fb77633 --- /dev/null +++ b/libexec/bugfiler/Makefile @@ -0,0 +1,17 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= bugfiler +CFLAGS+=-I${.CURDIR} +SRCS= bugfiler.c error.c gethead.c process.c redist.c reply.c +BINOWN= root +BINMODE=4555 +MAN1= sendbug.1 +MAN8= bugfiler.8 + +beforeinstall: + install -c -o bin -g ${BINGRP} -m 555 \ + ${.CURDIR}/sendbug.sh ${DESTDIR}/usr/bin/sendbug + install -c -o bin -g ${BINGRP} -m 444 ${.CURDIR}/bugformat \ + ${DESTDIR}/usr/share/misc + +.include <bsd.prog.mk> diff --git a/libexec/bugfiler/bug.h b/libexec/bugfiler/bug.h new file mode 100644 index 0000000..69ea986 --- /dev/null +++ b/libexec/bugfiler/bug.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 1986, 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)bug.h 8.1 (Berkeley) 6/4/93 + */ + +#define BUGS_HOME "owner-bugs@ucbvax.Berkeley.EDU" +#define BUGS_ID "bugs" + +/* + * the METOO definition has the bugfiler exit with an error (-1) status + * if there's a problem. This causes sendmail to send off a copy of the + * report (as failed mail) to the "owner" of the mail alias that executed + * the bugfiler. This is great if you would have otherwise lost the bug + * report. It's not so great if you get a whole bunch of mail that you + * really don't want. + */ +#define METOO + +/* files */ +#define ACK_FILE "bug:ack" /* acknowledge file */ +#define DIST_FILE "bug:redist" /* redistribution file */ +#define ERROR_FILE "log" /* error file */ +#define LOCK_FILE "bug:lock" /* lock file name */ +#define SUMMARY_FILE "summary" /* summary file */ +#define TMP_BUG "errors/BUG_XXXXXX" /* tmp bug report */ +#define TMP_DIR "errors" /* tmp directory */ + +#define CHN (char *)NULL /* null arg string */ +#define COMMENT '#' /* comment in redist file */ +#define EOS (char)NULL /* end of string */ +#define ERR -1 /* error return */ +#define MAXLINELEN 200 /* max line length in message */ +#define NO 0 /* no/false */ +#define OK 0 /* okay return */ +#define YES 1 /* yes/true */ + +typedef struct { + short found, /* line number if found */ + redist; /* if part of redist headers */ + int (*valid)(); /* validation routine */ + short len; /* length of tag */ + char *tag, /* leading tag */ + *line; /* actual line */ +} HEADER; +extern HEADER mailhead[]; + +#define DATE_TAG 0 /* "Date:" offset */ +#define FROM_TAG 1 /* "From " offset */ +#define CFROM_TAG 2 /* "From:" offset */ +#define INDX_TAG 3 /* "Index:" offset */ +#define MSG_TAG 4 /* "Message-Id:" offset */ +#define RPLY_TAG 5 /* "Reply-To:" offset */ +#define RET_TAG 6 /* "Return-Path:" offset */ +#define SUBJ_TAG 7 /* "Subject:" offset */ +#define TO_TAG 8 /* "To:" offset */ +#define APPAR_TO_TAG 9 /* "Apparently-To:" offset */ + +/* so sizeof doesn't return 0 */ +extern char bfr[MAXBSIZE], /* general I/O buffer */ + dir[MAXNAMLEN], /* subject and folder */ + folder[MAXNAMLEN], + tmpname[sizeof(TMP_BUG) + 5]; /* temp bug file */ diff --git a/libexec/bugfiler/bugfiler.8 b/libexec/bugfiler/bugfiler.8 new file mode 100644 index 0000000..1fc86d2 --- /dev/null +++ b/libexec/bugfiler/bugfiler.8 @@ -0,0 +1,290 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)bugfiler.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt BUGFILER 8 +.Os BSD 4.2 +.Sh NAME +.Nm bugfiler +.Nd file bug reports in folders automatically +.Sh SYNOPSIS +.Nm bugfiler +.Op Fl ar +.Op Fl v Ar version +.Sh DESCRIPTION +.Nm Bugfiler +is a program to automatically intercept, acknowledge, +redistribute and store bug reports. +.Nm Bugfiler +is normally invoked +by the mail delivery program with a line similar to the following in +.Pa /etc/aliases . +.Bd -literal -offset indent +bugs: "|bugfiler" +.Ed +.Pp +It should be noted that the login +.Dq bugs +must exist for the bugfiler +to run. Unless otherwise noted all paths used by +.Nm bugfiler +are +relative to the home directory of this login. +.Nm Bugfiler +also +expects all of its files and directories to be owned by +.Dq bugs . +.Pp +Available options. +.Bl -tag -width Ds +.It Fl a +Do not send automatic mail acknowledgement to the bug report filer. +(The default is to send the acknowledgement with the file +.Pa ~bugs/version/bug:ack +appended). +.It Fl r +Do not redistribute. +.It Fl v Ar version +Override the +.Ar version +provided within the bug report itself. +.El +.Pp +For the bug report to be correctly filed, it must contain a line +in the following format: +.Pp +.Bd -filled -offset indent -compact +.Bl -column Index folder +.It Index: Ta Em folder Ta Ar version +.El +.Ed +.Pp +The directories +.Pa ~bugs/ Ns Ar version +and +.Pa ~bugs/ Ns Ar version/ Ns Em folder +must exist before +.Nm bugfiler +attempts to store the bug report. Bug +reports will be stored in files named by the concatenation of +.Ar version , +.Em folder , +and sequential numbers, i.e. if +.Ar version +is +.Dq 4.3 Tn BSD +and +.Em folder +is +.Dq ucb +the first bug report will be placed in +.Pa ~bugs/4.3BSD/ucb/1 . +If +.Em folder +contains more than one component only +the first one will be used, e.g. if +.Em folder +is +.Dq bin/from.c +or +.Dq bin/adb/con.c +it will be treated as if it were simply +.Dq bin . +.Pp +.Pp +If the +.Fl r +flag is not supplied, redistribution of the bug reports +is done as specified in the file +.Pa ~bugs/version/bug:redist . +This file +is in the format of the +.Xr aliases 5 +file, including comments and +entries requiring multiple lines, with the single exception that the +.Em folder +component of the +.Dq Index: +line replaces the name to alias. +The special folder +.Dq all: +receives a redistribution of all bug reports +sent to this +.Ar version . +For example, the +.Pa bug:redist +file +.Pp +.Bd -literal -offset indent -compact +# bigbug gets a copy of everything +all: bigbug +# ucb folder redistribution list +ucb: karels, kjd@coke.berkeley.edu + ra@beno.css.gov +.Ed +.Pp +will send copies of all bug reports with +.Dq ucb +as the +.Em folder +to bigbug, karels, kjd, and ra. +.Pp +Reports that cannot be filed, due to an invalid +.Dq Index: +line or +some other error, are placed in the directory +.Pa ~bugs/errors . +The +.Nm bugfiler +maintainer should correct these bug reports and then +run +.Nm bugfiler , +with the corrected report as its standard input, +as bug reports with errors are neither acknowledged or redistributed. +All reports that +.Nm bugfiler +handles are logged in +.Pa ~bugs/log. +.Pp +Valid bugs are also logged in the file +.Pa ~bugs/version/summary. +This file has an entry for each bug report for +.Ar version +in the +format: +.Pp +.Bd -literal -offset indent -compact +Filename Date + Subject: + Index: + Owner: Bugs Bunny + Status: Received +.Ed +.Pp +.Li Filename +is the concatenation of +.Ar version , +.Em folder , +and a number +as described above. +.Xr Date +is the date as reported by the system +clock, using +.Xr ctime 3 . +The +.Li Subject: +and +.Li Index: +lines are +copies of the +.Dq Subject: +and +.Dq index: +lines contained in the bug +report. The +.Li Owner +and +.Li Status +fields are intended to provide a +rudimentary method of tracking the status of bug reports. +.Pp +The file +.Pa ~bugs/bug:lock +is the focus of all locking for +.Nm bugfiler . +If you wish to manipulate any of the log or error files, rename or remove +it and +.Nm bugfiler +will treat all bug reports that it receives as if +they were incorrectly formatted, i.e. it will place them in the directory +.Pa ~bugs/errors , +for later recovery by the +.Nm bugfiler +maintainer. +Obviously, this file must be created when you first install +.Nm bugfiler . +.Pp +All errors that occur before +.Pa ~bugs/log +is found are logged into the system +log file, using +.Xr syslog 8 . +.Sh FILES +.Bl -tag -width /usr/share/misc/bugformatxx -compact +.It Pa ~bugs/bug:ack +the acknowledgement message +.It Pa ~bugs/bug:redist +the redistribution list +.It Pa ~bugs/bug:lock +the locking file +.It Pa ~bugs/errors/BUG_?????? +bug reports with format errors +.It Pa ~bugs/log +the log file +.It Pa ~bugs/folder/summary +the summary files +.It Pa /usr/sbin/sendmail +the mail delivery program +.It Pa /usr/share/misc/bugformat +a sample bug report format +.El +.Sh SEE ALSO +.Xr sendbug 1 , +.Xr aliases 5 , +.Xr syslog 8 +.Sh BUGS +Since mail can be forwarded in a number of different ways, +.Nm bugfiler +does not recognize forwarded mail and will acknowledge to the forwarder +instead of the original sender unless there is a +.Dq Reply-To +field in the +header. +.Pp +This version of +.Nm bugfiler +is not compatible with the version +released with +.Bx 4.3 +in that it doesn't complain to the sender about +incorrectly formatted bug reports. +Frankly, we got tired of the profanity, not to mention the extended +conversations +.Nm bugfiler +was holding with +.Xr vacation 1 . +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/libexec/bugfiler/bugfiler.c b/libexec/bugfiler/bugfiler.c new file mode 100644 index 0000000..75dbef3 --- /dev/null +++ b/libexec/bugfiler/bugfiler.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 1983, 1986, 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1986, 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)bugfiler.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * Bug report processing program, designed to be invoked + * through aliases(5). + */ +#include <sys/param.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <dirent.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "bug.h" +#include "extern.h" + +char bfr[MAXBSIZE], /* general I/O buffer */ + tmpname[sizeof(TMP_BUG) + 5]; /* temp bug file */ + +static void logit __P((void)); +static void make_copy __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; /* getopt arguments */ + register struct passwd *pwd; /* bugs password entry */ + register int ch; /* getopts char */ + int do_ack, /* acknowledge bug report */ + do_redist; /* redistribut BR */ + char *argversion; /* folder name provided */ + + do_ack = do_redist = YES; + argversion = NULL; + while ((ch = getopt(argc, argv, "av:r")) != EOF) + switch(ch) { + case 'a': + do_ack = NO; + break; + case 'v': + argversion = optarg; + break; + case 'r': + do_redist = NO; + break; + case '?': + default: + fputs("usage: bugfiler [-ar] [-v version]\n", stderr); + error("usage: bugfiler [-ar] [-v version]", CHN); + } + + if (!(pwd = getpwnam(BUGS_ID))) + error("can't find bugs login.", BUGS_ID); + + if (chdir(pwd->pw_dir)) /* change to bugs home directory */ + error("can't chdir to %s.", pwd->pw_dir); + + if (seteuid(pwd->pw_uid)) + error("can't set id to %s.", BUGS_ID); + + (void)umask(02); /* everything is 664 */ + seterr(); /* redirect to log file */ + logit(); /* log report arrival */ + make_copy(); /* save copy in case */ + gethead(do_redist); + + if (argversion) /* specific folder requested */ + (void)strcpy(dir, argversion); + + process(); + + if (seteuid(0)) + error("can't set id to root.", CHN); + if (do_ack) + reply(); + if (do_redist) + redist(); + (void)unlink(tmpname); + exit(OK); +} + +/* + * make_copy -- + * make a copy of bug report in error folder + */ +static void +make_copy() +{ + register int cnt, /* read return value */ + tfd; /* temp file descriptor */ + + if (access(TMP_DIR, F_OK)) + (void)mkdir(TMP_DIR, S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH); + (void)strcpy(tmpname, TMP_BUG); + if (tfd = mkstemp(tmpname)) { + while ((cnt = read(fileno(stdin), + bfr, sizeof(bfr))) != ERR && cnt) + write(tfd, bfr, cnt); + (void)close(tfd); + return; + } + error("can't make copy using %s.", tmpname); +} + +/* + * logit -- + * log this run of the bugfiler + */ +static void +logit() +{ + struct timeval tp; + char *C1, *C2; + + if (gettimeofday(&tp, (struct timezone *)NULL)) + error("can't get time of day.", CHN); + for (C1 = C2 = ctime(&tp.tv_sec); *C1 && *C1 != '\n'; ++C1); + *C1 = EOS; + fputs(C2, stderr); +} diff --git a/libexec/bugfiler/bugformat b/libexec/bugfiler/bugformat new file mode 100644 index 0000000..97a767d --- /dev/null +++ b/libexec/bugfiler/bugformat @@ -0,0 +1,32 @@ +Subject: Short summary of the problem (please make this meaningful!) +Index: folder 4.4BSD-alpha + +Description: + Detailed description of the problem, suggestion, or complaint. +Repeat-By: + Describe the sequence of events that causes the problem + to occur. +Fix: + Description of how to fix the problem. If you don't know a + fix for the problem, don't include this section. + +-------- Remove this line and what's below it, for reference only. -------- + +To ensure that your bug report is handled correctly by bugfiler(8), +you must replace "folder" (on the line above starting with "Index:") +with one of the following values: + + folder ::= bin | doc | etc | games | ideas | include | lib + | local | man | misc | new | sys | ucb + | usr.bin | usr.lib + +If you're not running 4.3BSD, you should also replace "4.3BSD" on +the same line with one of the following values. + + version ::= 4.3BSD | 4.3BSD-tahoe | 4.3BSD-reno | net2 + | 4.4BSD-alpha + +For example, if your bug concerns the program "/usr/bin/file" and +you're currently running 4.3BSD-Reno, you should replace "folder" +with "usr.bin/file", and "4.4BSD-alpha" with "4.3BSD-Reno". The folder +"ideas" is for suggestions. diff --git a/libexec/bugfiler/error.c b/libexec/bugfiler/error.c new file mode 100644 index 0000000..bd00b2b --- /dev/null +++ b/libexec/bugfiler/error.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 1986, 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)error.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> + +#include <dirent.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include "bug.h" +#include "extern.h" + +static short err_redir; /* stderr redirected */ + +/* + * seterr -- + * redirect stderr for error processing + */ +void +seterr() +{ + if (!freopen(ERROR_FILE, "a", stderr)) + error("can't open error file %s.", ERROR_FILE); + err_redir = YES; +} + +/* + * error -- + * write errors to log file and die + */ +void +error(fmt, arg) + register char *fmt, + *arg; +{ + static char logmsg[MAXLINELEN]; /* syslog message */ + + if (err_redir) { + /* don't combine these, "fmt" may not require "arg" */ + fprintf(stderr, "\t%s\n\t", tmpname); + fprintf(stderr, fmt, arg); + fputc('\n', stderr); + } + else { + sprintf(logmsg, "bugfiler: %s", fmt); + syslog(LOG_ERR, logmsg, arg); + } +#ifdef METOO + exit(ERR); +#else + exit(OK); +#endif +} diff --git a/libexec/bugfiler/extern.h b/libexec/bugfiler/extern.h new file mode 100644 index 0000000..b096f3d --- /dev/null +++ b/libexec/bugfiler/extern.h @@ -0,0 +1,41 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/4/93 + */ + +void error __P((char *, char *)); +void gethead __P((int)); +int process __P((void)); +void redist __P((void)); +void reply __P((void)); +void seterr __P((void)); diff --git a/libexec/bugfiler/gethead.c b/libexec/bugfiler/gethead.c new file mode 100644 index 0000000..ba7e361 --- /dev/null +++ b/libexec/bugfiler/gethead.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 1986, 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)gethead.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <dirent.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "pathnames.h" +#include "bug.h" +#include "extern.h" + +static int chk1 __P((char *)); +static int pbuf __P((char *)); + +#define ENT(X) sizeof(X) - 1, X +HEADER mailhead[] = { /* mail headers */ + { NO, YES, NULL, ENT("Date:"), }, + { NO, NO, NULL, ENT("From "), }, + { NO, YES, NULL, ENT("From:"), }, + { NO, NO, chk1, ENT("Index:"), }, + { NO, YES, NULL, ENT("Message-Id:"), }, + { NO, YES, NULL, ENT("Reply-To:"), }, + { NO, YES, NULL, ENT("Return-Path:"), }, + { NO, NO, pbuf, ENT("Subject:"), }, + { NO, YES, NULL, ENT("To:"), }, + { NO, NO, NULL, ENT("Apparently-To:"), }, + { ERR, } +}; + +FILE *dfp; /* distf file pointer */ +char dir[MAXNAMLEN], /* subject and folder */ + folder[MAXNAMLEN]; + +/* + * gethead -- + * read mail and bug headers from bug report, construct redist headers + */ +void +gethead(redist) + int redist; +{ + register HEADER *hp; /* mail header pointer */ + + if (redist) { + int fd; + char *distf; + + distf = strdup(_PATH_TMP); + if (!(fd = mkstemp(distf)) || !(dfp = fdopen(fd, "w+"))) + error("can't create redistribution file %s.", distf); + /* disappear after last reference is closed */ + (void)unlink(distf); + free(distf); + } + if (!freopen(tmpname, "r", stdin)) + error("can't read temporary bug file %s.", tmpname); + + while (fgets(bfr, sizeof(bfr), stdin)) { + for (hp = mailhead; hp->found != ERR; ++hp) + if (!hp->found) + if (!strncmp(hp->tag, bfr, hp->len)) { + if (hp->valid && !((*(hp->valid))(bfr))) + break; + if (!(hp->line = + malloc((u_int)(strlen(bfr) + 1)))) + error("malloc failed.", CHN); + (void)strcpy(hp->line, bfr); + hp->found = YES; + break; + } + if ((hp->found == ERR || hp->redist) && redist) + fputs(bfr, dfp); + } + + if (!mailhead[INDX_TAG].found) + error("no readable \"Index:\" header in bug report.", CHN); +} + +/* + * chk1 -- + * parse the "Index:" line into folder and directory + */ +static int +chk1(line) + char *line; +{ + register char *C; /* tmp pointer */ + struct stat sbuf; /* existence check */ + + if (sscanf(line, " Index: %s %s ", folder, dir) != 2) + return(NO); + if (C = strchr(folder, '/')) { /* deal with "bin/from.c" */ + if (C == folder) + return(NO); + *C = EOS; + } + if (stat(dir, &sbuf) || (sbuf.st_mode & S_IFMT) != S_IFDIR) + return(NO); + (void)pbuf(line); + return(YES); +} + +/* + * pbuf -- + * kludge so that summary file looks pretty + */ +static int +pbuf(line) + char *line; +{ + register char *rp, /* tmp pointers */ + *wp; + + for (rp = line; *rp == ' ' || *rp == '\t'; ++rp); + for (wp = line; *rp; ++wp) { + if ((*wp = *rp++) != ' ' && *wp != '\t') + continue; + *wp = ' '; + while (*rp == ' ' || *rp == '\t') + ++rp; + } + if (wp[-1] == ' ') /* wp can't == line */ + --wp; + *wp = EOS; + return(YES); +} diff --git a/libexec/bugfiler/pathnames.h b/libexec/bugfiler/pathnames.h new file mode 100644 index 0000000..7ff5ab6 --- /dev/null +++ b/libexec/bugfiler/pathnames.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + */ + +#define MAIL_CMD "/usr/sbin/sendmail -i -t -F \"Bugs Bunny\" -f owner-bugs" +#undef _PATH_TMP +#define _PATH_TMP "/tmp/BUG_XXXXXX" diff --git a/libexec/bugfiler/process.c b/libexec/bugfiler/process.c new file mode 100644 index 0000000..6d376fd --- /dev/null +++ b/libexec/bugfiler/process.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 1986, 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)process.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> + +#include <ctype.h> +#include <dirent.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "bug.h" +#include "extern.h" + +char pfile[MAXPATHLEN]; /* permanent file name */ + +static int getnext __P((void)); + +/* + * process -- + * copy report to permanent file, + * update summary file. + */ +int +process() +{ + register int rval; /* read return value */ + struct timeval tp; /* time of day */ + int lfd; /* lock file descriptor */ + + if (access(LOCK_FILE, R_OK) || (lfd = open(LOCK_FILE, O_RDONLY, 0)) < 0) + error("can't find lock file %s.", LOCK_FILE); + if (flock(lfd, LOCK_EX)) + error("can't get lock.", CHN); + sprintf(pfile, "%s/%s/%d", dir, folder, getnext()); + fprintf(stderr, "\t%s\n", pfile); + if (!(freopen(pfile, "w", stdout))) + error("can't create %s.", pfile); + rewind(stdin); + while ((rval = read(fileno(stdin), bfr, sizeof(bfr))) != ERR && rval) + if (write(fileno(stdout), bfr, rval) != rval) + error("write to %s failed.", pfile); + + /* append information to the summary file */ + sprintf(bfr, "%s/%s", dir, SUMMARY_FILE); + if (!(freopen(bfr, "a", stdout))) + error("can't append to summary file %s.", bfr); + if (gettimeofday(&tp, (struct timezone *)NULL)) + error("can't get time of day.", CHN); + printf("\n%s\t\t%s\t%s\t%s\tOwner: Bugs Bunny\n\tStatus: Received\n", + pfile, ctime(&tp.tv_sec), mailhead[INDX_TAG].line, + mailhead[SUBJ_TAG].found ? mailhead[SUBJ_TAG].line : "Subject:\n"); + (void)flock(lfd, LOCK_UN); + (void)fclose(stdout); +} + +/* + * getnext -- + * get next file name (number) + */ +static int +getnext() +{ + register struct dirent *d; /* directory structure */ + register DIR *dirp; /* directory pointer */ + register int highval, newval; + register char *p; + + (void)sprintf(bfr, "%s/%s", dir, folder); + if (!(dirp = opendir(bfr))) + error("can't read folder directory %s.", bfr); + for (highval = -1; d = readdir(dirp);) { + for (p = d->d_name; *p && isdigit(*p); ++p); + if (!*p && (newval = atoi(d->d_name)) > highval) + highval = newval; + } + closedir(dirp); + return(++highval); +} diff --git a/libexec/bugfiler/redist.c b/libexec/bugfiler/redist.c new file mode 100644 index 0000000..87a18b6 --- /dev/null +++ b/libexec/bugfiler/redist.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 1986, 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)redist.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> + +#include <ctype.h> +#include <dirent.h> +#include <stdio.h> +#include <string.h> + +#include "bug.h" +#include "pathnames.h" +#include "extern.h" + +/* + * redist -- + * Redistribute a bug report to those people indicated in the + * redistribution list file. + */ +void +redist() +{ + extern FILE *dfp; /* dist file fp */ + extern char pfile[]; /* permanent bug file */ + register char *C1, *C2; + FILE *pf; + int group; + + (void)sprintf(bfr, "%s/%s", dir, DIST_FILE); + if (!freopen(bfr, "r", stdin)) + return; + for (pf = NULL, group = 0; fgets(bfr, sizeof(bfr), stdin);) { + if (C1 = strchr(bfr, '\n')) + *C1 = '\0'; +nextline: if (*bfr == COMMENT || + isspace(*bfr) || !(C1 = index(bfr, ':'))) + continue; + *C1 = EOS; + if (!strcmp(bfr, folder) || !strcmp(bfr, "all")) { + for (++C1; *C1 && (*C1 == ' ' || *C1 == '\t'); ++C1); + if (!*C1) /* if empty list */ + continue; + if (!pf) { + if (!(pf = popen(MAIL_CMD, "w"))) + error("sendmail pipe failed.", CHN); + if (mailhead[SUBJ_TAG].found) + fprintf(pf, + "%s", mailhead[SUBJ_TAG].line); + else + fprintf(pf, + "Subject: Untitled Bug Report\n"); + if (!mailhead[TO_TAG].line) { + if (mailhead[APPAR_TO_TAG].line) + fprintf(pf, "To%s", + strchr(mailhead[APPAR_TO_TAG].line, + ':')); + else + fprintf(pf, "To: %s\n", BUGS_ID); + } + fputs("Resent-To: ", pf); + } + /* + * write out first entry, then succeeding entries + * backward compatible, handles back slashes at end + * of line + */ + if (group++) + fputs(", ", pf); + for (;;) { + if (C2 = strchr(C1, '\\')) + *C2 = EOS; + fputs(C1, pf); + if (!fgets(bfr, sizeof(bfr), stdin)) + break; + if (C1 = strchr(bfr, '\n')) + *C1 = '\0'; + if (*bfr != ' ' && *bfr != '\t') + goto nextline; + for (C1 = bfr; + *C1 && (*C1 == ' ' || *C1 == '\t'); ++C1); + } + } + } + if (!pf) + return; + + putc('\n', pf); + + rewind(dfp); + /* add Reference header and copy bug report out */ + while (fgets(bfr, sizeof(bfr), dfp) && *bfr != '\n') + fputs(bfr, pf); + fprintf(pf, "\n%sReference: %s\n\n", mailhead[INDX_TAG].line, pfile); + while (fgets(bfr, sizeof(bfr), dfp)) + fputs(bfr, pf); + (void)pclose(pf); +} diff --git a/libexec/bugfiler/reply.c b/libexec/bugfiler/reply.c new file mode 100644 index 0000000..0f99401 --- /dev/null +++ b/libexec/bugfiler/reply.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 1986, 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)reply.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> + +#include <dirent.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "bug.h" +#include "extern.h" +#include "pathnames.h" + +/* + * reply -- + * tell the user we got their silly little bug report + */ +void +reply() +{ + register char *C, /* traveling pointer */ + *to; /* who we're replying to */ + register int afd, /* ack file descriptor */ + rval; /* return value */ + FILE *pf; /* pipe pointer */ + + if (mailhead[RPLY_TAG].found) { + for (C = mailhead[RPLY_TAG].line + mailhead[RPLY_TAG].len; + *C != '\n' && (*C == ' ' || *C == '\t');++C); + if (*C) + goto gotone; + } + if (mailhead[FROM_TAG].found) { + for (C = mailhead[FROM_TAG].line + mailhead[FROM_TAG].len; + *C != '\n' && (*C == ' ' || *C == '\t');++C); + if (*C) + goto gotone; + } + if (mailhead[CFROM_TAG].found) { + for (C = mailhead[CFROM_TAG].line + mailhead[CFROM_TAG].len; + *C != '\n' && (*C == ' ' || *C == '\t');++C); + if (*C) + goto gotone; + } + return; + + /* if it's a foo <XXX>, get the XXX, else get foo (first string) */ +gotone: if (to = strchr(C, '<')) + for (C = ++to; + *C != '\n' && *C != ' ' && *C != '\t' && *C != '>';++C); + else { + to = C; + for (to = C++;*C != '\n' && *C != ' ' && *C != '\t';++C); + } + *C = EOS; + + if (!(pf = popen(MAIL_CMD, "w"))) + error("sendmail pipe failed.", CHN); + + fprintf(pf, "Reply-To: %s\nFrom: %s (Bugs Bunny)\nTo: %s\n", + BUGS_HOME, BUGS_HOME, to); + if (mailhead[SUBJ_TAG].found) + fprintf(pf, "Subject: Re:%s", + mailhead[SUBJ_TAG].line + mailhead[SUBJ_TAG].len); + else + fputs("Subject: Bug report acknowledgement.\n", pf); + if (mailhead[DATE_TAG].found) + fprintf(pf, "In-Acknowledgement-Of: Your message of %s", + mailhead[DATE_TAG].line + mailhead[DATE_TAG].len); + if (mailhead[MSG_TAG].found) + fprintf(pf, "\t\t%s", mailhead[MSG_TAG].line); + fputs("Precedence: bulk\n\n", pf); /* vacation(1) uses this... */ + fflush(pf); + + (void)sprintf(bfr, "%s/%s", dir, ACK_FILE); + if ((afd = open(bfr, O_RDONLY, 0)) >= 0) { + while ((rval = read(afd, bfr, sizeof(bfr))) != ERR && rval) + (void)write(fileno(pf), bfr, rval); + (void)close(afd); + } + pclose(pf); +} diff --git a/libexec/bugfiler/sendbug.1 b/libexec/bugfiler/sendbug.1 new file mode 100644 index 0000000..31ca802 --- /dev/null +++ b/libexec/bugfiler/sendbug.1 @@ -0,0 +1,85 @@ +.\" Copyright (c) 1983, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)sendbug.1 8.1 (Berkeley) 6/4/93 +.\" +.Dd June 4, 1993 +.Dt SENDBUG 1 +.Os BSD 4.2 +.Sh NAME +.Nm sendbug +.Nd mail a system bug report to 4bsd-bugs +.Sh SYNOPSIS +.Nm sendbug +.Op Ar address +.Sh DESCRIPTION +Bug reports sent to `4bsd-bugs@Berkeley.EDU' are intercepted +by a program which expects bug reports to conform to a standard format. +.Nm Sendbug +is a shell script to help the user compose and mail bug reports +in the correct format. +.Nm Sendbug +works by invoking the editor specified by the environment variable +.Ev EDITOR +on a temporary copy of the bug report format outline. The user must fill in the +appropriate fields and exit the editor. +.Nm Sendbug +then mails the completed report to `4bsd-bugs@Berkeley.EDU' or the +.Ar address +specified on the command line. +.Sh ENVIRONMENT +.Nm Sendbug +will utilize the following environment variable if it exists: +.Bl -tag -width EDITOR +.It Ev EDITOR +Specifies the preferred editor. If +.Ev EDITOR +is not set, +.Nm +defaults to +.Xr vi 1 . +.El +.Sh FILES +.Bl -tag -width /usr/share/misc/bugformat -compact +.It Pa /usr/share/misc/bugformat +Contains the bug report outline. +.El +.Sh SEE ALSO +.Xr vi 1 , +.Xr environ 7 , +.Xr bugfiler 8 , +.Xr sendmail 8 +.Sh HISTORY +The +.Nm sendbug +command +appeared in +.Bx 4.2 . diff --git a/libexec/bugfiler/sendbug.sh b/libexec/bugfiler/sendbug.sh new file mode 100644 index 0000000..911ef9d --- /dev/null +++ b/libexec/bugfiler/sendbug.sh @@ -0,0 +1,66 @@ +#!/bin/sh - +# +# Copyright (c) 1983, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)sendbug.sh 8.1 (Berkeley) 6/4/93 +# + +# create a bug report and mail it to '4bsd-bugs'. + +PATH=/bin:/sbin:/usr/sbin:/usr/bin +export PATH + +TEMP=/tmp/bug$$ +FORMAT=/usr/share/misc/bugformat + +# uucp sites should use ": ${BUGADDR=ucbvax!4bsd-bugs}" with a suitable path. +: ${BUGADDR=4bsd-bugs@CS.Berkeley.EDU} +: ${EDITOR=vi} + +trap 'rm -f $TEMP ; exit 1' 1 2 3 13 15 + +cp $FORMAT $TEMP +chmod u+w $TEMP +if $EDITOR $TEMP +then + if cmp -s $FORMAT $TEMP + then + echo "File not changed, no bug report submitted." + exit + fi + case "$#" in + 0) sendmail -t -oi $BUGADDR < $TEMP ;; + *) sendmail -t -oi "$@" < $TEMP ;; + esac +fi + +rm -f $TEMP diff --git a/libexec/comsat/Makefile b/libexec/comsat/Makefile new file mode 100644 index 0000000..14e793d --- /dev/null +++ b/libexec/comsat/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= comsat +MAN8= comsat.8 + +.include <bsd.prog.mk> diff --git a/libexec/comsat/comsat.8 b/libexec/comsat/comsat.8 new file mode 100644 index 0000000..2608676 --- /dev/null +++ b/libexec/comsat/comsat.8 @@ -0,0 +1,100 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)comsat.8 8.1 (Berkeley) 6/4/93 +.\" +.Dd June 4, 1993 +.Dt COMSAT 8 +.Os BSD 4.2 +.Sh NAME +.Nm comsat +.Nd biff server +.Sh SYNOPSIS +.Nm comsat +.Sh DESCRIPTION +.Nm Comsat +is the server process which receives reports of incoming mail +and notifies users if they have requested this service. +.Nm Comsat +receives messages on a datagram port associated with the +.Dq biff +service +specification (see +.Xr services 5 +and +.Xr inetd 8 ) . +The one line messages are of the form: +.Pp +.Dl user@mailbox-offset[:mailbox-name] +.Pp +If the +.Em user +specified is logged in to the system and the associated terminal has +the owner execute bit turned on (by a +.Dq Li biff y ) , +the +.Em offset +is used as a seek offset into the appropriate mailbox file and +the first 7 lines or 560 characters of the message are printed +on the user's terminal. Lines which appear to be part of +the message header other than the +.Dq From , +.Dq \&To , +.Dq Date , +or +.Dq Subject +lines are not included in the displayed message. +.Pp +If mailbox-name omitted, standard mailbox assumed. +.Sh FILES +.Bl -tag -width /var/mail/user -compact +.It Pa /var/run/utmp +to find out who's logged on and on what terminals +.It Pa /var/mail/user +standard mailbox +.El +.Sh SEE ALSO +.Xr biff 1 , +.Xr inetd 8 +.Sh BUGS +The message header filtering is prone to error. +The density of the information presented is near the theoretical minimum. +.Pp +Users should be notified of mail which arrives on other +machines than the one to which they are currently logged in. +.Pp +The notification should appear in a separate window so it +does not mess up the screen. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/libexec/comsat/comsat.c b/libexec/comsat/comsat.c new file mode 100644 index 0000000..f48c57c --- /dev/null +++ b/libexec/comsat/comsat.c @@ -0,0 +1,311 @@ +/* + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)comsat.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/wait.h> + +#include <netinet/in.h> + +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <paths.h> +#include <pwd.h> +#include <sgtty.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <utmp.h> + +int debug = 0; +#define dsyslog if (debug) syslog + +#define MAXIDLE 120 + +char hostname[MAXHOSTNAMELEN]; +struct utmp *utmp = NULL; +time_t lastmsgtime; +int nutmp, uf; + +void jkfprintf __P((FILE *, char[], off_t)); +void mailfor __P((char *)); +void notify __P((struct utmp *, char[], off_t, int)); +void onalrm __P((int)); +void reapchildren __P((int)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct sockaddr_in from; + register int cc; + int fromlen; + char msgbuf[256]; + + /* verify proper invocation */ + fromlen = sizeof(from); + if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { + (void)fprintf(stderr, + "comsat: getsockname: %s.\n", strerror(errno)); + exit(1); + } + openlog("comsat", LOG_PID, LOG_DAEMON); + if (chdir(_PATH_MAILDIR)) { + syslog(LOG_ERR, "chdir: %s: %m", _PATH_MAILDIR); + (void) recv(0, msgbuf, sizeof(msgbuf) - 1, 0); + exit(1); + } + if ((uf = open(_PATH_UTMP, O_RDONLY, 0)) < 0) { + syslog(LOG_ERR, "open: %s: %m", _PATH_UTMP); + (void) recv(0, msgbuf, sizeof(msgbuf) - 1, 0); + exit(1); + } + (void)time(&lastmsgtime); + (void)gethostname(hostname, sizeof(hostname)); + onalrm(0); + (void)signal(SIGALRM, onalrm); + (void)signal(SIGTTOU, SIG_IGN); + (void)signal(SIGCHLD, reapchildren); + for (;;) { + cc = recv(0, msgbuf, sizeof(msgbuf) - 1, 0); + if (cc <= 0) { + if (errno != EINTR) + sleep(1); + errno = 0; + continue; + } + if (!nutmp) /* no one has logged in yet */ + continue; + sigblock(sigmask(SIGALRM)); + msgbuf[cc] = '\0'; + (void)time(&lastmsgtime); + mailfor(msgbuf); + sigsetmask(0L); + } +} + +void +reapchildren(signo) + int signo; +{ + while (wait3(NULL, WNOHANG, NULL) > 0); +} + +void +onalrm(signo) + int signo; +{ + static u_int utmpsize; /* last malloced size for utmp */ + static u_int utmpmtime; /* last modification time for utmp */ + struct stat statbf; + + if (time(NULL) - lastmsgtime >= MAXIDLE) + exit(0); + (void)alarm((u_int)15); + (void)fstat(uf, &statbf); + if (statbf.st_mtime > utmpmtime) { + utmpmtime = statbf.st_mtime; + if (statbf.st_size > utmpsize) { + utmpsize = statbf.st_size + 10 * sizeof(struct utmp); + if ((utmp = realloc(utmp, utmpsize)) == NULL) { + syslog(LOG_ERR, "%s", strerror(errno)); + exit(1); + } + } + (void)lseek(uf, (off_t)0, L_SET); + nutmp = read(uf, utmp, (int)statbf.st_size)/sizeof(struct utmp); + } +} + +void +mailfor(name) + char *name; +{ + register struct utmp *utp = &utmp[nutmp]; + register char *cp; + char *file; + off_t offset; + int folder; + char buf[sizeof(_PATH_MAILDIR) + sizeof(utmp[0].ut_name) + 1]; + char buf2[sizeof(_PATH_MAILDIR) + sizeof(utmp[0].ut_name) + 1]; + + if (!(cp = strchr(name, '@'))) + return; + *cp = '\0'; + offset = atoi(cp + 1); + if (!(cp = strchr(cp + 1, ':'))) + file = name; + else + file = cp + 1; + sprintf(buf, "%s/%.*s", _PATH_MAILDIR, sizeof(utmp[0].ut_name), name); + if (*file != '/') { + sprintf(buf2, "%s/%.*s", _PATH_MAILDIR, sizeof(utmp[0].ut_name), file); + file = buf2; + } + folder = strcmp(buf, file); + while (--utp >= utmp) + if (!strncmp(utp->ut_name, name, sizeof(utmp[0].ut_name))) + notify(utp, file, offset, folder); +} + +static char *cr; + +void +notify(utp, file, offset, folder) + register struct utmp *utp; + char file[]; + off_t offset; + int folder; +{ + FILE *tp; + struct stat stb; + struct sgttyb gttybuf; + char tty[20], name[sizeof(utmp[0].ut_name) + 1]; + + (void)snprintf(tty, sizeof(tty), "%s%.*s", + _PATH_DEV, (int)sizeof(utp->ut_line), utp->ut_line); + if (strchr(tty + sizeof(_PATH_DEV) - 1, '/')) { + /* A slash is an attempt to break security... */ + syslog(LOG_AUTH | LOG_NOTICE, "'/' in \"%s\"", tty); + return; + } + if (stat(tty, &stb) || !(stb.st_mode & S_IEXEC)) { + dsyslog(LOG_DEBUG, "%s: wrong mode on %s", utp->ut_name, tty); + return; + } + dsyslog(LOG_DEBUG, "notify %s on %s\n", utp->ut_name, tty); + if (fork()) + return; + (void)signal(SIGALRM, SIG_DFL); + (void)alarm((u_int)30); + if ((tp = fopen(tty, "w")) == NULL) { + dsyslog(LOG_ERR, "%s: %s", tty, strerror(errno)); + _exit(-1); + } + (void)ioctl(fileno(tp), TIOCGETP, >tybuf); + cr = (gttybuf.sg_flags&CRMOD) && !(gttybuf.sg_flags&RAW) ? + "\n" : "\n\r"; + (void)strncpy(name, utp->ut_name, sizeof(utp->ut_name)); + name[sizeof(name) - 1] = '\0'; + (void)fprintf(tp, "%s\007New mail for %s@%.*s\007 has arrived%s%s%s:%s----%s", + cr, name, (int)sizeof(hostname), hostname, + folder ? cr : "", folder ? "to " : "", folder ? file : "", + cr, cr); + jkfprintf(tp, file, offset); + (void)fclose(tp); + _exit(0); +} + +void +jkfprintf(tp, name, offset) + register FILE *tp; + char name[]; + off_t offset; +{ + register char *cp, ch; + register FILE *fi; + register int linecnt, charcnt, inheader; + register struct passwd *p; + char line[BUFSIZ]; + + /* Set effective uid to user in case mail drop is on nfs */ + if ((p = getpwnam(name)) != NULL) + (void) setuid(p->pw_uid); + + if ((fi = fopen(name, "r")) == NULL) + return; + + (void)fseek(fi, offset, L_SET); + /* + * Print the first 7 lines or 560 characters of the new mail + * (whichever comes first). Skip header crap other than + * From, Subject, To, and Date. + */ + linecnt = 7; + charcnt = 560; + inheader = 1; + while (fgets(line, sizeof(line), fi) != NULL) { + if (inheader) { + if (line[0] == '\n') { + inheader = 0; + continue; + } + if (line[0] == ' ' || line[0] == '\t' || + strncmp(line, "From:", 5) && + strncmp(line, "Subject:", 8)) + continue; + } + if (linecnt <= 0 || charcnt <= 0) { + (void)fprintf(tp, "...more...%s", cr); + (void)fclose(fi); + return; + } + /* strip weird stuff so can't trojan horse stupid terminals */ + for (cp = line; (ch = *cp) && ch != '\n'; ++cp, --charcnt) { + if (!isprint(ch)) { + if (ch & 0x80) + (void)fputs("M-", tp); + ch &= 0177; + if (!isprint(ch)) { + if (ch == 0177) + ch = '?'; + else + ch |= 0x40; + (void)fputc('^', tp); + } + } + (void)fputc(ch, tp); + } + (void)fputs(cr, tp); + --linecnt; + } + (void)fprintf(tp, "----%s\n", cr); + (void)fclose(fi); +} diff --git a/libexec/fingerd/Makefile b/libexec/fingerd/Makefile new file mode 100644 index 0000000..bd39992 --- /dev/null +++ b/libexec/fingerd/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= fingerd +MAN8= fingerd.8 + +.include <bsd.prog.mk> diff --git a/libexec/fingerd/fingerd.8 b/libexec/fingerd/fingerd.8 new file mode 100644 index 0000000..c5c0762 --- /dev/null +++ b/libexec/fingerd/fingerd.8 @@ -0,0 +1,139 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)fingerd.8 8.1 (Berkeley) 6/4/93 +.\" +.Dd June 4, 1993 +.Dt FINGERD 8 +.Os BSD 4.3 +.Sh NAME +.Nm fingerd +.Nd remote user information server +.Sh SYNOPSIS +.Nm fingerd +.Op Fl s +.Op Fl l +.Op Fl p Ar filename +.Sh DESCRIPTION +.Nm Fingerd +is a simple protocol based on +.%T RFC1196 +that provides an interface to the +Name and Finger programs at several network sites. +The program is supposed to return a friendly, +human-oriented status report on either the system at the moment +or a particular person in depth. +There is no required format and the +protocol consists mostly of specifying a single +.Dq command line . +.Pp +.Nm Fingerd +listens for +.Tn TCP +requests at port 79. +Once connected it reads a single command line +terminated by a +.Aq Tn CRLF +which is passed to +.Xr finger 1 . +.Nm Fingerd +closes its connections as soon as the output is finished. +.Pp +If the line is null (i.e. just a +.Aq Tn CRLF +is sent) then +.Xr finger +returns a +.Dq default +report that lists all people logged into +the system at that moment. +.Pp +If a user name is specified (e.g. +.Pf eric Aq Tn CRLF ) +then the +response lists more extended information for only that particular user, +whether logged in or not. +Allowable +.Dq names +in the command line include both +.Dq login names +and +.Dq user names . +If a name is ambiguous, all possible derivations are returned. +.Pp +The following options may be passed to +.Nm fingerd +as server program arguments in +.Pa /etc/inetd.conf : +.Bl -tag -width Ds +.It Fl s +Enable secure mode. +Queries without a user name are rejected and +forwarding of queries to other remote hosts is denied. +.It Fl l +Enable logging. +The name of the host originating the query is reported via +.Xr syslog 3 +at LOG_NOTICE priority. +.It Fl p +Use an alternate program as the local information provider. +The default local program +executed by +.Nm fingerd +is +.Xr finger 1 . +By specifying a customized local server, +this option allows a system manager +to have more control over what information is +provided to remote sites. +.El +.Sh SEE ALSO +.Xr finger 1 +.Sh BUGS +Connecting directly to the server from a +.Tn TIP +or an equally narrow-minded +.Tn TELNET Ns \-protocol +user program can result +in meaningless attempts at option negotiation being sent to the +server, which will foul up the command line interpretation. +.Nm Fingerd +should be taught to filter out +.Tn IAC Ns \'s +and perhaps even respond +negatively +.Pq Tn IAC WON'T +to all option commands received. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/libexec/fingerd/fingerd.c b/libexec/fingerd/fingerd.c new file mode 100644 index 0000000..455d6e9 --- /dev/null +++ b/libexec/fingerd/fingerd.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +/* +static char sccsid[] = "@(#)fingerd.c 8.1 (Berkeley) 6/4/93"; +*/ +static const char rcsid[] = + "$Id$"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <errno.h> + +#include <unistd.h> +#include <syslog.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include "pathnames.h" + +void logerr __P((const char *, ...)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register FILE *fp; + register int ch; + register char *lp; + struct hostent *hp; + struct sockaddr_in sin; + int p[2], logging, secure, sval; +#define ENTRIES 50 + char **ap, *av[ENTRIES + 1], **comp, line[1024], *prog; + + prog = _PATH_FINGER; + logging = secure = 0; + openlog("fingerd", LOG_PID | LOG_CONS, LOG_DAEMON); + opterr = 0; + while ((ch = getopt(argc, argv, "slp:")) != EOF) + switch (ch) { + case 'l': + logging = 1; + break; + case 'p': + prog = optarg; + break; + case 's': + secure = 1; + break; + case '?': + default: + logerr("illegal option -- %c", ch); + } + + if (logging) { + sval = sizeof(sin); + if (getpeername(0, (struct sockaddr *)&sin, &sval) < 0) + logerr("getpeername: %s", strerror(errno)); + if (hp = gethostbyaddr((char *)&sin.sin_addr.s_addr, + sizeof(sin.sin_addr.s_addr), AF_INET)) + lp = hp->h_name; + else + lp = inet_ntoa(sin.sin_addr); + syslog(LOG_NOTICE, "query from %s", lp); + } + + /* + * Enable server-side Transaction TCP. + */ + { + int one = 1; + if (setsockopt(STDOUT_FILENO, IPPROTO_TCP, TCP_NOPUSH, &one, + sizeof one) < 0) { + logerr("setsockopt(TCP_NOPUSH) failed: %m"); + } + } + + if (!fgets(line, sizeof(line), stdin)) + exit(1); + + comp = &av[1]; + av[2] = "--"; + for (lp = line, ap = &av[3];;) { + *ap = strtok(lp, " \t\r\n"); + if (!*ap) { + if (secure && ap == &av[3]) { + puts("must provide username\r\n"); + exit(1); + } + break; + } + if (secure && strchr(*ap, '@')) { + puts("forwarding service denied\r\n"); + exit(1); + } + + /* RFC742: "/[Ww]" == "-l" */ + if ((*ap)[0] == '/' && ((*ap)[1] == 'W' || (*ap)[1] == 'w')) { + av[1] = "-l"; + comp = &av[0]; + } + else if (++ap == av + ENTRIES) + break; + lp = NULL; + } + + if (lp = strrchr(prog, '/')) + *comp = ++lp; + else + *comp = prog; + if (pipe(p) < 0) + logerr("pipe: %s", strerror(errno)); + + switch(vfork()) { + case 0: + (void)close(p[0]); + if (p[1] != 1) { + (void)dup2(p[1], 1); + (void)close(p[1]); + } + execv(prog, comp); + logerr("execv: %s: %s", prog, strerror(errno)); + _exit(1); + case -1: + logerr("fork: %s", strerror(errno)); + } + (void)close(p[1]); + if (!(fp = fdopen(p[0], "r"))) + logerr("fdopen: %s", strerror(errno)); + while ((ch = getc(fp)) != EOF) { + if (ch == '\n') + putchar('\r'); + putchar(ch); + } + exit(0); +} + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +logerr(const char *fmt, ...) +#else +logerr(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)vsyslog(LOG_ERR, fmt, ap); + va_end(ap); + exit(1); + /* NOTREACHED */ +} diff --git a/libexec/fingerd/pathnames.h b/libexec/fingerd/pathnames.h new file mode 100644 index 0000000..864a8f6 --- /dev/null +++ b/libexec/fingerd/pathnames.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + */ + +#define _PATH_FINGER "/usr/bin/finger" diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile new file mode 100644 index 0000000..a218380 --- /dev/null +++ b/libexec/ftpd/Makefile @@ -0,0 +1,15 @@ +# @(#)Makefile 8.2 (Berkeley) 4/4/94 + +PROG= ftpd +MAN8= ftpd.8 +SRCS= ftpd.c ftpcmd.c logwtmp.c popen.c skey-stuff.c + +CFLAGS+=-DSETPROCTITLE -DSKEY -DSTATS +CFLAGS+=-DFTP_DATA_BOTTOM=40000 -DFTP_DATA_TOP=44999 + +LDADD= -lskey -lmd -lcrypt +DPADD= ${LIBSKEY} ${LIBMD} ${LIBCRYPT} + +CLEANFILES+=ftpcmd.c y.tab.h + +.include <bsd.prog.mk> diff --git a/libexec/ftpd/extern.h b/libexec/ftpd/extern.h new file mode 100644 index 0000000..e3336b5 --- /dev/null +++ b/libexec/ftpd/extern.h @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.2 (Berkeley) 4/4/94 + */ + +void blkfree __P((char **)); +char **copyblk __P((char **)); +void cwd __P((char *)); +void delete __P((char *)); +void dologout __P((int)); +void fatal __P((char *)); +int ftpd_pclose __P((FILE *)); +FILE *ftpd_popen __P((char *, char *)); +char *getline __P((char *, int, FILE *)); +void logwtmp __P((char *, char *, char *)); +void lreply __P((int, const char *, ...)); +void makedir __P((char *)); +void nack __P((char *)); +void pass __P((char *)); +void passive __P((void)); +void perror_reply __P((int, char *)); +void pwd __P((void)); +void removedir __P((char *)); +void renamecmd __P((char *, char *)); +char *renamefrom __P((char *)); +void reply __P((int, const char *, ...)); +void retrieve __P((char *, char *)); +void send_file_list __P((char *)); +void setproctitle __P((const char *, ...)); +void statcmd __P((void)); +void statfilecmd __P((char *)); +void store __P((char *, char *, int)); +void upper __P((char *)); +void user __P((char *)); +void yyerror __P((char *)); diff --git a/libexec/ftpd/ftpcmd.y b/libexec/ftpd/ftpcmd.y new file mode 100644 index 0000000..151c3d7 --- /dev/null +++ b/libexec/ftpd/ftpcmd.y @@ -0,0 +1,1266 @@ +/* + * Copyright (c) 1985, 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 + */ + +/* + * Grammar for FTP commands. + * See RFC 959. + */ + +%{ + +#ifndef lint +static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/stat.h> + +#include <netinet/in.h> +#include <arpa/ftp.h> + +#include <ctype.h> +#include <errno.h> +#include <glob.h> +#include <pwd.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#include "extern.h" + +extern struct sockaddr_in data_dest; +extern int logged_in; +extern struct passwd *pw; +extern int guest; +extern int logging; +extern int type; +extern int form; +extern int debug; +extern int timeout; +extern int maxtimeout; +extern int pdata; +extern char hostname[], remotehost[]; +extern char proctitle[]; +extern int usedefault; +extern int transflag; +extern char tmpline[]; + +off_t restart_point; + +static int cmd_type; +static int cmd_form; +static int cmd_bytesz; +char cbuf[512]; +char *fromname; + +%} + +%union { + int i; + char *s; +} + +%token + A B C E F I + L N P R S T + + SP CRLF COMMA + + USER PASS ACCT REIN QUIT PORT + PASV TYPE STRU MODE RETR STOR + APPE MLFL MAIL MSND MSOM MSAM + MRSQ MRCP ALLO REST RNFR RNTO + ABOR DELE CWD LIST NLST SITE + STAT HELP NOOP MKD RMD PWD + CDUP STOU SMNT SYST SIZE MDTM + + UMASK IDLE CHMOD + + LEXERR + +%token <s> STRING +%token <i> NUMBER + +%type <i> check_login octal_number byte_size +%type <i> struct_code mode_code type_code form_code +%type <s> pathstring pathname password username + +%start cmd_list + +%% + +cmd_list + : /* empty */ + | cmd_list cmd + { + fromname = (char *) 0; + restart_point = (off_t) 0; + } + | cmd_list rcmd + ; + +cmd + : USER SP username CRLF + { + user($3); + free($3); + } + | PASS SP password CRLF + { + pass($3); + free($3); + } + | PORT SP host_port CRLF + { + usedefault = 0; + if (pdata >= 0) { + (void) close(pdata); + pdata = -1; + } + reply(200, "PORT command successful."); + } + | PASV CRLF + { + passive(); + } + | TYPE SP type_code CRLF + { + switch (cmd_type) { + + case TYPE_A: + if (cmd_form == FORM_N) { + reply(200, "Type set to A."); + type = cmd_type; + form = cmd_form; + } else + reply(504, "Form must be N."); + break; + + case TYPE_E: + reply(504, "Type E not implemented."); + break; + + case TYPE_I: + reply(200, "Type set to I."); + type = cmd_type; + break; + + case TYPE_L: +#if NBBY == 8 + if (cmd_bytesz == 8) { + reply(200, + "Type set to L (byte size 8)."); + type = cmd_type; + } else + reply(504, "Byte size must be 8."); +#else /* NBBY == 8 */ + UNIMPLEMENTED for NBBY != 8 +#endif /* NBBY == 8 */ + } + } + | STRU SP struct_code CRLF + { + switch ($3) { + + case STRU_F: + reply(200, "STRU F ok."); + break; + + default: + reply(504, "Unimplemented STRU type."); + } + } + | MODE SP mode_code CRLF + { + switch ($3) { + + case MODE_S: + reply(200, "MODE S ok."); + break; + + default: + reply(502, "Unimplemented MODE type."); + } + } + | ALLO SP NUMBER CRLF + { + reply(202, "ALLO command ignored."); + } + | ALLO SP NUMBER SP R SP NUMBER CRLF + { + reply(202, "ALLO command ignored."); + } + | RETR check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + retrieve((char *) 0, $4); + if ($4 != NULL) + free($4); + } + | STOR check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + store($4, "w", 0); + if ($4 != NULL) + free($4); + } + | APPE check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + store($4, "a", 0); + if ($4 != NULL) + free($4); + } + | NLST check_login CRLF + { + if ($2) + send_file_list("."); + } + | NLST check_login SP STRING CRLF + { + if ($2 && $4 != NULL) + send_file_list($4); + if ($4 != NULL) + free($4); + } + | LIST check_login CRLF + { + if ($2) + retrieve("/bin/ls -lgA", ""); + } + | LIST check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + retrieve("/bin/ls -lgA %s", $4); + if ($4 != NULL) + free($4); + } + | STAT check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + statfilecmd($4); + if ($4 != NULL) + free($4); + } + | STAT CRLF + { + statcmd(); + } + | DELE check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + delete($4); + if ($4 != NULL) + free($4); + } + | RNTO SP pathname CRLF + { + if (fromname) { + renamecmd(fromname, $3); + free(fromname); + fromname = (char *) 0; + } else { + reply(503, "Bad sequence of commands."); + } + free($3); + } + | ABOR CRLF + { + reply(225, "ABOR command successful."); + } + | CWD check_login CRLF + { + if ($2) + cwd(pw->pw_dir); + } + | CWD check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + cwd($4); + if ($4 != NULL) + free($4); + } + | HELP CRLF + { + help(cmdtab, (char *) 0); + } + | HELP SP STRING CRLF + { + char *cp = $3; + + if (strncasecmp(cp, "SITE", 4) == 0) { + cp = $3 + 4; + if (*cp == ' ') + cp++; + if (*cp) + help(sitetab, cp); + else + help(sitetab, (char *) 0); + } else + help(cmdtab, $3); + } + | NOOP CRLF + { + reply(200, "NOOP command successful."); + } + | MKD check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + makedir($4); + if ($4 != NULL) + free($4); + } + | RMD check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + removedir($4); + if ($4 != NULL) + free($4); + } + | PWD check_login CRLF + { + if ($2) + pwd(); + } + | CDUP check_login CRLF + { + if ($2) + cwd(".."); + } + | SITE SP HELP CRLF + { + help(sitetab, (char *) 0); + } + | SITE SP HELP SP STRING CRLF + { + help(sitetab, $5); + } + | SITE SP UMASK check_login CRLF + { + int oldmask; + + if ($4) { + oldmask = umask(0); + (void) umask(oldmask); + reply(200, "Current UMASK is %03o", oldmask); + } + } + | SITE SP UMASK check_login SP octal_number CRLF + { + int oldmask; + + if ($4) { + if (($6 == -1) || ($6 > 0777)) { + reply(501, "Bad UMASK value"); + } else { + oldmask = umask($6); + reply(200, + "UMASK set to %03o (was %03o)", + $6, oldmask); + } + } + } + | SITE SP CHMOD check_login SP octal_number SP pathname CRLF + { + if ($4 && ($8 != NULL)) { + if ($6 > 0777) + reply(501, + "CHMOD: Mode value must be between 0 and 0777"); + else if (chmod($8, $6) < 0) + perror_reply(550, $8); + else + reply(200, "CHMOD command successful."); + } + if ($8 != NULL) + free($8); + } + | SITE SP IDLE CRLF + { + reply(200, + "Current IDLE time limit is %d seconds; max %d", + timeout, maxtimeout); + } + | SITE SP IDLE SP NUMBER CRLF + { + if ($5 < 30 || $5 > maxtimeout) { + reply(501, + "Maximum IDLE time must be between 30 and %d seconds", + maxtimeout); + } else { + timeout = $5; + (void) alarm((unsigned) timeout); + reply(200, + "Maximum IDLE time set to %d seconds", + timeout); + } + } + | STOU check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + store($4, "w", 1); + if ($4 != NULL) + free($4); + } + | SYST CRLF + { +#ifdef unix +#ifdef BSD + reply(215, "UNIX Type: L%d Version: BSD-%d", + NBBY, BSD); +#else /* BSD */ + reply(215, "UNIX Type: L%d", NBBY); +#endif /* BSD */ +#else /* unix */ + reply(215, "UNKNOWN Type: L%d", NBBY); +#endif /* unix */ + } + + /* + * SIZE is not in RFC959, but Postel has blessed it and + * it will be in the updated RFC. + * + * Return size of file in a format suitable for + * using with RESTART (we just count bytes). + */ + | SIZE check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + sizecmd($4); + if ($4 != NULL) + free($4); + } + + /* + * MDTM is not in RFC959, but Postel has blessed it and + * it will be in the updated RFC. + * + * Return modification time of file as an ISO 3307 + * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx + * where xxx is the fractional second (of any precision, + * not necessarily 3 digits) + */ + | MDTM check_login SP pathname CRLF + { + if ($2 && $4 != NULL) { + struct stat stbuf; + if (stat($4, &stbuf) < 0) + reply(550, "%s: %s", + $4, strerror(errno)); + else if (!S_ISREG(stbuf.st_mode)) { + reply(550, "%s: not a plain file.", $4); + } else { + struct tm *t; + t = gmtime(&stbuf.st_mtime); + reply(213, + "19%02d%02d%02d%02d%02d%02d", + t->tm_year, t->tm_mon+1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + } + } + if ($4 != NULL) + free($4); + } + | QUIT CRLF + { + reply(221, "Goodbye."); + dologout(0); + } + | error CRLF + { + yyerrok; + } + ; +rcmd + : RNFR check_login SP pathname CRLF + { + char *renamefrom(); + + restart_point = (off_t) 0; + if ($2 && $4) { + fromname = renamefrom($4); + if (fromname == (char *) 0 && $4) { + free($4); + } + } + } + | REST SP byte_size CRLF + { + fromname = (char *) 0; + restart_point = $3; /* XXX $3 is only "int" */ + reply(350, "Restarting at %qd. %s", restart_point, + "Send STORE or RETRIEVE to initiate transfer."); + } + ; + +username + : STRING + ; + +password + : /* empty */ + { + $$ = (char *)calloc(1, sizeof(char)); + } + | STRING + ; + +byte_size + : NUMBER + ; + +host_port + : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER + { + char *a, *p; + + a = (char *)&data_dest.sin_addr; + a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; + p = (char *)&data_dest.sin_port; + p[0] = $9; p[1] = $11; + data_dest.sin_family = AF_INET; + } + ; + +form_code + : N + { + $$ = FORM_N; + } + | T + { + $$ = FORM_T; + } + | C + { + $$ = FORM_C; + } + ; + +type_code + : A + { + cmd_type = TYPE_A; + cmd_form = FORM_N; + } + | A SP form_code + { + cmd_type = TYPE_A; + cmd_form = $3; + } + | E + { + cmd_type = TYPE_E; + cmd_form = FORM_N; + } + | E SP form_code + { + cmd_type = TYPE_E; + cmd_form = $3; + } + | I + { + cmd_type = TYPE_I; + } + | L + { + cmd_type = TYPE_L; + cmd_bytesz = NBBY; + } + | L SP byte_size + { + cmd_type = TYPE_L; + cmd_bytesz = $3; + } + /* this is for a bug in the BBN ftp */ + | L byte_size + { + cmd_type = TYPE_L; + cmd_bytesz = $2; + } + ; + +struct_code + : F + { + $$ = STRU_F; + } + | R + { + $$ = STRU_R; + } + | P + { + $$ = STRU_P; + } + ; + +mode_code + : S + { + $$ = MODE_S; + } + | B + { + $$ = MODE_B; + } + | C + { + $$ = MODE_C; + } + ; + +pathname + : pathstring + { + /* + * Problem: this production is used for all pathname + * processing, but only gives a 550 error reply. + * This is a valid reply in some cases but not in others. + */ + if (logged_in && $1 && *$1 == '~') { + glob_t gl; + int flags = + GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; + + memset(&gl, 0, sizeof(gl)); + if (glob($1, flags, NULL, &gl) || + gl.gl_pathc == 0) { + reply(550, "not found"); + $$ = NULL; + } else { + $$ = strdup(gl.gl_pathv[0]); + } + globfree(&gl); + free($1); + } else + $$ = $1; + } + ; + +pathstring + : STRING + ; + +octal_number + : NUMBER + { + int ret, dec, multby, digit; + + /* + * Convert a number that was read as decimal number + * to what it would be if it had been read as octal. + */ + dec = $1; + multby = 1; + ret = 0; + while (dec) { + digit = dec%10; + if (digit > 7) { + ret = -1; + break; + } + ret += digit * multby; + multby *= 8; + dec /= 10; + } + $$ = ret; + } + ; + + +check_login + : /* empty */ + { + if (logged_in) + $$ = 1; + else { + reply(530, "Please login with USER and PASS."); + $$ = 0; + } + } + ; + +%% + +extern jmp_buf errcatch; + +#define CMD 0 /* beginning of command */ +#define ARGS 1 /* expect miscellaneous arguments */ +#define STR1 2 /* expect SP followed by STRING */ +#define STR2 3 /* expect STRING */ +#define OSTR 4 /* optional SP then STRING */ +#define ZSTR1 5 /* SP then optional STRING */ +#define ZSTR2 6 /* optional STRING after SP */ +#define SITECMD 7 /* SITE command */ +#define NSTR 8 /* Number followed by a string */ + +struct tab { + char *name; + short token; + short state; + short implemented; /* 1 if command is implemented */ + char *help; +}; + +struct tab cmdtab[] = { /* In order defined in RFC 765 */ + { "USER", USER, STR1, 1, "<sp> username" }, + { "PASS", PASS, ZSTR1, 1, "<sp> password" }, + { "ACCT", ACCT, STR1, 0, "(specify account)" }, + { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, + { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, + { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, + { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, + { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, + { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, + { "STRU", STRU, ARGS, 1, "(specify file structure)" }, + { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, + { "RETR", RETR, STR1, 1, "<sp> file-name" }, + { "STOR", STOR, STR1, 1, "<sp> file-name" }, + { "APPE", APPE, STR1, 1, "<sp> file-name" }, + { "MLFL", MLFL, OSTR, 0, "(mail file)" }, + { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, + { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, + { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, + { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, + { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, + { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, + { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, + { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, + { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, + { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, + { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, + { "DELE", DELE, STR1, 1, "<sp> file-name" }, + { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, + { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, + { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, + { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, + { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, + { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, + { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, + { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, + { "NOOP", NOOP, ARGS, 1, "" }, + { "MKD", MKD, STR1, 1, "<sp> path-name" }, + { "XMKD", MKD, STR1, 1, "<sp> path-name" }, + { "RMD", RMD, STR1, 1, "<sp> path-name" }, + { "XRMD", RMD, STR1, 1, "<sp> path-name" }, + { "PWD", PWD, ARGS, 1, "(return current directory)" }, + { "XPWD", PWD, ARGS, 1, "(return current directory)" }, + { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, + { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, + { "STOU", STOU, STR1, 1, "<sp> file-name" }, + { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, + { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, + { NULL, 0, 0, 0, 0 } +}; + +struct tab sitetab[] = { + { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, + { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, + { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, + { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, + { NULL, 0, 0, 0, 0 } +}; + +static char *copy __P((char *)); +static void help __P((struct tab *, char *)); +static struct tab * + lookup __P((struct tab *, char *)); +static void sizecmd __P((char *)); +static void toolong __P((int)); +static int yylex __P((void)); + +static struct tab * +lookup(p, cmd) + struct tab *p; + char *cmd; +{ + + for (; p->name != NULL; p++) + if (strcmp(cmd, p->name) == 0) + return (p); + return (0); +} + +#include <arpa/telnet.h> + +/* + * getline - a hacked up version of fgets to ignore TELNET escape codes. + */ +char * +getline(s, n, iop) + char *s; + int n; + FILE *iop; +{ + int c; + register char *cs; + + cs = s; +/* tmpline may contain saved command from urgent mode interruption */ + for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { + *cs++ = tmpline[c]; + if (tmpline[c] == '\n') { + *cs++ = '\0'; + if (debug) + syslog(LOG_DEBUG, "command: %s", s); + tmpline[0] = '\0'; + return(s); + } + if (c == 0) + tmpline[0] = '\0'; + } + while ((c = getc(iop)) != EOF) { + c &= 0377; + if (c == IAC) { + if ((c = getc(iop)) != EOF) { + c &= 0377; + switch (c) { + case WILL: + case WONT: + c = getc(iop); + printf("%c%c%c", IAC, DONT, 0377&c); + (void) fflush(stdout); + continue; + case DO: + case DONT: + c = getc(iop); + printf("%c%c%c", IAC, WONT, 0377&c); + (void) fflush(stdout); + continue; + case IAC: + break; + default: + continue; /* ignore command */ + } + } + } + *cs++ = c; + if (--n <= 0 || c == '\n') + break; + } + if (c == EOF && cs == s) + return (NULL); + *cs++ = '\0'; + if (debug) { + if (!guest && strncasecmp("pass ", s, 5) == 0) { + /* Don't syslog passwords */ + syslog(LOG_DEBUG, "command: %.5s ???", s); + } else { + register char *cp; + register int len; + + /* Don't syslog trailing CR-LF */ + len = strlen(s); + cp = s + len - 1; + while (cp >= s && (*cp == '\n' || *cp == '\r')) { + --cp; + --len; + } + syslog(LOG_DEBUG, "command: %.*s", len, s); + } + } + return (s); +} + +static void +toolong(signo) + int signo; +{ + + reply(421, + "Timeout (%d seconds): closing control connection.", timeout); + if (logging) + syslog(LOG_INFO, "User %s timed out after %d seconds", + (pw ? pw -> pw_name : "unknown"), timeout); + dologout(1); +} + +static int +yylex() +{ + static int cpos, state; + char *cp, *cp2; + struct tab *p; + int n; + char c; + + for (;;) { + switch (state) { + + case CMD: + (void) signal(SIGALRM, toolong); + (void) alarm((unsigned) timeout); + if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { + reply(221, "You could at least say goodbye."); + dologout(0); + } + (void) alarm(0); +#ifdef SETPROCTITLE + if (strncasecmp(cbuf, "PASS", 4) != NULL) + setproctitle("%s: %s", proctitle, cbuf); +#endif /* SETPROCTITLE */ + if ((cp = strchr(cbuf, '\r'))) { + *cp++ = '\n'; + *cp = '\0'; + } + if ((cp = strpbrk(cbuf, " \n"))) + cpos = cp - cbuf; + if (cpos == 0) + cpos = 4; + c = cbuf[cpos]; + cbuf[cpos] = '\0'; + upper(cbuf); + p = lookup(cmdtab, cbuf); + cbuf[cpos] = c; + if (p != 0) { + if (p->implemented == 0) { + nack(p->name); + longjmp(errcatch,0); + /* NOTREACHED */ + } + state = p->state; + yylval.s = p->name; + return (p->token); + } + break; + + case SITECMD: + if (cbuf[cpos] == ' ') { + cpos++; + return (SP); + } + cp = &cbuf[cpos]; + if ((cp2 = strpbrk(cp, " \n"))) + cpos = cp2 - cbuf; + c = cbuf[cpos]; + cbuf[cpos] = '\0'; + upper(cp); + p = lookup(sitetab, cp); + cbuf[cpos] = c; + if (guest == 0 && p != 0) { + if (p->implemented == 0) { + state = CMD; + nack(p->name); + longjmp(errcatch,0); + /* NOTREACHED */ + } + state = p->state; + yylval.s = p->name; + return (p->token); + } + state = CMD; + break; + + case OSTR: + if (cbuf[cpos] == '\n') { + state = CMD; + return (CRLF); + } + /* FALLTHROUGH */ + + case STR1: + case ZSTR1: + dostr1: + if (cbuf[cpos] == ' ') { + cpos++; + state = state == OSTR ? STR2 : ++state; + return (SP); + } + break; + + case ZSTR2: + if (cbuf[cpos] == '\n') { + state = CMD; + return (CRLF); + } + /* FALLTHROUGH */ + + case STR2: + cp = &cbuf[cpos]; + n = strlen(cp); + cpos += n - 1; + /* + * Make sure the string is nonempty and \n terminated. + */ + if (n > 1 && cbuf[cpos] == '\n') { + cbuf[cpos] = '\0'; + yylval.s = copy(cp); + cbuf[cpos] = '\n'; + state = ARGS; + return (STRING); + } + break; + + case NSTR: + if (cbuf[cpos] == ' ') { + cpos++; + return (SP); + } + if (isdigit(cbuf[cpos])) { + cp = &cbuf[cpos]; + while (isdigit(cbuf[++cpos])) + ; + c = cbuf[cpos]; + cbuf[cpos] = '\0'; + yylval.i = atoi(cp); + cbuf[cpos] = c; + state = STR1; + return (NUMBER); + } + state = STR1; + goto dostr1; + + case ARGS: + if (isdigit(cbuf[cpos])) { + cp = &cbuf[cpos]; + while (isdigit(cbuf[++cpos])) + ; + c = cbuf[cpos]; + cbuf[cpos] = '\0'; + yylval.i = atoi(cp); + cbuf[cpos] = c; + return (NUMBER); + } + switch (cbuf[cpos++]) { + + case '\n': + state = CMD; + return (CRLF); + + case ' ': + return (SP); + + case ',': + return (COMMA); + + case 'A': + case 'a': + return (A); + + case 'B': + case 'b': + return (B); + + case 'C': + case 'c': + return (C); + + case 'E': + case 'e': + return (E); + + case 'F': + case 'f': + return (F); + + case 'I': + case 'i': + return (I); + + case 'L': + case 'l': + return (L); + + case 'N': + case 'n': + return (N); + + case 'P': + case 'p': + return (P); + + case 'R': + case 'r': + return (R); + + case 'S': + case 's': + return (S); + + case 'T': + case 't': + return (T); + + } + break; + + default: + fatal("Unknown state in scanner."); + } + yyerror((char *) 0); + state = CMD; + longjmp(errcatch,0); + } +} + +void +upper(s) + char *s; +{ + while (*s != '\0') { + if (islower(*s)) + *s = toupper(*s); + s++; + } +} + +static char * +copy(s) + char *s; +{ + char *p; + + p = malloc((unsigned) strlen(s) + 1); + if (p == NULL) + fatal("Ran out of memory."); + (void) strcpy(p, s); + return (p); +} + +static void +help(ctab, s) + struct tab *ctab; + char *s; +{ + struct tab *c; + int width, NCMDS; + char *type; + + if (ctab == sitetab) + type = "SITE "; + else + type = ""; + width = 0, NCMDS = 0; + for (c = ctab; c->name != NULL; c++) { + int len = strlen(c->name); + + if (len > width) + width = len; + NCMDS++; + } + width = (width + 8) &~ 7; + if (s == 0) { + int i, j, w; + int columns, lines; + + lreply(214, "The following %scommands are recognized %s.", + type, "(* =>'s unimplemented)"); + columns = 76 / width; + if (columns == 0) + columns = 1; + lines = (NCMDS + columns - 1) / columns; + for (i = 0; i < lines; i++) { + printf(" "); + for (j = 0; j < columns; j++) { + c = ctab + j * lines + i; + printf("%s%c", c->name, + c->implemented ? ' ' : '*'); + if (c + lines >= &ctab[NCMDS]) + break; + w = strlen(c->name) + 1; + while (w < width) { + putchar(' '); + w++; + } + } + printf("\r\n"); + } + (void) fflush(stdout); + reply(214, "Direct comments to ftp-bugs@%s.", hostname); + return; + } + upper(s); + c = lookup(ctab, s); + if (c == (struct tab *)0) { + reply(502, "Unknown command %s.", s); + return; + } + if (c->implemented) + reply(214, "Syntax: %s%s %s", type, c->name, c->help); + else + reply(214, "%s%-*s\t%s; unimplemented.", type, width, + c->name, c->help); +} + +static void +sizecmd(filename) + char *filename; +{ + switch (type) { + case TYPE_L: + case TYPE_I: { + struct stat stbuf; + if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) + reply(550, "%s: not a plain file.", filename); + else + reply(213, "%qu", stbuf.st_size); + break; } + case TYPE_A: { + FILE *fin; + int c; + off_t count; + struct stat stbuf; + fin = fopen(filename, "r"); + if (fin == NULL) { + perror_reply(550, filename); + return; + } + if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { + reply(550, "%s: not a plain file.", filename); + (void) fclose(fin); + return; + } + + count = 0; + while((c=getc(fin)) != EOF) { + if (c == '\n') /* will get expanded to \r\n */ + count++; + count++; + } + (void) fclose(fin); + + reply(213, "%qd", count); + break; } + default: + reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); + } +} diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8 new file mode 100644 index 0000000..6e5b199 --- /dev/null +++ b/libexec/ftpd/ftpd.8 @@ -0,0 +1,310 @@ +.\" Copyright (c) 1985, 1988, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94 +.\" +.Dd April 19, 1994 +.Dt FTPD 8 +.Os BSD 4.2 +.Sh NAME +.Nm ftpd +.Nd +Internet File Transfer Protocol server +.Sh SYNOPSIS +.Nm ftpd +.Op Fl dl +.Op Fl S +.Op Fl U +.Op Fl T Ar maxtimeout +.Op Fl t Ar timeout +.Sh DESCRIPTION +.Nm Ftpd +is the +Internet File Transfer Protocol +server process. The server uses the +.Tn TCP +protocol +and listens at the port specified in the +.Dq ftp +service specification; see +.Xr services 5 . +.Pp +Available options: +.Bl -tag -width Ds +.It Fl d +Debugging information is written to the syslog using LOG_FTP. +.It Fl l +Each successful and failed +.Xr ftp 1 +session is logged using syslog with a facility of LOG_FTP. +If this option is specified twice, the retrieve (get), store (put), append, +delete, make directory, remove directory and rename operations and +their filename arguments are also logged. +.It Fl S +With this option set, +.Nm ftpd +logs all anonymous transfers to the file +.Pa /var/log/ftpd +when this file exists. +. +.It Fl U +In previous versions of +.Nm ftpd , +when a passive mode client requested a data connection to the server, +the server would use data ports in the range 1024..4999. Now, by default, +the server will use data ports in the range 40000..44999. Specifying this +option will revert to the old behavior. +.It Fl T +A client may also request a different timeout period; +the maximum period allowed may be set to +.Ar timeout +seconds with the +.Fl T +option. +The default limit is 2 hours. +.It Fl t +The inactivity timeout period is set to +.Ar timeout +seconds (the default is 15 minutes). +.El +.Pp +The file +.Pa /etc/nologin +can be used to disable ftp access. +If the file exists, +.Nm +displays it and exits. +If the file +.Pa /etc/ftpwelcome +exists, +.Nm +prints it before issuing the +.Dq ready +message. +If the file +.Pa /etc/ftpmotd +exists, +.Nm +prints it after a successful login. +.Pp +The ftp server currently supports the following ftp requests. +The case of the requests is ignored. +.Bl -column "Request" -offset indent +.It Request Ta "Description" +.It ABOR Ta "abort previous command" +.It ACCT Ta "specify account (ignored)" +.It ALLO Ta "allocate storage (vacuously)" +.It APPE Ta "append to a file" +.It CDUP Ta "change to parent of current working directory" +.It CWD Ta "change working directory" +.It DELE Ta "delete a file" +.It HELP Ta "give help information" +.It LIST Ta "give list files in a directory" Pq Dq Li "ls -lgA" +.It MKD Ta "make a directory" +.It MDTM Ta "show last modification time of file" +.It MODE Ta "specify data transfer" Em mode +.It NLST Ta "give name list of files in directory" +.It NOOP Ta "do nothing" +.It PASS Ta "specify password" +.It PASV Ta "prepare for server-to-server transfer" +.It PORT Ta "specify data connection port" +.It PWD Ta "print the current working directory" +.It QUIT Ta "terminate session" +.It REST Ta "restart incomplete transfer" +.It RETR Ta "retrieve a file" +.It RMD Ta "remove a directory" +.It RNFR Ta "specify rename-from file name" +.It RNTO Ta "specify rename-to file name" +.It SITE Ta "non-standard commands (see next section)" +.It SIZE Ta "return size of file" +.It STAT Ta "return status of server" +.It STOR Ta "store a file" +.It STOU Ta "store a file with a unique name" +.It STRU Ta "specify data transfer" Em structure +.It SYST Ta "show operating system type of server system" +.It TYPE Ta "specify data transfer" Em type +.It USER Ta "specify user name" +.It XCUP Ta "change to parent of current working directory (deprecated)" +.It XCWD Ta "change working directory (deprecated)" +.It XMKD Ta "make a directory (deprecated)" +.It XPWD Ta "print the current working directory (deprecated)" +.It XRMD Ta "remove a directory (deprecated)" +.El +.Pp +The following non-standard or +.Tn UNIX +specific commands are supported +by the +SITE request. +.Pp +.Bl -column Request -offset indent +.It Sy Request Ta Sy Description +.It UMASK Ta change umask, e.g. ``SITE UMASK 002'' +.It IDLE Ta set idle-timer, e.g. ``SITE IDLE 60'' +.It CHMOD Ta change mode of a file, e.g. ``SITE CHMOD 755 filename'' +.It HELP Ta give help information. +.El +.Pp +The remaining ftp requests specified in Internet RFC 959 +are +recognized, but not implemented. +MDTM and SIZE are not specified in RFC 959, but will appear in the +next updated FTP RFC. +.Pp +The ftp server will abort an active file transfer only when the +ABOR +command is preceded by a Telnet "Interrupt Process" (IP) +signal and a Telnet "Synch" signal in the command Telnet stream, +as described in Internet RFC 959. +If a +STAT +command is received during a data transfer, preceded by a Telnet IP +and Synch, transfer status will be returned. +.Pp +.Nm Ftpd +interprets file names according to the +.Dq globbing +conventions used by +.Xr csh 1 . +This allows users to utilize the metacharacters +.Dq Li \&*?[]{}~ . +.Pp +.Nm Ftpd +authenticates users according to three rules. +.Pp +.Bl -enum -offset indent +.It +The login name must be in the password data base, +.Pa /etc/passwd , +and not have a null password. +In this case a password must be provided by the client before any +file operations may be performed. +.It +The login name must not appear in the file +.Pa /etc/ftpusers . +.It +The user must have a standard shell returned by +.Xr getusershell 3 . +.It +If the user name is +.Dq anonymous +or +.Dq ftp , +an +anonymous ftp account must be present in the password +file (user +.Dq ftp ) . +In this case the user is allowed +to log in by specifying any password (by convention an email address for +the user should be used as the password). When the +.Fl S +option is set, all transfers are logged as well. +.El +.Pp +In the last case, +.Nm ftpd +takes special measures to restrict the client's access privileges. +The server performs a +.Xr chroot 2 +to the home directory of the +.Dq ftp +user. +In order that system security is not breached, it is recommended +that the +.Dq ftp +subtree be constructed with care, following these rules: +.Bl -tag -width "~ftp/pub" -offset indent +.It Pa ~ftp +Make the home directory owned by +.Dq root +and unwritable by anyone. +.It Pa ~ftp/bin +Make this directory owned by +.Dq root +and unwritable by anyone (mode 555). +The program +.Xr ls 1 +must be present to support the list command. +This program should be mode 111. +.It Pa ~ftp/etc +Make this directory owned by +.Dq root +and unwritable by anyone (mode 555). +The files +.Xr passwd 5 +and +.Xr group 5 +must be present for the +.Xr ls +command to be able to produce owner names rather than numbers. +The password field in +.Xr passwd +is not used, and should not contain real passwords. +The file +.Pa ftpmotd , +if present, will be printed after a successful login. +These files should be mode 444. +.It Pa ~ftp/pub +Make this directory mode 777 and owned by +.Dq ftp . +Guests +can then place files which are to be accessible via the anonymous +account in this directory. +.El +.Sh FILES +.Bl -tag -width /etc/ftpwelcome -compact +.It Pa /etc/ftpusers +List of unwelcome/restricted users. +.It Pa /etc/ftpwelcome +Welcome notice. +.It Pa /etc/ftpmotd +Welcome notice after login. +.It Pa /etc/nologin +Displayed and access refused. +.It Pa /var/log/ftpd +Log file for anonymous transfers. +.El +.Sh SEE ALSO +.Xr ftp 1 , +.Xr getusershell 3 , +.Xr syslogd 8 +.Sh BUGS +The server must run as the super-user +to create sockets with privileged port numbers. It maintains +an effective user id of the logged in user, reverting to +the super-user only when binding addresses to sockets. The +possible security holes have been extensively +scrutinized, but are possibly incomplete. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c new file mode 100644 index 0000000..0e256bf --- /dev/null +++ b/libexec/ftpd/ftpd.c @@ -0,0 +1,1853 @@ +/* + * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ftpd.c,v 1.12 1995/08/28 21:30:49 mpp Exp $ + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)ftpd.c 8.4 (Berkeley) 4/16/94"; +#endif /* not lint */ + +/* + * FTP server. + */ +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <sys/mman.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> + +#define FTP_NAMES +#include <arpa/ftp.h> +#include <arpa/inet.h> +#include <arpa/telnet.h> + +#include <ctype.h> +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <glob.h> +#include <limits.h> +#include <netdb.h> +#include <pwd.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#ifdef SKEY +#include <skey.h> +#endif + +#include "pathnames.h" +#include "extern.h" + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +static char version[] = "Version 6.00"; + +extern off_t restart_point; +extern char cbuf[]; + +struct sockaddr_in ctrl_addr; +struct sockaddr_in data_source; +struct sockaddr_in data_dest; +struct sockaddr_in his_addr; +struct sockaddr_in pasv_addr; + +int data; +jmp_buf errcatch, urgcatch; +int logged_in; +struct passwd *pw; +int debug; +int timeout = 900; /* timeout after 15 minutes of inactivity */ +int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ +int logging; +int restricted_data_ports = 1; +int guest; +#ifdef STATS +int stats; +int statfd = -1; +#endif +int type; +int form; +int stru; /* avoid C keyword */ +int mode; +int usedefault = 1; /* for data transfers */ +int pdata = -1; /* for passive mode */ +sig_atomic_t transflag; +off_t file_size; +off_t byte_count; +#if !defined(CMASK) || CMASK == 0 +#undef CMASK +#define CMASK 027 +#endif +int defumask = CMASK; /* default umask value */ +char tmpline[7]; +char hostname[MAXHOSTNAMELEN]; +char remotehost[MAXHOSTNAMELEN]; +#ifdef STATS +char *ident = NULL; +#endif + +/* + * Timeout intervals for retrying connections + * to hosts that don't accept PORT cmds. This + * is a kludge, but given the problems with TCP... + */ +#define SWAITMAX 90 /* wait at most 90 seconds */ +#define SWAITINT 5 /* interval between retries */ + +int swaitmax = SWAITMAX; +int swaitint = SWAITINT; + +#ifdef SETPROCTITLE +char **Argv = NULL; /* pointer to argument vector */ +char *LastArgv = NULL; /* end of argv */ +char proctitle[LINE_MAX]; /* initial part of title */ +#endif /* SETPROCTITLE */ + +#ifdef SKEY +int pwok = 0; +char addr_string[20]; /* XXX */ +#endif + +#define LOGCMD(cmd, file) \ + if (logging > 1) \ + syslog(LOG_INFO,"%s %s%s", cmd, \ + *(file) == '/' ? "" : curdir(), file); +#define LOGCMD2(cmd, file1, file2) \ + if (logging > 1) \ + syslog(LOG_INFO,"%s %s%s %s%s", cmd, \ + *(file1) == '/' ? "" : curdir(), file1, \ + *(file2) == '/' ? "" : curdir(), file2); +#define LOGBYTES(cmd, file, cnt) \ + if (logging > 1) { \ + if (cnt == (off_t)-1) \ + syslog(LOG_INFO,"%s %s%s", cmd, \ + *(file) == '/' ? "" : curdir(), file); \ + else \ + syslog(LOG_INFO, "%s %s%s = %qd bytes", \ + cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \ + } + +static void ack __P((char *)); +static void myoob __P((int)); +static int checkuser __P((char *)); +static FILE *dataconn __P((char *, off_t, char *)); +static void dolog __P((struct sockaddr_in *)); +static char *curdir __P((void)); +static void end_login __P((void)); +static FILE *getdatasock __P((char *)); +static char *gunique __P((char *)); +static void lostconn __P((int)); +static int receive_data __P((FILE *, FILE *)); +static void send_data __P((FILE *, FILE *, off_t, off_t, int)); +static struct passwd * + sgetpwnam __P((char *)); +static char *sgetsave __P((char *)); + +static char * +curdir() +{ + static char path[MAXPATHLEN+1+1]; /* path + '/' + '\0' */ + + if (getcwd(path, sizeof(path)-2) == NULL) + return (""); + if (path[1] != '\0') /* special case for root dir. */ + strcat(path, "/"); + /* For guest account, skip / since it's chrooted */ + return (guest ? path+1 : path); +} + +int +main(argc, argv, envp) + int argc; + char *argv[]; + char **envp; +{ + int addrlen, ch, on = 1, tos; + char *cp, line[LINE_MAX]; + FILE *fd; + + tzset(); /* in case no timezone database in ~ftp */ + + /* + * LOG_NDELAY sets up the logging connection immediately, + * necessary for anonymous ftp's that chroot and can't do it later. + */ + openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); + addrlen = sizeof(his_addr); + if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { + syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); + exit(1); + } +#ifdef SKEY + strcpy(addr_string, inet_ntoa(his_addr.sin_addr)); +#endif + addrlen = sizeof(ctrl_addr); + if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { + syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); + exit(1); + } +#ifdef IP_TOS + tos = IPTOS_LOWDELAY; + if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) + syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); +#endif + data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); + debug = 0; +#ifdef SETPROCTITLE + /* + * Save start and extent of argv for setproctitle. + */ + Argv = argv; + while (*envp) + envp++; + LastArgv = envp[-1] + strlen(envp[-1]); +#endif /* SETPROCTITLE */ + + +#ifdef STATS + while ((ch = getopt(argc, argv, "dlSt:T:u:v")) != EOF) { +#else + while ((ch = getopt(argc, argv, "dlUt:T:u:v")) != EOF) { +#endif + switch (ch) { + case 'd': + debug = 1; + break; + + case 'l': + logging++; /* > 1 == extra logging */ + break; + + case 'U': + restricted_data_ports = 0; + break; + + case 't': + timeout = atoi(optarg); + if (maxtimeout < timeout) + maxtimeout = timeout; + break; +#ifdef STATS + case 'S': + stats = 1; + break; +#endif + case 'T': + maxtimeout = atoi(optarg); + if (timeout > maxtimeout) + timeout = maxtimeout; + break; + + case 'u': + { + long val = 0; + + val = strtol(optarg, &optarg, 8); + if (*optarg != '\0' || val < 0) + warnx("bad value for -u"); + else + defumask = val; + break; + } + + case 'v': + debug = 1; + break; + + default: + warnx("unknown flag -%c ignored", optopt); + break; + } + } + (void) freopen(_PATH_DEVNULL, "w", stderr); + (void) signal(SIGPIPE, lostconn); + (void) signal(SIGCHLD, SIG_IGN); + if ((int)signal(SIGURG, myoob) < 0) + syslog(LOG_ERR, "signal: %m"); + + /* Try to handle urgent data inline */ +#ifdef SO_OOBINLINE + if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) + syslog(LOG_ERR, "setsockopt: %m"); +#endif + +#ifdef F_SETOWN + if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) + syslog(LOG_ERR, "fcntl F_SETOWN: %m"); +#endif + dolog(&his_addr); + /* + * Set up default state + */ + data = -1; + type = TYPE_A; + form = FORM_N; + stru = STRU_F; + mode = MODE_S; + tmpline[0] = '\0'; + + /* If logins are disabled, print out the message. */ + if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { + while (fgets(line, sizeof(line), fd) != NULL) { + if ((cp = strchr(line, '\n')) != NULL) + *cp = '\0'; + lreply(530, "%s", line); + } + (void) fflush(stdout); + (void) fclose(fd); + reply(530, "System not available."); + exit(0); + } + if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { + while (fgets(line, sizeof(line), fd) != NULL) { + if ((cp = strchr(line, '\n')) != NULL) + *cp = '\0'; + lreply(220, "%s", line); + } + (void) fflush(stdout); + (void) fclose(fd); + /* reply(220,) must follow */ + } + (void) gethostname(hostname, sizeof(hostname)); + reply(220, "%s FTP server (%s) ready.", hostname, version); + (void) setjmp(errcatch); + for (;;) + (void) yyparse(); + /* NOTREACHED */ +} + +static void +lostconn(signo) + int signo; +{ + + if (debug) + syslog(LOG_DEBUG, "lost connection"); + dologout(-1); +} + +static char ttyline[20]; + +/* + * Helper function for sgetpwnam(). + */ +static char * +sgetsave(s) + char *s; +{ + char *new = malloc((unsigned) strlen(s) + 1); + + if (new == NULL) { + perror_reply(421, "Local resource failure: malloc"); + dologout(1); + /* NOTREACHED */ + } + (void) strcpy(new, s); + return (new); +} + +/* + * Save the result of a getpwnam. Used for USER command, since + * the data returned must not be clobbered by any other command + * (e.g., globbing). + */ +static struct passwd * +sgetpwnam(name) + char *name; +{ + static struct passwd save; + struct passwd *p; + + if ((p = getpwnam(name)) == NULL) + return (p); + if (save.pw_name) { + free(save.pw_name); + free(save.pw_passwd); + free(save.pw_gecos); + free(save.pw_dir); + free(save.pw_shell); + } + save = *p; + save.pw_name = sgetsave(p->pw_name); + save.pw_passwd = sgetsave(p->pw_passwd); + save.pw_gecos = sgetsave(p->pw_gecos); + save.pw_dir = sgetsave(p->pw_dir); + save.pw_shell = sgetsave(p->pw_shell); + return (&save); +} + +static int login_attempts; /* number of failed login attempts */ +static int askpasswd; /* had user command, ask for passwd */ +static char curname[10]; /* current USER name */ + +/* + * USER command. + * Sets global passwd pointer pw if named account exists and is acceptable; + * sets askpasswd if a PASS command is expected. If logged in previously, + * need to reset state. If name is "ftp" or "anonymous", the name is not in + * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. + * If account doesn't exist, ask for passwd anyway. Otherwise, check user + * requesting login privileges. Disallow anyone who does not have a standard + * shell as returned by getusershell(). Disallow anyone mentioned in the file + * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. + */ +void +user(name) + char *name; +{ + char *cp, *shell; + + if (logged_in) { + if (guest) { + reply(530, "Can't change user from guest login."); + return; + } + end_login(); + } + + guest = 0; + if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { + if (checkuser("ftp") || checkuser("anonymous")) + reply(530, "User %s access denied.", name); + else if ((pw = sgetpwnam("ftp")) != NULL) { + guest = 1; + askpasswd = 1; + reply(331, + "Guest login ok, send your email address as password."); + } else + reply(530, "User %s unknown.", name); + if (!askpasswd && logging) + syslog(LOG_NOTICE, + "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); + return; + } + if (pw = sgetpwnam(name)) { + if ((shell = pw->pw_shell) == NULL || *shell == 0) + shell = _PATH_BSHELL; + while ((cp = getusershell()) != NULL) + if (strcmp(cp, shell) == 0) + break; + endusershell(); + + if (cp == NULL || checkuser(name)) { + reply(530, "User %s access denied.", name); + if (logging) + syslog(LOG_NOTICE, + "FTP LOGIN REFUSED FROM %s, %s", + remotehost, name); + pw = (struct passwd *) NULL; + return; + } + } + if (logging) + strncpy(curname, name, sizeof(curname)-1); +#ifdef SKEY + pwok = skeyaccess(name, NULL, remotehost, addr_string); + reply(331, "%s", skey_challenge(name, pw, pwok)); +#else + reply(331, "Password required for %s.", name); +#endif + askpasswd = 1; + /* + * Delay before reading passwd after first failed + * attempt to slow down passwd-guessing programs. + */ + if (login_attempts) + sleep((unsigned) login_attempts); +} + +/* + * Check if a user is in the file _PATH_FTPUSERS + */ +static int +checkuser(name) + char *name; +{ + FILE *fd; + int found = 0; + char *p, line[BUFSIZ]; + + if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) { + while (fgets(line, sizeof(line), fd) != NULL) + if ((p = strchr(line, '\n')) != NULL) { + *p = '\0'; + if (line[0] == '#') + continue; + if (strcmp(line, name) == 0) { + found = 1; + break; + } + } + (void) fclose(fd); + } + return (found); +} + +/* + * Terminate login as previous user, if any, resetting state; + * used when USER command is given or login fails. + */ +static void +end_login() +{ + + (void) seteuid((uid_t)0); + if (logged_in) + logwtmp(ttyline, "", ""); + pw = NULL; + logged_in = 0; + guest = 0; +} + +void +pass(passwd) + char *passwd; +{ + char *salt, *xpasswd; + FILE *fd; + static char homedir[MAXPATHLEN]; + + if (logged_in || askpasswd == 0) { + reply(503, "Login with USER first."); + return; + } + askpasswd = 0; + if (!guest) { /* "ftp" is only account allowed no password */ + if (pw == NULL) + salt = "xx"; + else + salt = pw->pw_passwd; +#ifdef SKEY + xpasswd = skey_crypt(passwd, salt, pw, pwok); + pwok = 0; +#else + xpasswd = crypt(passwd, salt); +#endif + /* The strcmp does not catch null passwords! */ + if (pw == NULL || *pw->pw_passwd == '\0' || + (pw->pw_expire && time(NULL) >= pw->pw_expire) || + strcmp(xpasswd, pw->pw_passwd)) { + reply(530, "Login incorrect."); + if (logging) + syslog(LOG_NOTICE, + "FTP LOGIN FAILED FROM %s, %s", + remotehost, curname); + pw = NULL; + if (login_attempts++ >= 5) { + syslog(LOG_NOTICE, + "repeated login failures from %s", + remotehost); + exit(0); + } + return; + } + } + login_attempts = 0; /* this time successful */ + if (setegid((gid_t)pw->pw_gid) < 0) { + reply(550, "Can't set gid."); + return; + } + (void) initgroups(pw->pw_name, pw->pw_gid); + + /* open wtmp before chroot */ + (void)sprintf(ttyline, "ftp%d", getpid()); + logwtmp(ttyline, pw->pw_name, remotehost); + logged_in = 1; + +#ifdef STATS + if (guest && stats == 1 && statfd < 0) + if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0) + stats = 0; +#endif + + if (guest) { + /* + * We MUST do a chdir() after the chroot. Otherwise + * the old current directory will be accessible as "." + * outside the new root! + */ + if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { + reply(550, "Can't set guest privileges."); + goto bad; + } + } else if (chdir(pw->pw_dir) < 0) { + if (chdir("/") < 0) { + reply(530, "User %s: can't change directory to %s.", + pw->pw_name, pw->pw_dir); + goto bad; + } else + lreply(230, "No directory! Logging in with home=/"); + } + if (seteuid((uid_t)pw->pw_uid) < 0) { + reply(550, "Can't set uid."); + goto bad; + } + + /* + * Set home directory so that use of ~ (tilde) works correctly. + */ + if (getcwd(homedir, MAXPATHLEN) != NULL) + setenv("HOME", homedir, 1); + + /* + * Display a login message, if it exists. + * N.B. reply(230,) must follow the message. + */ + if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { + char *cp, line[LINE_MAX]; + + while (fgets(line, sizeof(line), fd) != NULL) { + if ((cp = strchr(line, '\n')) != NULL) + *cp = '\0'; + lreply(230, "%s", line); + } + (void) fflush(stdout); + (void) fclose(fd); + } + if (guest) { +#ifdef STATS + char * copy(); + + if (ident != NULL) + free(ident); + ident = (char *) copy(passwd); +#endif + reply(230, "Guest login ok, access restrictions apply."); +#ifdef SETPROCTITLE + snprintf(proctitle, sizeof(proctitle), + "%s: anonymous/%.*s", remotehost, + sizeof(proctitle) - sizeof(remotehost) - + sizeof(": anonymous/"), passwd); + setproctitle(proctitle); +#endif /* SETPROCTITLE */ + if (logging) + syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", + remotehost, passwd); + } else { + reply(230, "User %s logged in.", pw->pw_name); +#ifdef SETPROCTITLE + snprintf(proctitle, sizeof(proctitle), + "%s: %s", remotehost, pw->pw_name); + setproctitle(proctitle); +#endif /* SETPROCTITLE */ + if (logging) + syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", + remotehost, pw->pw_name); + } + (void) umask(defumask); + return; +bad: + /* Forget all about it... */ + end_login(); +} + +void +retrieve(cmd, name) + char *cmd, *name; +{ + FILE *fin, *dout; + struct stat st; + int (*closefunc) __P((FILE *)); +#ifdef STATS + long start; +#endif + + if (cmd == 0) { + fin = fopen(name, "r"), closefunc = fclose; + st.st_size = 0; + } else { + char line[BUFSIZ]; + + (void) sprintf(line, cmd, name), name = line; + fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; + st.st_size = -1; + st.st_blksize = BUFSIZ; + } + if (fin == NULL) { + if (errno != 0) { + perror_reply(550, name); + if (cmd == 0) { + LOGCMD("get", name); + } + } + return; + } + byte_count = -1; + if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) { + reply(550, "%s: not a plain file.", name); + goto done; + } + if (restart_point) { + if (type == TYPE_A) { + off_t i, n; + int c; + + n = restart_point; + i = 0; + while (i++ < n) { + if ((c=getc(fin)) == EOF) { + perror_reply(550, name); + goto done; + } + if (c == '\n') + i++; + } + } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { + perror_reply(550, name); + goto done; + } + } + dout = dataconn(name, st.st_size, "w"); + if (dout == NULL) + goto done; +#ifdef STATS + time(&start); +#endif + send_data(fin, dout, st.st_blksize, st.st_size, + restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode)); +#ifdef STATS + if (cmd == 0 && guest && stats) + logxfer( name, st.st_size, start); +#endif + (void) fclose(dout); + data = -1; + pdata = -1; +done: + if (cmd == 0) + LOGBYTES("get", name, byte_count); + (*closefunc)(fin); +} + +void +store(name, mode, unique) + char *name, *mode; + int unique; +{ + FILE *fout, *din; + struct stat st; + int (*closefunc) __P((FILE *)); + + if (unique && stat(name, &st) == 0 && + (name = gunique(name)) == NULL) { + LOGCMD(*mode == 'w' ? "put" : "append", name); + return; + } + + if (restart_point) + mode = "r+"; + fout = fopen(name, mode); + closefunc = fclose; + if (fout == NULL) { + perror_reply(553, name); + LOGCMD(*mode == 'w' ? "put" : "append", name); + return; + } + byte_count = -1; + if (restart_point) { + if (type == TYPE_A) { + off_t i, n; + int c; + + n = restart_point; + i = 0; + while (i++ < n) { + if ((c=getc(fout)) == EOF) { + perror_reply(550, name); + goto done; + } + if (c == '\n') + i++; + } + /* + * We must do this seek to "current" position + * because we are changing from reading to + * writing. + */ + if (fseek(fout, 0L, L_INCR) < 0) { + perror_reply(550, name); + goto done; + } + } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { + perror_reply(550, name); + goto done; + } + } + din = dataconn(name, (off_t)-1, "r"); + if (din == NULL) + goto done; + if (receive_data(din, fout) == 0) { + if (unique) + reply(226, "Transfer complete (unique file name:%s).", + name); + else + reply(226, "Transfer complete."); + } + (void) fclose(din); + data = -1; + pdata = -1; +done: + LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count); + (*closefunc)(fout); +} + +static FILE * +getdatasock(mode) + char *mode; +{ + int on = 1, s, t, tries; + + if (data >= 0) + return (fdopen(data, mode)); + (void) seteuid((uid_t)0); + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + goto bad; + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, + (char *) &on, sizeof(on)) < 0) + goto bad; + /* anchor socket to avoid multi-homing problems */ + data_source.sin_family = AF_INET; + data_source.sin_addr = ctrl_addr.sin_addr; + for (tries = 1; ; tries++) { + if (bind(s, (struct sockaddr *)&data_source, + sizeof(data_source)) >= 0) + break; + if (errno != EADDRINUSE || tries > 10) + goto bad; + sleep(tries); + } + (void) seteuid((uid_t)pw->pw_uid); +#ifdef IP_TOS + on = IPTOS_THROUGHPUT; + if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) + syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); +#endif +#ifdef TCP_NOPUSH + /* + * Turn off push flag to keep sender TCP from sending short packets + * at the boundaries of each write(). Should probably do a SO_SNDBUF + * to set the send buffer size as well, but that may not be desirable + * in heavy-load situations. + */ + on = 1; + if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *)&on, sizeof on) < 0) + syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m"); +#endif +#ifdef SO_SNDBUF + on = 65536; + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&on, sizeof on) < 0) + syslog(LOG_WARNING, "setsockopt (SO_SNDBUF): %m"); +#endif + + return (fdopen(s, mode)); +bad: + /* Return the real value of errno (close may change it) */ + t = errno; + (void) seteuid((uid_t)pw->pw_uid); + (void) close(s); + errno = t; + return (NULL); +} + +static FILE * +dataconn(name, size, mode) + char *name; + off_t size; + char *mode; +{ + char sizebuf[32]; + FILE *file; + int retry = 0, tos; + + file_size = size; + byte_count = 0; + if (size != (off_t) -1) + (void) sprintf(sizebuf, " (%qd bytes)", size); + else + (void) strcpy(sizebuf, ""); + if (pdata >= 0) { + struct sockaddr_in from; + int s, fromlen = sizeof(from); + struct timeval timeout; + fd_set set; + + FD_ZERO(&set); + FD_SET(pdata, &set); + + timeout.tv_usec = 0; + timeout.tv_sec = 120; + + if (select(pdata+1, &set, (fd_set *) 0, (fd_set *) 0, &timeout) == 0 || + (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) { + reply(425, "Can't open data connection."); + (void) close(pdata); + pdata = -1; + return (NULL); + } + (void) close(pdata); + pdata = s; +#ifdef IP_TOS + tos = IPTOS_LOWDELAY; + (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, + sizeof(int)); +#endif + reply(150, "Opening %s mode data connection for '%s'%s.", + type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); + return (fdopen(pdata, mode)); + } + if (data >= 0) { + reply(125, "Using existing data connection for '%s'%s.", + name, sizebuf); + usedefault = 1; + return (fdopen(data, mode)); + } + if (usedefault) + data_dest = his_addr; + usedefault = 1; + file = getdatasock(mode); + if (file == NULL) { + reply(425, "Can't create data socket (%s,%d): %s.", + inet_ntoa(data_source.sin_addr), + ntohs(data_source.sin_port), strerror(errno)); + return (NULL); + } + data = fileno(file); + while (connect(data, (struct sockaddr *)&data_dest, + sizeof(data_dest)) < 0) { + if (errno == EADDRINUSE && retry < swaitmax) { + sleep((unsigned) swaitint); + retry += swaitint; + continue; + } + perror_reply(425, "Can't build data connection"); + (void) fclose(file); + data = -1; + return (NULL); + } + reply(150, "Opening %s mode data connection for '%s'%s.", + type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); + return (file); +} + +/* + * Tranfer the contents of "instr" to "outstr" peer using the appropriate + * encapsulation of the data subject to Mode, Structure, and Type. + * + * NB: Form isn't handled. + */ +static void +send_data(instr, outstr, blksize, filesize, isreg) + FILE *instr, *outstr; + off_t blksize; + off_t filesize; + int isreg; +{ + int c, cnt, filefd, netfd; + char *buf, *bp; + size_t len; + + transflag++; + if (setjmp(urgcatch)) { + transflag = 0; + return; + } + switch (type) { + + case TYPE_A: + while ((c = getc(instr)) != EOF) { + byte_count++; + if (c == '\n') { + if (ferror(outstr)) + goto data_err; + (void) putc('\r', outstr); + } + (void) putc(c, outstr); + } + fflush(outstr); + transflag = 0; + if (ferror(instr)) + goto file_err; + if (ferror(outstr)) + goto data_err; + reply(226, "Transfer complete."); + return; + + case TYPE_I: + case TYPE_L: + /* + * isreg is only set if we are not doing restart and we + * are sending a regular file + */ + netfd = fileno(outstr); + filefd = fileno(instr); + + if (isreg && filesize < (off_t)16 * 1024 * 1024) { + buf = mmap(0, filesize, PROT_READ, MAP_SHARED, filefd, + (off_t)0); + if (!buf) { + syslog(LOG_WARNING, "mmap(%lu): %m", + (unsigned long)filesize); + goto oldway; + } + bp = buf; + len = filesize; + do { + cnt = write(netfd, bp, len); + len -= cnt; + bp += cnt; + if (cnt > 0) byte_count += cnt; + } while(cnt > 0 && len > 0); + + transflag = 0; + munmap(buf, (size_t)filesize); + if (cnt < 0) + goto data_err; + reply(226, "Transfer complete."); + return; + } + +oldway: + if ((buf = malloc((u_int)blksize)) == NULL) { + transflag = 0; + perror_reply(451, "Local resource failure: malloc"); + return; + } + + while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && + write(netfd, buf, cnt) == cnt) + byte_count += cnt; + transflag = 0; + (void)free(buf); + if (cnt != 0) { + if (cnt < 0) + goto file_err; + goto data_err; + } + reply(226, "Transfer complete."); + return; + default: + transflag = 0; + reply(550, "Unimplemented TYPE %d in send_data", type); + return; + } + +data_err: + transflag = 0; + perror_reply(426, "Data connection"); + return; + +file_err: + transflag = 0; + perror_reply(551, "Error on input file"); +} + +/* + * Transfer data from peer to "outstr" using the appropriate encapulation of + * the data subject to Mode, Structure, and Type. + * + * N.B.: Form isn't handled. + */ +static int +receive_data(instr, outstr) + FILE *instr, *outstr; +{ + int c; + int cnt, bare_lfs = 0; + char buf[BUFSIZ]; + + transflag++; + if (setjmp(urgcatch)) { + transflag = 0; + return (-1); + } + switch (type) { + + case TYPE_I: + case TYPE_L: + while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) { + if (write(fileno(outstr), buf, cnt) != cnt) + goto file_err; + byte_count += cnt; + } + if (cnt < 0) + goto data_err; + transflag = 0; + return (0); + + case TYPE_E: + reply(553, "TYPE E not implemented."); + transflag = 0; + return (-1); + + case TYPE_A: + while ((c = getc(instr)) != EOF) { + byte_count++; + if (c == '\n') + bare_lfs++; + while (c == '\r') { + if (ferror(outstr)) + goto data_err; + if ((c = getc(instr)) != '\n') { + (void) putc ('\r', outstr); + if (c == '\0' || c == EOF) + goto contin2; + } + } + (void) putc(c, outstr); + contin2: ; + } + fflush(outstr); + if (ferror(instr)) + goto data_err; + if (ferror(outstr)) + goto file_err; + transflag = 0; + if (bare_lfs) { + lreply(226, + "WARNING! %d bare linefeeds received in ASCII mode", + bare_lfs); + (void)printf(" File may not have transferred correctly.\r\n"); + } + return (0); + default: + reply(550, "Unimplemented TYPE %d in receive_data", type); + transflag = 0; + return (-1); + } + +data_err: + transflag = 0; + perror_reply(426, "Data Connection"); + return (-1); + +file_err: + transflag = 0; + perror_reply(452, "Error writing file"); + return (-1); +} + +void +statfilecmd(filename) + char *filename; +{ + FILE *fin; + int c; + char line[LINE_MAX]; + + (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename); + fin = ftpd_popen(line, "r"); + lreply(211, "status of %s:", filename); + while ((c = getc(fin)) != EOF) { + if (c == '\n') { + if (ferror(stdout)){ + perror_reply(421, "control connection"); + (void) ftpd_pclose(fin); + dologout(1); + /* NOTREACHED */ + } + if (ferror(fin)) { + perror_reply(551, filename); + (void) ftpd_pclose(fin); + return; + } + (void) putc('\r', stdout); + } + (void) putc(c, stdout); + } + (void) ftpd_pclose(fin); + reply(211, "End of Status"); +} + +void +statcmd() +{ + struct sockaddr_in *sin; + u_char *a, *p; + + lreply(211, "%s FTP server status:", hostname, version); + printf(" %s\r\n", version); + printf(" Connected to %s", remotehost); + if (!isdigit(remotehost[0])) + printf(" (%s)", inet_ntoa(his_addr.sin_addr)); + printf("\r\n"); + if (logged_in) { + if (guest) + printf(" Logged in anonymously\r\n"); + else + printf(" Logged in as %s\r\n", pw->pw_name); + } else if (askpasswd) + printf(" Waiting for password\r\n"); + else + printf(" Waiting for user name\r\n"); + printf(" TYPE: %s", typenames[type]); + if (type == TYPE_A || type == TYPE_E) + printf(", FORM: %s", formnames[form]); + if (type == TYPE_L) +#if NBBY == 8 + printf(" %d", NBBY); +#else + printf(" %d", bytesize); /* need definition! */ +#endif + printf("; STRUcture: %s; transfer MODE: %s\r\n", + strunames[stru], modenames[mode]); + if (data != -1) + printf(" Data connection open\r\n"); + else if (pdata != -1) { + printf(" in Passive mode"); + sin = &pasv_addr; + goto printaddr; + } else if (usedefault == 0) { + printf(" PORT"); + sin = &data_dest; +printaddr: + a = (u_char *) &sin->sin_addr; + p = (u_char *) &sin->sin_port; +#define UC(b) (((int) b) & 0xff) + printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), + UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); +#undef UC + } else + printf(" No data connection\r\n"); + reply(211, "End of status"); +} + +void +fatal(s) + char *s; +{ + + reply(451, "Error in server: %s\n", s); + reply(221, "Closing connection due to server error."); + dologout(0); + /* NOTREACHED */ +} + +void +#if __STDC__ +reply(int n, const char *fmt, ...) +#else +reply(n, fmt, va_alist) + int n; + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)printf("%d ", n); + (void)vprintf(fmt, ap); + (void)printf("\r\n"); + (void)fflush(stdout); + if (debug) { + syslog(LOG_DEBUG, "<--- %d ", n); + vsyslog(LOG_DEBUG, fmt, ap); + } +} + +void +#if __STDC__ +lreply(int n, const char *fmt, ...) +#else +lreply(n, fmt, va_alist) + int n; + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)printf("%d- ", n); + (void)vprintf(fmt, ap); + (void)printf("\r\n"); + (void)fflush(stdout); + if (debug) { + syslog(LOG_DEBUG, "<--- %d- ", n); + vsyslog(LOG_DEBUG, fmt, ap); + } +} + +static void +ack(s) + char *s; +{ + + reply(250, "%s command successful.", s); +} + +void +nack(s) + char *s; +{ + + reply(502, "%s command not implemented.", s); +} + +/* ARGSUSED */ +void +yyerror(s) + char *s; +{ + char *cp; + + if (cp = strchr(cbuf,'\n')) + *cp = '\0'; + reply(500, "'%s': command not understood.", cbuf); +} + +void +delete(name) + char *name; +{ + struct stat st; + + LOGCMD("delete", name); + if (stat(name, &st) < 0) { + perror_reply(550, name); + return; + } + if ((st.st_mode&S_IFMT) == S_IFDIR) { + if (rmdir(name) < 0) { + perror_reply(550, name); + return; + } + goto done; + } + if (unlink(name) < 0) { + perror_reply(550, name); + return; + } +done: + ack("DELE"); +} + +void +cwd(path) + char *path; +{ + + if (chdir(path) < 0) + perror_reply(550, path); + else + ack("CWD"); +} + +void +makedir(name) + char *name; +{ + + LOGCMD("mkdir", name); + if (mkdir(name, 0777) < 0) + perror_reply(550, name); + else + reply(257, "MKD command successful."); +} + +void +removedir(name) + char *name; +{ + + LOGCMD("rmdir", name); + if (rmdir(name) < 0) + perror_reply(550, name); + else + ack("RMD"); +} + +void +pwd() +{ + char path[MAXPATHLEN + 1]; + + if (getwd(path) == (char *)NULL) + reply(550, "%s.", path); + else + reply(257, "\"%s\" is current directory.", path); +} + +char * +renamefrom(name) + char *name; +{ + struct stat st; + + if (stat(name, &st) < 0) { + perror_reply(550, name); + return ((char *)0); + } + reply(350, "File exists, ready for destination name"); + return (name); +} + +void +renamecmd(from, to) + char *from, *to; +{ + + LOGCMD2("rename", from, to); + if (rename(from, to) < 0) + perror_reply(550, "rename"); + else + ack("RNTO"); +} + +static void +dolog(sin) + struct sockaddr_in *sin; +{ + struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, + sizeof(struct in_addr), AF_INET); + + if (hp) + (void) strncpy(remotehost, hp->h_name, sizeof(remotehost)); + else + (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), + sizeof(remotehost)); +#ifdef SETPROCTITLE + snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); + setproctitle(proctitle); +#endif /* SETPROCTITLE */ + + if (logging) + syslog(LOG_INFO, "connection from %s", remotehost); +} + +/* + * Record logout in wtmp file + * and exit with supplied status. + */ +void +dologout(status) + int status; +{ + + if (logged_in) { + (void) seteuid((uid_t)0); + logwtmp(ttyline, "", ""); + } + /* beware of flushing buffers after a SIGPIPE */ + _exit(status); +} + +static void +myoob(signo) + int signo; +{ + char *cp; + + /* only process if transfer occurring */ + if (!transflag) + return; + cp = tmpline; + if (getline(cp, 7, stdin) == NULL) { + reply(221, "You could at least say goodbye."); + dologout(0); + } + upper(cp); + if (strcmp(cp, "ABOR\r\n") == 0) { + tmpline[0] = '\0'; + reply(426, "Transfer aborted. Data connection closed."); + reply(226, "Abort successful"); + longjmp(urgcatch, 1); + } + if (strcmp(cp, "STAT\r\n") == 0) { + if (file_size != (off_t) -1) + reply(213, "Status: %qd of %qd bytes transferred", + byte_count, file_size); + else + reply(213, "Status: %qd bytes transferred", byte_count); + } +} + +/* + * Note: a response of 425 is not mentioned as a possible response to + * the PASV command in RFC959. However, it has been blessed as + * a legitimate response by Jon Postel in a telephone conversation + * with Rick Adams on 25 Jan 89. + */ +void +passive() +{ + int len; + u_short port; + char *p, *a; + + pdata = socket(AF_INET, SOCK_STREAM, 0); + if (pdata < 0) { + perror_reply(425, "Can't open passive connection"); + return; + } + + if (restricted_data_ports) { + for (port = FTP_DATA_BOTTOM; port <= FTP_DATA_TOP; port++) { + pasv_addr = ctrl_addr; + pasv_addr.sin_port = htons(port); + (void) seteuid((uid_t)0); + if (bind(pdata, (struct sockaddr *)&pasv_addr, + sizeof(pasv_addr)) < 0) { + (void) seteuid((uid_t)pw->pw_uid); + if (errno == EADDRINUSE) + continue; + else + goto pasv_error; + } + (void) seteuid((uid_t)pw->pw_uid); + break; + } + if (port > FTP_DATA_TOP) + goto pasv_error; + } else { + pasv_addr = ctrl_addr; + pasv_addr.sin_port = 0; + (void) seteuid((uid_t)0); + if (bind(pdata, (struct sockaddr *)&pasv_addr, + sizeof(pasv_addr)) < 0) { + (void) seteuid((uid_t)pw->pw_uid); + goto pasv_error; + } + (void) seteuid((uid_t)pw->pw_uid); + } + + len = sizeof(pasv_addr); + if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) + goto pasv_error; + if (listen(pdata, 1) < 0) + goto pasv_error; + a = (char *) &pasv_addr.sin_addr; + p = (char *) &pasv_addr.sin_port; + +#define UC(b) (((int) b) & 0xff) + + reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), + UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); + return; + +pasv_error: + (void) close(pdata); + pdata = -1; + perror_reply(425, "Can't open passive connection"); + return; +} + +/* + * Generate unique name for file with basename "local". + * The file named "local" is already known to exist. + * Generates failure reply on error. + */ +static char * +gunique(local) + char *local; +{ + static char new[MAXPATHLEN]; + struct stat st; + int count; + char *cp; + + cp = strrchr(local, '/'); + if (cp) + *cp = '\0'; + if (stat(cp ? local : ".", &st) < 0) { + perror_reply(553, cp ? local : "."); + return ((char *) 0); + } + if (cp) + *cp = '/'; + (void) strcpy(new, local); + cp = new + strlen(new); + *cp++ = '.'; + for (count = 1; count < 100; count++) { + (void)sprintf(cp, "%d", count); + if (stat(new, &st) < 0) + return (new); + } + reply(452, "Unique file name cannot be created."); + return (NULL); +} + +/* + * Format and send reply containing system error number. + */ +void +perror_reply(code, string) + int code; + char *string; +{ + + reply(code, "%s: %s.", string, strerror(errno)); +} + +static char *onefile[] = { + "", + 0 +}; + +void +send_file_list(whichf) + char *whichf; +{ + struct stat st; + DIR *dirp = NULL; + struct dirent *dir; + FILE *dout = NULL; + char **dirlist, *dirname; + int simple = 0; + int freeglob = 0; + glob_t gl; + + if (strpbrk(whichf, "~{[*?") != NULL) { + int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; + + memset(&gl, 0, sizeof(gl)); + freeglob = 1; + if (glob(whichf, flags, 0, &gl)) { + reply(550, "not found"); + goto out; + } else if (gl.gl_pathc == 0) { + errno = ENOENT; + perror_reply(550, whichf); + goto out; + } + dirlist = gl.gl_pathv; + } else { + onefile[0] = whichf; + dirlist = onefile; + simple = 1; + } + + if (setjmp(urgcatch)) { + transflag = 0; + goto out; + } + while (dirname = *dirlist++) { + if (stat(dirname, &st) < 0) { + /* + * If user typed "ls -l", etc, and the client + * used NLST, do what the user meant. + */ + if (dirname[0] == '-' && *dirlist == NULL && + transflag == 0) { + retrieve("/bin/ls %s", dirname); + goto out; + } + perror_reply(550, whichf); + if (dout != NULL) { + (void) fclose(dout); + transflag = 0; + data = -1; + pdata = -1; + } + goto out; + } + + if (S_ISREG(st.st_mode)) { + if (dout == NULL) { + dout = dataconn("file list", (off_t)-1, "w"); + if (dout == NULL) + goto out; + transflag++; + } + fprintf(dout, "%s%s\n", dirname, + type == TYPE_A ? "\r" : ""); + byte_count += strlen(dirname) + 1; + continue; + } else if (!S_ISDIR(st.st_mode)) + continue; + + if ((dirp = opendir(dirname)) == NULL) + continue; + + while ((dir = readdir(dirp)) != NULL) { + char nbuf[MAXPATHLEN]; + + if (dir->d_name[0] == '.' && dir->d_namlen == 1) + continue; + if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && + dir->d_namlen == 2) + continue; + + sprintf(nbuf, "%s/%s", dirname, dir->d_name); + + /* + * We have to do a stat to insure it's + * not a directory or special file. + */ + if (simple || (stat(nbuf, &st) == 0 && + S_ISREG(st.st_mode))) { + if (dout == NULL) { + dout = dataconn("file list", (off_t)-1, + "w"); + if (dout == NULL) + goto out; + transflag++; + } + if (nbuf[0] == '.' && nbuf[1] == '/') + fprintf(dout, "%s%s\n", &nbuf[2], + type == TYPE_A ? "\r" : ""); + else + fprintf(dout, "%s%s\n", nbuf, + type == TYPE_A ? "\r" : ""); + byte_count += strlen(nbuf) + 1; + } + } + (void) closedir(dirp); + } + + if (dout == NULL) + reply(550, "No files found."); + else if (ferror(dout) != 0) + perror_reply(550, "Data connection"); + else + reply(226, "Transfer complete."); + + transflag = 0; + if (dout != NULL) + (void) fclose(dout); + data = -1; + pdata = -1; +out: + if (freeglob) { + freeglob = 0; + globfree(&gl); + } +} + +#ifdef SETPROCTITLE +/* + * Clobber argv so ps will show what we're doing. (Stolen from sendmail.) + * Warning, since this is usually started from inetd.conf, it often doesn't + * have much of an environment or arglist to overwrite. + */ +void +#if __STDC__ +setproctitle(const char *fmt, ...) +#else +setproctitle(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + int i; + va_list ap; + char *p, *bp, ch; + char buf[LINE_MAX]; + +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)vsnprintf(buf, sizeof(buf), fmt, ap); + + /* make ps print our process name */ + p = Argv[0]; + *p++ = '-'; + + i = strlen(buf); + if (i > LastArgv - p - 2) { + i = LastArgv - p - 2; + buf[i] = '\0'; + } + bp = buf; + while (ch = *bp++) + if (ch != '\n' && ch != '\r') + *p++ = ch; + while (p < LastArgv) + *p++ = ' '; +} +#endif /* SETPROCTITLE */ + +#ifdef STATS +logxfer(name, size, start) + char *name; + long size; + long start; +{ + char buf[1024]; + char path[MAXPATHLEN + 1]; + long now; + + if (statfd >= 0 && getwd(path) != NULL) { + time(&now); + sprintf(buf, "%.20s!%s!%s!%s/%s!%ld!%ld\n", + ctime(&now)+4, ident, remotehost, + path, name, size, now - start + (now == start)); + write(statfd, buf, strlen(buf)); + } +} + +char * +copy(s) + char *s; +{ + char *p; + + p = malloc((unsigned) strlen(s) + 1); + if (p == NULL) + fatal("Ran out of memory."); + (void) strcpy(p, s); + return (p); +} +#endif diff --git a/libexec/ftpd/logwtmp.c b/libexec/ftpd/logwtmp.c new file mode 100644 index 0000000..d40840c --- /dev/null +++ b/libexec/ftpd/logwtmp.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)logwtmp.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <fcntl.h> +#include <utmp.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include "extern.h" + +static int fd = -1; + +/* + * Modified version of logwtmp that holds wtmp file open + * after first call, for use with ftp (which may chroot + * after login, but before logout). + */ +void +logwtmp(line, name, host) + char *line, *name, *host; +{ + struct utmp ut; + struct stat buf; + + if (fd < 0 && (fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) < 0) + return; + if (fstat(fd, &buf) == 0) { + (void)strncpy(ut.ut_line, line, sizeof(ut.ut_line)); + (void)strncpy(ut.ut_name, name, sizeof(ut.ut_name)); + (void)strncpy(ut.ut_host, host, sizeof(ut.ut_host)); + (void)time(&ut.ut_time); + if (write(fd, (char *)&ut, sizeof(struct utmp)) != + sizeof(struct utmp)) + (void)ftruncate(fd, buf.st_size); + } +} diff --git a/libexec/ftpd/pathnames.h b/libexec/ftpd/pathnames.h new file mode 100644 index 0000000..85a656b --- /dev/null +++ b/libexec/ftpd/pathnames.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + */ + +#include <paths.h> + +#define _PATH_FTPWELCOME "/etc/ftpwelcome" +#define _PATH_FTPLOGINMESG "/etc/ftpmotd" +#define _PATH_FTPDSTATFILE "/var/log/ftpd" diff --git a/libexec/ftpd/popen.c b/libexec/ftpd/popen.c new file mode 100644 index 0000000..b26732e --- /dev/null +++ b/libexec/ftpd/popen.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software written by Ken Arnold and + * published in UNIX Review, Vol. 6, No. 8. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 4/6/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/wait.h> + +#include <errno.h> +#include <glob.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" + +/* + * Special version of popen which avoids call to shell. This ensures noone + * may create a pipe to a hidden program as a side effect of a list or dir + * command. + */ +static int *pids; +static int fds; + +FILE * +ftpd_popen(program, type) + char *program, *type; +{ + char *cp; + FILE *iop; + int argc, gargc, pdes[2], pid; + char **pop, *argv[100], *gargv[1000]; + + if (*type != 'r' && *type != 'w' || type[1]) + return (NULL); + + if (!pids) { + if ((fds = getdtablesize()) <= 0) + return (NULL); + if ((pids = (int *)malloc((u_int)(fds * sizeof(int)))) == NULL) + return (NULL); + memset(pids, 0, fds * sizeof(int)); + } + if (pipe(pdes) < 0) + return (NULL); + + /* break up string into pieces */ + for (argc = 0, cp = program;; cp = NULL) + if (!(argv[argc++] = strtok(cp, " \t\n"))) + break; + + /* glob each piece */ + gargv[0] = argv[0]; + for (gargc = argc = 1; argv[argc]; argc++) { + glob_t gl; + int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; + + memset(&gl, 0, sizeof(gl)); + if (glob(argv[argc], flags, NULL, &gl)) + gargv[gargc++] = strdup(argv[argc]); + else + for (pop = gl.gl_pathv; *pop; pop++) + gargv[gargc++] = strdup(*pop); + globfree(&gl); + } + gargv[gargc] = NULL; + + iop = NULL; + switch(pid = vfork()) { + case -1: /* error */ + (void)close(pdes[0]); + (void)close(pdes[1]); + goto pfree; + /* NOTREACHED */ + case 0: /* child */ + if (*type == 'r') { + if (pdes[1] != STDOUT_FILENO) { + dup2(pdes[1], STDOUT_FILENO); + (void)close(pdes[1]); + } + dup2(STDOUT_FILENO, STDERR_FILENO); /* stderr too! */ + (void)close(pdes[0]); + } else { + if (pdes[0] != STDIN_FILENO) { + dup2(pdes[0], STDIN_FILENO); + (void)close(pdes[0]); + } + (void)close(pdes[1]); + } + execv(gargv[0], gargv); + _exit(1); + } + /* parent; assume fdopen can't fail... */ + if (*type == 'r') { + iop = fdopen(pdes[0], type); + (void)close(pdes[1]); + } else { + iop = fdopen(pdes[1], type); + (void)close(pdes[0]); + } + pids[fileno(iop)] = pid; + +pfree: for (argc = 1; gargv[argc] != NULL; argc++) + free(gargv[argc]); + + return (iop); +} + +int +ftpd_pclose(iop) + FILE *iop; +{ + int fdes, omask, status; + pid_t pid; + + /* + * pclose returns -1 if stream is not associated with a + * `popened' command, or, if already `pclosed'. + */ + if (pids == 0 || pids[fdes = fileno(iop)] == 0) + return (-1); + (void)fclose(iop); + omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); + while ((pid = waitpid(pids[fdes], &status, 0)) < 0 && errno == EINTR) + continue; + (void)sigsetmask(omask); + pids[fdes] = 0; + if (pid < 0) + return (pid); + if (WIFEXITED(status)) + return (WEXITSTATUS(status)); + return (1); +} diff --git a/libexec/ftpd/skey-stuff.c b/libexec/ftpd/skey-stuff.c new file mode 100644 index 0000000..5c34b9b --- /dev/null +++ b/libexec/ftpd/skey-stuff.c @@ -0,0 +1,24 @@ +/* Author: Wietse Venema, Eindhoven University of Technology. */ + +#include <stdio.h> +#include <pwd.h> + +#include <skey.h> + +/* skey_challenge - additional password prompt stuff */ + +char *skey_challenge(name, pwd, pwok) +char *name; +struct passwd *pwd; +int pwok; +{ + static char buf[128]; + struct skey skey; + + /* Display s/key challenge where appropriate. */ + + if (pwd == 0 || skeychallenge(&skey, pwd->pw_name, buf) != 0) + sprintf(buf, "%s required for %s.", + pwok ? "Password" : "S/Key password", name); + return (buf); +} diff --git a/libexec/getNAME/Makefile b/libexec/getNAME/Makefile new file mode 100644 index 0000000..a78bc28 --- /dev/null +++ b/libexec/getNAME/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= getNAME +NOMAN= noman + +.include <bsd.prog.mk> diff --git a/libexec/getNAME/getNAME.c b/libexec/getNAME/getNAME.c new file mode 100644 index 0000000..2ab64b4 --- /dev/null +++ b/libexec/getNAME/getNAME.c @@ -0,0 +1,339 @@ +/*- + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)getNAME.c 8.1 (Berkeley) 6/30/93"; +#endif /* not lint */ + +/* + * Get name sections from manual pages. + * -t for building toc + * -i for building intro entries + * other apropos database + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int tocrc; +int intro; +int typeflag; + +void doname __P((char *)); +void dorefname __P((char *)); +void getfrom __P((char *)); +void split __P((char *, char *)); +void trimln __P((char *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + int ch; + + while ((ch = getopt(argc, argv, "itw")) != EOF) + switch(ch) { + case 'i': + intro = 1; + break; + case 't': + tocrc = 1; + break; + case 'w': + typeflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (!*argv) + usage(); + + for (; *argv; ++argv) + getfrom(*argv); + exit(0); +} + +void +getfrom(pathname) + char *pathname; +{ + int i = 0; + char *name, *loc; + char headbuf[BUFSIZ]; + char linbuf[BUFSIZ]; + + if (freopen(pathname, "r", stdin) == 0) { + perror(pathname); + return; + } + if (name = strrchr(pathname, '/')) + name++; + else + name = pathname; + for (;;) { + if (fgets(headbuf, sizeof headbuf, stdin) == NULL) { + if (typeflag) + printf("%-60s UNKNOWN\n", pathname); + return; + } + if (headbuf[0] != '.') + continue; + if ((headbuf[1] == 'T' && headbuf[2] == 'H') || + (headbuf[1] == 't' && headbuf[2] == 'h')) + break; + if (headbuf[1] == 'D' && headbuf[2] == 't') { + if (typeflag) { + printf("%-60s NEW\n", pathname); + return; + } + goto newman; + } + } + if (typeflag) { + printf("%-60s OLD\n", pathname); + return; + } + for (;;) { + if (fgets(linbuf, sizeof linbuf, stdin) == NULL) + return; + if (linbuf[0] != '.') + continue; + if (linbuf[1] == 'S' && linbuf[2] == 'H') + break; + if (linbuf[1] == 's' && linbuf[2] == 'h') + break; + } + trimln(headbuf); + if (tocrc) + doname(name); + if (!tocrc && !intro) + printf("%s\t", headbuf); + linbuf[0] = '\0'; + for (;;) { + if (fgets(headbuf, sizeof headbuf, stdin) == NULL) + break; + if (headbuf[0] == '.') { + if (headbuf[1] == 'S' && headbuf[2] == 'H') + break; + if (headbuf[1] == 's' && headbuf[2] == 'h') + break; + } + if (i != 0) + strcat(linbuf, " "); + i++; + trimln(headbuf); + strcat(linbuf, headbuf); + } + if (intro) + split(linbuf, name); + else + printf("%s\n", linbuf); + return; + +newman: + for (;;) { + if (fgets(linbuf, sizeof linbuf, stdin) == NULL) + return; + if (linbuf[0] != '.') + continue; + if (linbuf[1] == 'S' && linbuf[2] == 'h') + break; + } + trimln(headbuf); + if (tocrc) + doname(name); + if (!tocrc && !intro) + printf(".TH%s\t", &headbuf[3]); + linbuf[0] = '\0'; + for (;;) { + if (fgets(headbuf, sizeof headbuf, stdin) == NULL) + break; + if (headbuf[0] == '.') { + if (headbuf[1] == 'S' && headbuf[2] == 'h') + break; + } + if (i != 0) + strcat(linbuf, " "); + i++; + trimln(headbuf); + for (loc = strchr(headbuf, ' '); loc; loc = strchr(loc, ' ')) + if (loc[1] == ',') + strcpy(loc, &loc[1]); + else + loc++; + if (headbuf[0] != '.') { + strcat(linbuf, headbuf); + } else { + /* + * Get rid of quotes in macros. + */ + for (loc = strchr(&headbuf[4], '"'); loc; ) { + strcpy(loc, &loc[1]); + loc = strchr(loc, '"'); + } + /* + * Handle cross references + */ + if (headbuf[1] == 'X' && headbuf[2] == 'r') { + for (loc = &headbuf[4]; *loc != ' '; loc++) + continue; + loc[0] = '('; + loc[2] = ')'; + loc[3] = '\0'; + } + /* + * Put dash between names and description. + */ + if (headbuf[1] == 'N' && headbuf[2] == 'd') + strcat(linbuf, "\\- "); + /* + * Skip over macro names. + */ + strcat(linbuf, &headbuf[4]); + } + } + if (intro) + split(linbuf, name); + else + printf("%s\n", linbuf); +} + +void +trimln(cp) + register char *cp; +{ + + while (*cp) + cp++; + if (*--cp == '\n') + *cp = 0; +} + +void +doname(name) + char *name; +{ + register char *dp = name, *ep; + +again: + while (*dp && *dp != '.') + putchar(*dp++); + if (*dp) + for (ep = dp+1; *ep; ep++) + if (*ep == '.') { + putchar(*dp++); + goto again; + } + putchar('('); + if (*dp) + dp++; + while (*dp) + putchar (*dp++); + putchar(')'); + putchar(' '); +} + +void +split(line, name) + char *line, *name; +{ + register char *cp, *dp; + char *sp, *sep; + + cp = strchr(line, '-'); + if (cp == 0) + return; + sp = cp + 1; + for (--cp; *cp == ' ' || *cp == '\t' || *cp == '\\'; cp--) + ; + *++cp = '\0'; + while (*sp && (*sp == ' ' || *sp == '\t')) + sp++; + for (sep = "", dp = line; dp && *dp; dp = cp, sep = "\n") { + cp = strchr(dp, ','); + if (cp) { + register char *tp; + + for (tp = cp - 1; *tp == ' ' || *tp == '\t'; tp--) + ; + *++tp = '\0'; + for (++cp; *cp == ' ' || *cp == '\t'; cp++) + ; + } + printf("%s%s\t", sep, dp); + dorefname(name); + printf("\t%s", sp); + } +} + +void +dorefname(name) + char *name; +{ + register char *dp = name, *ep; + +again: + while (*dp && *dp != '.') + putchar(*dp++); + if (*dp) + for (ep = dp+1; *ep; ep++) + if (*ep == '.') { + putchar(*dp++); + goto again; + } + putchar('.'); + if (*dp) + dp++; + while (*dp) + putchar (*dp++); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: getNAME [-it] file ...\n"); + exit(1); +} diff --git a/libexec/getty/Makefile b/libexec/getty/Makefile new file mode 100644 index 0000000..8113441 --- /dev/null +++ b/libexec/getty/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 5.12 (Berkeley) 1/21/91 + +PROG= getty +SRCS= main.c init.c subr.c gettytab.c +DPADD= ${LIBUTIL} +LDADD= -lutil +MAN5= gettytab.5 ttys.5 +MAN8= getty.8 + +.include <bsd.prog.mk> diff --git a/libexec/getty/extern.h b/libexec/getty/extern.h new file mode 100644 index 0000000..644f93f --- /dev/null +++ b/libexec/getty/extern.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/4/93 + */ + +struct delayval; + +int adelay __P((int, struct delayval *)); +char *autobaud __P((void)); +int delaybits __P((void)); +void edithost __P((char *)); +void gendefaults __P((void)); +int getent __P((char *, char *)); +int getflag __P((char *)); +long getnum __P((char *)); +char *getstr __P((char *, char **)); +void gettable __P((char *, char *)); +void makeenv __P((char *[])); +char *portselector __P((void)); +void set_ttydefaults __P((int)); +void setchars __P((void)); +void setdefaults __P((void)); +long setflags __P((int)); +int speed __P((int)); + +int login_tty __P((int)); /* From libutil. */ diff --git a/libexec/getty/fbtab_stuff.c b/libexec/getty/fbtab_stuff.c new file mode 100644 index 0000000..2ae5394 --- /dev/null +++ b/libexec/getty/fbtab_stuff.c @@ -0,0 +1,93 @@ +#include <sys/types.h> +#include <stdio.h> +#include <syslog.h> +#include <string.h> +#include <errno.h> +#include <dirent.h> + +#include "pathnames.h" + +#define WSPACE " \t\n" + +void reset_fbtab __P((char *tty)); +void reset_protect __P((char *table, char *path, int mask)); + +/* + * reset_fbtab - reset ownership to root/wheel and apply protections + * specified in /etc/fbtab or logindevperm + */ + +void +reset_fbtab(tty) +char *tty; +{ + FILE *fp; + char buf[BUFSIZ]; + char *devname; + char *cp; + int prot; + char *table; + + if ((fp = fopen(table = _PATH_FBTAB, "r")) == 0 + && (fp = fopen(table = _PATH_LOGINDEVPERM, "r")) == 0) + return; + + while (fgets(buf, sizeof(buf), fp)) { + if (cp = strchr(buf, '#')) + *cp = 0; /* strip comment */ + if ((cp = devname = strtok(buf, WSPACE)) == 0) + continue; /* empty or comment */ + if (strncmp(devname, "/dev/", 5) != 0 + || (cp = strtok((char *) 0, WSPACE)) == 0 + || *cp != '0' + || sscanf(cp, "%o", &prot) == 0 + || prot == 0 + || (prot & 0777) != prot + || (cp = strtok((char *) 0, WSPACE)) == 0) { + syslog(LOG_ERR, "%s: bad entry: %s", table, cp ? cp : "(null)"); + continue; + } + if (strcmp(devname, tty) == 0) { + for (cp = strtok(cp, ":"); cp; cp = strtok((char *) 0, ":")) { + reset_protect(table, cp, prot); + } + } + } + fclose(fp); +} + +/* reset_protect - protect one device entry */ + +void +reset_protect(table, path, mask) +char *table; +char *path; +int mask; +{ + char buf[BUFSIZ]; + int pathlen = strlen(path); + struct dirent *ent; + DIR *dir; + + if (strcmp("/*", path + pathlen - 2) != 0) { + if (chmod(path, mask) && errno != ENOENT) + syslog(LOG_ERR, "%s: chmod(%s): %m", table, path); + if (chown(path, 0, 0) && errno != ENOENT) + syslog(LOG_ERR, "%s: chown(%s): %m", table, path); + } else { + strcpy(buf, path); + buf[pathlen - 1] = 0; + if ((dir = opendir(buf)) == 0) { + syslog(LOG_ERR, "%s: opendir(%s): %m", table, path); + } else { + while ((ent = readdir(dir)) != 0) { + if (strcmp(ent->d_name, ".") != 0 + && strcmp(ent->d_name, "..") != 0) { + strcpy(buf + pathlen - 1, ent->d_name); + reset_protect(table, buf, mask); + } + } + closedir(dir); + } + } +} diff --git a/libexec/getty/getty.8 b/libexec/getty/getty.8 new file mode 100644 index 0000000..ce6274b --- /dev/null +++ b/libexec/getty/getty.8 @@ -0,0 +1,127 @@ +.\" Copyright (c) 1980, 1991 Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)getty.8 6.6 (Berkeley) 4/25/91 +.\" +.Dd April 25, 1991 +.Dt GETTY 8 +.Os BSD 4 +.Sh NAME +.Nm getty +.Nd set terminal mode +.Sh SYNOPSIS +.Nm getty +.Oo +.Ar type +.Op Ar tty +.Oc +.Sh DESCRIPTION +The +.Nm getty +program +is called by +.Xr init 8 +to open and initialize the tty line, read a login name, and invoke +.Xr login 1 . +.Pp +The argument +.Ar tty +is the special device file in +.Pa /dev +to open for the terminal (for example, ``ttyh0''). +If there is no argument or the argument is +.Ql Fl , +the tty line is assumed to be open as file descriptor 0. +.Pp +The +.Ar type +argument can be used to make +.Nm getty +treat the terminal line specially. +This argument is used as an index into the +.Nm gettytab 5 +database, to determine the characteristics of the line. +If there is no argument, or there is no such table, the +.Em default +table is used. +If there is no +.Pa /etc/gettytab +a set of system defaults is used. +If indicated by the table located, +.Nm getty +will clear the terminal screen, +print a banner heading, +and prompt for a login name. +Usually either the banner or the login prompt will include +the system hostname. +.Pp +Most of the default actions of +.Nm getty +can be circumvented, or modified, by a suitable +.Nm gettytab +table. +.Pp +The +.Nm getty +program +can be set to timeout after some interval, +which will cause dial up lines to hang up +if the login name is not entered reasonably quickly. +.Sh DIAGNOSTICS +.Bl -diag +.It "ttyxx: No such device or address." +.It "ttyxx: No such file or address." +A terminal which is turned +on in the +.Xr ttys +file cannot be opened, likely because the requisite +lines are either not configured into the system, the associated device +was not attached during boot-time system configuration, +or the special file in +.Pa /dev +does not exist. +.El +.Sh FILES +.Bl -tag -width /etc/gettytab -compact +.It Pa /etc/gettytab +.El +.Sh SEE ALSO +.Xr gettytab 5 , +.Xr init 8 , +.Xr login 1 , +.Xr ioctl 2 , +.Xr tty 4 , +.Xr ttys 5 +.Sh HISTORY +A +.Nm getty +program appeared in +.At v6 . diff --git a/libexec/getty/gettytab.5 b/libexec/getty/gettytab.5 new file mode 100644 index 0000000..db6b1cc --- /dev/null +++ b/libexec/getty/gettytab.5 @@ -0,0 +1,324 @@ +.\" Copyright (c) 1983, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)gettytab.5 6.7 (Berkeley) 5/10/91 +.\" +.Dd May 10, 1991 +.Dt GETTYTAB 5 +.Os BSD 4.2 +.Sh NAME +.Nm gettytab +.Nd terminal configuration data base +.Sh SYNOPSIS +.Nm gettytab +.Sh DESCRIPTION +The +.Nm gettytab +file +is a simplified version of the +.Xr termcap 5 +data base +used to describe terminal lines. +The initial terminal login process +.Xr getty 8 +accesses the +.Nm gettytab +file each time it starts, allowing simpler +reconfiguration of terminal characteristics. +Each entry in the data base +is used to describe one class of terminals. +.Pp +There is a default terminal class, +.Em default , +that is used to set global defaults for all other classes. +(That is, the +.Em default +entry is read, then the entry for the class required +is used to override particular settings.) +.Sh CAPABILITIES +Refer to +.Xr termcap 5 +for a description of the file layout. +The +.Em default +column below lists defaults obtained if there is +no entry in the table obtained, nor one in the special +.Em default +table. +.Bl -column Namexx /usr/bin/login Default +.It Sy Name Type Default Description +.It "ap bool false terminal uses any parity" +.It "bd num 0 backspace delay" +.It "bk str 0377 alternate end of line character (input break)" +.It "cb bool false use crt backspace mode" +.It "cd num 0 carriage-return delay" +.It "ce bool false use crt erase algorithm" +.It "ck bool false use crt kill algorithm" +.It "cl str" Ta Dv NULL Ta +.No "screen clear sequence" +.It "co bool false console - add" +.Ql \en +after login prompt +.It "ds str" Ta So Li ^Y Sc Ta +.No "delayed suspend character" +.It "dx bool false set" +.Dv DECCTLQ +.It "ec bool false leave echo" +.Tn OFF +.It "ep bool false terminal uses even parity" +.It "er str" Ta So Li ^? Sc Ta +.No "erase character" +.It "et str" Ta So Li ^D Sc Ta +.No "end of text" +.Pq Dv EOF +character +.It "ev str" Ta Dv NULL Ta +.No "initial environment" +.It "f0 num unused tty mode flags to write messages" +.It "f1 num unused tty mode flags to read login name" +.It "f2 num unused tty mode flags to leave terminal as" +.It "fd num 0 form-feed (vertical motion) delay" +.It "fl str" Ta So Li ^O Sc Ta +.No "output flush character" +.It "hc bool false do" +.Tn NOT +hangup line on last close +.It "he str" Ta Dv NULL Ta +.No "hostname editing string" +.It "hn str hostname hostname" +.It "ht bool false terminal has real tabs" +.It "ig bool false ignore garbage characters in login name" +.It "im str" Ta Dv NULL Ta +.No "initial (banner) message" +.It "in str" Ta So Li ^C Sc Ta +.No "interrupt character" +.It "is num unused input speed" +.It "kl str" Ta So Li ^U Sc Ta +.No "kill character" +.It "lc bool false terminal has lower case" +.It "lm str login: login prompt" +.It "ln str" Ta So Li ^V Sc Ta +.No "``literal next'' character" +.It "lo str" Ta Pa /usr/bin/login Ta +.No "program to exec when name obtained" +.It "nd num 0 newline (line-feed) delay" +.It "nl bool false terminal has (or might have) a newline character" +.It "np bool false terminal uses no parity (8bit chars)" +.It "nx str default next table (for auto speed selection)" +.It "op bool false terminal uses odd parity" +.It "os num unused output speed" +.It "pc str" Ta So Li \e0 Sc Ta +.No "pad character" +.It "pe bool false use printer (hard copy) erase algorithm" +.It "pf num 0 delay" +between first prompt and following flush (seconds) +.It "ps bool false line connected to a" +.Tn MICOM +port selector +.It "qu str" Ta So Li \&^\e Sc Ta +.No "quit character" +.It "rp str" Ta So Li ^R Sc Ta +.No "line retype character" +.It "rw bool false do" +.Tn NOT +use raw for input, use cbreak +.It "sp num unused line speed (input and output)" +.It "su str" Ta So Li ^Z Sc Ta +.No "suspend character" +.It "tc str none table continuation" +.It "to num 0 timeout (seconds)" +.It "tt str" Ta Dv NULL Ta +.No "terminal type (for environment)" +.It "ub bool false do unbuffered output (of prompts etc)" +.It "uc bool false terminal is known upper case only" +.It "we str" Ta So Li ^W Sc Ta +.No "word erase character" +.It "xc bool false do +.Tn NOT +echo control chars as +.Ql ^X +.It "xf str" Ta So Li ^S Sc Ta Dv XOFF +(stop output) character +.It "xn str" Ta So Li ^Q Sc Ta Dv XON +(start output) character +.El +.Pp +If no line speed is specified, speed will not be altered +from that which prevails when getty is entered. +Specifying an input or output speed will override +line speed for stated direction only. +.Pp +Terminal modes to be used for the output of the message, +for input of the login name, +and to leave the terminal set as upon completion, +are derived from the boolean flags specified. +If the derivation should prove inadequate, +any (or all) of these three may be overridden +with one of the +.Em \&f0 , +.Em \&f1 , +or +.Em \&f2 +numeric specifications, which can be used to specify +(usually in octal, with a leading '0') +the exact values of the flags. +Local (new tty) flags are set in the top 16 bits +of this (32 bit) value. +.Pp +Should +.Xr getty +receive a null character +(presumed to indicate a line break) +it will restart using the table indicated by the +.Em nx +entry. If there is none, it will re-use its original table. +.Pp +Delays are specified in milliseconds, the nearest possible +delay available in the tty driver will be used. +Should greater certainty be desired, delays +with values 0, 1, 2, and 3 are interpreted as +choosing that particular delay algorithm from the driver. +.Pp +The +.Em \&cl +screen clear string may be preceded by a (decimal) number +of milliseconds of delay required (a la termcap). +This delay is simulated by repeated use of the pad character +.Em \&pc . +.Pp +The initial message, and login message, +.Em \&im +and +.Em \&lm +may include the character sequence +.Em \&%h +or +.Em \&%t +to obtain +the hostname or tty name respectively. +.Pf ( Em %% +obtains a single '%' character.) +The hostname is normally obtained from the system, +but may be set by the +.Em \&hn +table entry. +In either case it may be edited with +.Em \&he . +The +.Em \&he +string is a sequence of characters, each character that +is neither '@' nor '#' is copied into the final hostname. +A '@' in the +.Em \&he +string, causes one character from the real hostname to +be copied to the final hostname. +A '#' in the +.Em \&he +string, causes the next character of the real hostname +to be skipped. +Surplus '@' and '#' characters are ignored. +.Pp +When getty execs the login process, given +in the +.Em \&lo +string (usually +.Dq Pa /usr/bin/login ) , +it will have set +the environment to include the terminal type, as indicated +by the +.Em \&tt +string (if it exists). +The +.Em \&ev +string, can be used to enter additional data into +the environment. +It is a list of comma separated strings, each of which +will presumably be of the form +.Em name=value . +.Pp +If a non-zero timeout is specified, with +.Em \&to , +then getty will exit within the indicated +number of seconds, either having +received a login name and passed control +to +.Xr login , +or having received an alarm signal, and exited. +This may be useful to hangup dial in lines. +.Pp +Output from +.Xr getty +is even parity unless +.Em \&op +is specified. +The +.Em \&op +string +may be specified with +.Em \&ap +to allow any parity on input, but generate odd parity output. +Note: this only applies while getty is being run, +terminal driver limitations prevent a more complete +implementation. +.Xr Getty +does not check parity of input characters in +.Dv RAW +mode. +.Sh SEE ALSO +.Xr login 1 , +.Xr termcap 5 , +.Xr getty 8 . +.Sh BUGS +The special characters (erase, kill, etc.) are reset to system defaults +by +.Xr login 1 . +In +.Em all +cases, '#' or '^H' typed in a login name will be treated as +an erase character, and '@' will be treated as a kill character. +.Pp +The delay stuff is a real crock. +Apart form its general lack of flexibility, some +of the delay algorithms are not implemented. +The terminal driver should support sane delay settings. +.Pp +The +.Em \&he +capability is stupid. +.Pp +The +.Xr termcap +format is horrid, something more rational should +have been chosen. +.Sh HISTORY +The +.Nm gettytab +file format appeared in 4.2BSD. diff --git a/libexec/getty/gettytab.c b/libexec/getty/gettytab.c new file mode 100644 index 0000000..a864529 --- /dev/null +++ b/libexec/getty/gettytab.c @@ -0,0 +1,339 @@ +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)gettytab.c 5.5 (Berkeley) 2/25/91"; +#endif /* not lint */ + +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include "pathnames.h" + +#define TABBUFSIZ 512 + +static char *tbuf; +int hopcount; /* detect infinite loops in termcap, init 0 */ +static char *skip(); +char *getstr(); +static char *decode(); + +/* + * Get an entry for terminal name in buffer bp, + * from the termcap file. Parse is very rudimentary; + * we just notice escaped newlines. + */ +getent(bp, name) + char *bp, *name; +{ + register char *cp; + register int c; + register int i = 0, cnt = 0; + char ibuf[TABBUFSIZ]; + char *cp2; + int tf; + + tbuf = bp; + tf = open(_PATH_GETTYTAB, O_RDONLY, 0); + if (tf < 0) + return (-1); + for (;;) { + cp = bp; + for (;;) { + if (i == cnt) { + cnt = read(tf, ibuf, TABBUFSIZ); + if (cnt <= 0) { + close(tf); + return (0); + } + i = 0; + } + c = ibuf[i++]; + if (c == '\n') { + if (cp > bp && cp[-1] == '\\'){ + cp--; + continue; + } + break; + } + if (cp >= bp+TABBUFSIZ) { + write(2,"Gettytab entry too long\n", 24); + break; + } else + *cp++ = c; + } + *cp = 0; + + /* + * The real work for the match. + */ + if (namatch(name)) { + close(tf); + return(nchktc()); + } + } +} + +/* + * tnchktc: check the last entry, see if it's tc=xxx. If so, + * recursively find xxx and append that entry (minus the names) + * to take the place of the tc=xxx entry. This allows termcap + * entries to say "like an HP2621 but doesn't turn on the labels". + * Note that this works because of the left to right scan. + */ +#define MAXHOP 32 +nchktc() +{ + register char *p, *q; + char tcname[16]; /* name of similar terminal */ + char tcbuf[TABBUFSIZ]; + char *holdtbuf = tbuf; + int l; + + p = tbuf + strlen(tbuf) - 2; /* before the last colon */ + while (*--p != ':') + if (p<tbuf) { + write(2, "Bad gettytab entry\n", 19); + return (0); + } + p++; + /* p now points to beginning of last field */ + if (p[0] != 't' || p[1] != 'c') + return(1); + strcpy(tcname,p+3); + q = tcname; + while (q && *q != ':') + q++; + *q = 0; + if (++hopcount > MAXHOP) { + write(2, "Getty: infinite tc= loop\n", 25); + return (0); + } + if (getent(tcbuf, tcname) != 1) + return(0); + for (q=tcbuf; *q != ':'; q++) + ; + l = p - holdtbuf + strlen(q); + if (l > TABBUFSIZ) { + write(2, "Gettytab entry too long\n", 24); + q[TABBUFSIZ - (p-tbuf)] = 0; + } + strcpy(p, q+1); + tbuf = holdtbuf; + return(1); +} + +/* + * Tnamatch deals with name matching. The first field of the termcap + * entry is a sequence of names separated by |'s, so we compare + * against each such name. The normal : terminator after the last + * name (before the first field) stops us. + */ +namatch(np) + char *np; +{ + register char *Np, *Bp; + + Bp = tbuf; + if (*Bp == '#') + return(0); + for (;;) { + for (Np = np; *Np && *Bp == *Np; Bp++, Np++) + continue; + if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) + return (1); + while (*Bp && *Bp != ':' && *Bp != '|') + Bp++; + if (*Bp == 0 || *Bp == ':') + return (0); + Bp++; + } +} + +/* + * Skip to the next field. Notice that this is very dumb, not + * knowing about \: escapes or any such. If necessary, :'s can be put + * into the termcap file in octal. + */ +static char * +skip(bp) + register char *bp; +{ + + while (*bp && *bp != ':') + bp++; + if (*bp == ':') + bp++; + return (bp); +} + +/* + * Return the (numeric) option id. + * Numeric options look like + * li#80 + * i.e. the option string is separated from the numeric value by + * a # character. If the option is not found we return -1. + * Note that we handle octal numbers beginning with 0. + */ +long +getnum(id) + char *id; +{ + register long i, base; + register char *bp = tbuf; + + for (;;) { + bp = skip(bp); + if (*bp == 0) + return (-1); + if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) + continue; + if (*bp == '@') + return(-1); + if (*bp != '#') + continue; + bp++; + base = 10; + if (*bp == '0') + base = 8; + i = 0; + while (isdigit(*bp)) + i *= base, i += *bp++ - '0'; + return (i); + } +} + +/* + * Handle a flag option. + * Flag options are given "naked", i.e. followed by a : or the end + * of the buffer. Return 1 if we find the option, or 0 if it is + * not given. + */ +getflag(id) + char *id; +{ + register char *bp = tbuf; + + for (;;) { + bp = skip(bp); + if (!*bp) + return (-1); + if (*bp++ == id[0] && *bp != 0 && *bp++ == id[1]) { + if (!*bp || *bp == ':') + return (1); + else if (*bp == '!') + return (0); + else if (*bp == '@') + return(-1); + } + } +} + +/* + * Get a string valued option. + * These are given as + * cl=^Z + * Much decoding is done on the strings, and the strings are + * placed in area, which is a ref parameter which is updated. + * No checking on area overflow. + */ +char * +getstr(id, area) + char *id, **area; +{ + register char *bp = tbuf; + + for (;;) { + bp = skip(bp); + if (!*bp) + return (0); + if (*bp++ != id[0] || *bp == 0 || *bp++ != id[1]) + continue; + if (*bp == '@') + return(0); + if (*bp != '=') + continue; + bp++; + return (decode(bp, area)); + } +} + +/* + * Tdecode does the grung work to decode the + * string capability escapes. + */ +static char * +decode(str, area) + register char *str; + char **area; +{ + register char *cp; + register int c; + register char *dp; + int i; + + cp = *area; + while ((c = *str++) && c != ':') { + switch (c) { + + case '^': + c = *str++ & 037; + break; + + case '\\': + dp = "E\033^^\\\\::n\nr\rt\tb\bf\f"; + c = *str++; +nextc: + if (*dp++ == c) { + c = *dp++; + break; + } + dp++; + if (*dp) + goto nextc; + if (isdigit(c)) { + c -= '0', i = 2; + do + c <<= 3, c |= *str++ - '0'; + while (--i && isdigit(*str)); + } + break; + } + *cp++ = c; + } + *cp++ = 0; + str = *area; + *area = cp; + return (str); +} diff --git a/libexec/getty/gettytab.h b/libexec/getty/gettytab.h new file mode 100644 index 0000000..6f393ce --- /dev/null +++ b/libexec/getty/gettytab.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)gettytab.h 5.5 (Berkeley) 3/27/91 + */ + +/* + * Getty description definitions. + */ +struct gettystrs { + char *field; /* name to lookup in gettytab */ + char *defalt; /* value we find by looking in defaults */ + char *value; /* value that we find there */ +}; + +struct gettynums { + char *field; /* name to lookup */ + long defalt; /* number we find in defaults */ + long value; /* number we find there */ + int set; /* we actually got this one */ +}; + +struct gettyflags { + char *field; /* name to lookup */ + char invrt; /* name existing in gettytab --> false */ + char defalt; /* true/false in defaults */ + char value; /* true/false flag */ + char set; /* we found it */ +}; + +/* + * String values. + */ +#define NX gettystrs[0].value +#define CL gettystrs[1].value +#define IM gettystrs[2].value +#define LM gettystrs[3].value +#define ER gettystrs[4].value +#define KL gettystrs[5].value +#define ET gettystrs[6].value +#define PC gettystrs[7].value +#define TT gettystrs[8].value +#define EV gettystrs[9].value +#define LO gettystrs[10].value +#define HN gettystrs[11].value +#define HE gettystrs[12].value +#define IN gettystrs[13].value +#define QU gettystrs[14].value +#define XN gettystrs[15].value +#define XF gettystrs[16].value +#define BK gettystrs[17].value +#define SU gettystrs[18].value +#define DS gettystrs[19].value +#define RP gettystrs[20].value +#define FL gettystrs[21].value +#define WE gettystrs[22].value +#define LN gettystrs[23].value + +/* + * Numeric definitions. + */ +#define IS gettynums[0].value +#define OS gettynums[1].value +#define SP gettynums[2].value +#define ND gettynums[3].value +#define CD gettynums[4].value +#define TD gettynums[5].value +#define FD gettynums[6].value +#define BD gettynums[7].value +#define TO gettynums[8].value +#define F0 gettynums[9].value +#define F0set gettynums[9].set +#define F1 gettynums[10].value +#define F1set gettynums[10].set +#define F2 gettynums[11].value +#define F2set gettynums[11].set +#define PF gettynums[12].value + +/* + * Boolean values. + */ +#define HT gettyflags[0].value +#define NL gettyflags[1].value +#define EP gettyflags[2].value +#define EPset gettyflags[2].set +#define OP gettyflags[3].value +#define OPset gettyflags[3].set +#define AP gettyflags[4].value +#define APset gettyflags[4].set +#define EC gettyflags[5].value +#define CO gettyflags[6].value +#define CB gettyflags[7].value +#define CK gettyflags[8].value +#define CE gettyflags[9].value +#define PE gettyflags[10].value +#define RW gettyflags[11].value +#define XC gettyflags[12].value +#define LC gettyflags[13].value +#define UC gettyflags[14].value +#define IG gettyflags[15].value +#define PS gettyflags[16].value +#define HC gettyflags[17].value +#define UB gettyflags[18].value +#define AB gettyflags[19].value +#define DX gettyflags[20].value +#define NP gettyflags[21].value + +int getent(); +long getnum(); +int getflag(); +char *getstr(); + +extern struct gettyflags gettyflags[]; +extern struct gettynums gettynums[]; +extern struct gettystrs gettystrs[]; +extern int hopcount; diff --git a/libexec/getty/init.c b/libexec/getty/init.c new file mode 100644 index 0000000..ac7112f --- /dev/null +++ b/libexec/getty/init.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)init.c 5.6 (Berkeley) 3/27/91"; +#endif /* not lint */ + +/* + * Getty table initializations. + * + * Melbourne getty. + */ +#include <sgtty.h> +#include "gettytab.h" +#include "pathnames.h" + +extern struct sgttyb tmode; +extern struct tchars tc; +extern struct ltchars ltc; +extern char hostname[]; + +struct gettystrs gettystrs[] = { + { "nx" }, /* next table */ + { "cl" }, /* screen clear characters */ + { "im" }, /* initial message */ + { "lm", "login: " }, /* login message */ + { "er", &tmode.sg_erase }, /* erase character */ + { "kl", &tmode.sg_kill }, /* kill character */ + { "et", &tc.t_eofc }, /* eof chatacter (eot) */ + { "pc", "" }, /* pad character */ + { "tt" }, /* terminal type */ + { "ev" }, /* enviroment */ + { "lo", _PATH_LOGIN }, /* login program */ + { "hn", hostname }, /* host name */ + { "he" }, /* host name edit */ + { "in", &tc.t_intrc }, /* interrupt char */ + { "qu", &tc.t_quitc }, /* quit char */ + { "xn", &tc.t_startc }, /* XON (start) char */ + { "xf", &tc.t_stopc }, /* XOFF (stop) char */ + { "bk", &tc.t_brkc }, /* brk char (alt \n) */ + { "su", <c.t_suspc }, /* suspend char */ + { "ds", <c.t_dsuspc }, /* delayed suspend */ + { "rp", <c.t_rprntc }, /* reprint char */ + { "fl", <c.t_flushc }, /* flush output */ + { "we", <c.t_werasc }, /* word erase */ + { "ln", <c.t_lnextc }, /* literal next */ + { 0 } +}; + +struct gettynums gettynums[] = { + { "is" }, /* input speed */ + { "os" }, /* output speed */ + { "sp" }, /* both speeds */ + { "nd" }, /* newline delay */ + { "cd" }, /* carriage-return delay */ + { "td" }, /* tab delay */ + { "fd" }, /* form-feed delay */ + { "bd" }, /* backspace delay */ + { "to" }, /* timeout */ + { "f0" }, /* output flags */ + { "f1" }, /* input flags */ + { "f2" }, /* user mode flags */ + { "pf" }, /* delay before flush at 1st prompt */ + { 0 } +}; + +struct gettyflags gettyflags[] = { + { "ht", 0 }, /* has tabs */ + { "nl", 1 }, /* has newline char */ + { "ep", 0 }, /* even parity */ + { "op", 0 }, /* odd parity */ + { "ap", 0 }, /* any parity */ + { "ec", 1 }, /* no echo */ + { "co", 0 }, /* console special */ + { "cb", 0 }, /* crt backspace */ + { "ck", 0 }, /* crt kill */ + { "ce", 0 }, /* crt erase */ + { "pe", 0 }, /* printer erase */ + { "rw", 1 }, /* don't use raw */ + { "xc", 1 }, /* don't ^X ctl chars */ + { "lc", 0 }, /* terminal las lower case */ + { "uc", 0 }, /* terminal has no lower case */ + { "ig", 0 }, /* ignore garbage */ + { "ps", 0 }, /* do port selector speed select */ + { "hc", 1 }, /* don't set hangup on close */ + { "ub", 0 }, /* unbuffered output */ + { "ab", 0 }, /* auto-baud detect with '\r' */ + { "dx", 0 }, /* set decctlq */ + { "np", 0 }, /* no parity at all (8bit chars) */ + { 0 } +}; diff --git a/libexec/getty/main.c b/libexec/getty/main.c new file mode 100644 index 0000000..a161ed5 --- /dev/null +++ b/libexec/getty/main.c @@ -0,0 +1,502 @@ +/*- + * Copyright (c) 1980 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1980 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 5.16 (Berkeley) 3/27/91"; +#endif /* not lint */ + +#define USE_OLD_TTY + +#include <sys/param.h> +#include <sys/stat.h> +#include <signal.h> +#include <fcntl.h> +#include <sgtty.h> +#include <time.h> +#include <ctype.h> +#include <setjmp.h> +#include <syslog.h> +#include <unistd.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include "gettytab.h" +#include "pathnames.h" + +struct sgttyb tmode = { + 0, 0, CERASE, CKILL, 0 +}; +struct tchars tc = { + CINTR, CQUIT, CSTART, + CSTOP, CEOF, CBRK, +}; +struct ltchars ltc = { + CSUSP, CDSUSP, CRPRNT, + CFLUSH, CWERASE, CLNEXT +}; + +int crmod, digit, lower, upper; + +char hostname[MAXHOSTNAMELEN]; +char name[16]; +char dev[] = _PATH_DEV; +char ttyn[32]; +char *portselector(); +char *ttyname(); + +#define OBUFSIZ 128 +#define TABBUFSIZ 512 + +char defent[TABBUFSIZ]; +char defstrs[TABBUFSIZ]; +char tabent[TABBUFSIZ]; +char tabstrs[TABBUFSIZ]; + +char *env[128]; + +char partab[] = { + 0001,0201,0201,0001,0201,0001,0001,0201, + 0202,0004,0003,0205,0005,0206,0201,0001, + 0201,0001,0001,0201,0001,0201,0201,0001, + 0001,0201,0201,0001,0201,0001,0001,0201, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0000,0200,0200,0000,0200,0000,0000,0200, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0200,0000,0000,0200,0000,0200,0200,0000, + 0000,0200,0200,0000,0200,0000,0000,0201 +}; + +#define ERASE tmode.sg_erase +#define KILL tmode.sg_kill +#define EOT tc.t_eofc + +jmp_buf timeout; + +static void +dingdong() +{ + + alarm(0); + signal(SIGALRM, SIG_DFL); + longjmp(timeout, 1); +} + +jmp_buf intrupt; + +static void +interrupt() +{ + + signal(SIGINT, interrupt); + longjmp(intrupt, 1); +} + +main(argc, argv) + int argc; + char **argv; +{ + extern char **environ; + char *tname; + int repcnt = 0; + + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + + openlog("getty", LOG_ODELAY|LOG_CONS, LOG_AUTH); + gethostname(hostname, sizeof(hostname)); + if (hostname[0] == '\0') + strcpy(hostname, "Amnesiac"); + /* + * The following is a work around for vhangup interactions + * which cause great problems getting window systems started. + * If the tty line is "-", we do the old style getty presuming + * that the file descriptors are already set up for us. + * J. Gettys - MIT Project Athena. + */ + if (argc <= 2 || strcmp(argv[2], "-") == 0) + strcpy(ttyn, ttyname(0)); + else { + int i; + + strcpy(ttyn, dev); + strncat(ttyn, argv[2], sizeof(ttyn)-sizeof(dev)); + if (strcmp(argv[0], "+") != 0) { + chown(ttyn, 0, 0); + chmod(ttyn, 0600); + revoke(ttyn); + while ((i = open(ttyn, O_RDWR)) == -1) { + if (repcnt % 10 == 0) { + syslog(LOG_ERR, "%s: %m", ttyn); + closelog(); + } + repcnt++; + sleep(60); + } + login_tty(i); + } + } + + gettable("default", defent, defstrs); + gendefaults(); + tname = "default"; + if (argc > 1) + tname = argv[1]; + for (;;) { + int off = 0; + int flushboth = 0; + struct sgttyb fake; + + gettable(tname, tabent, tabstrs); + if (OPset || EPset || APset) + APset++, OPset++, EPset++; + setdefaults(); + ioctl(0, TIOCFLUSH, &flushboth); /* clear out the crap */ + ioctl(0, FIONBIO, &off); /* turn off non-blocking mode */ + ioctl(0, FIOASYNC, &off); /* ditto for async mode */ + ioctl(0, TIOCGETP, &fake); /* initialize kernel termios */ + if (IS) + tmode.sg_ispeed = speed(IS); + else if (SP) + tmode.sg_ispeed = speed(SP); + if (OS) + tmode.sg_ospeed = speed(OS); + else if (SP) + tmode.sg_ospeed = speed(SP); + set_tmode(0); + setchars(); + ioctl(0, TIOCSETC, &tc); + if (HC) + ioctl(0, TIOCHPCL, 0); + if (AB) { + extern char *autobaud(); + + tname = autobaud(); + continue; + } + if (PS) { + tname = portselector(); + continue; + } + if (CL && *CL) + putpad(CL); + edithost(HE); + if (IM && *IM) + putf(IM); + if (setjmp(timeout)) { + tmode.sg_ispeed = tmode.sg_ospeed = 0; + ioctl(0, TIOCSETP, &tmode); + exit(1); + } + if (TO) { + signal(SIGALRM, dingdong); + alarm(TO); + } + if (getname()) { + register int i; + + alarm(0); + signal(SIGALRM, SIG_DFL); + oflush(); + if (name[0] == '-') { + puts("user names may not start with '-'."); + continue; + } + if (!(upper || lower || digit)) + continue; + set_tmode(2); + ioctl(0, TIOCSLTC, <c); + for (i = 0; environ[i] != (char *)0; i++) + env[i] = environ[i]; + makeenv(&env[i]); + + execle(LO, "login", "-p", name, (char *) 0, env); + syslog(LOG_ERR, "%s: %m", LO); + exit(1); + } + signal(SIGINT, SIG_IGN); + alarm(0); + signal(SIGALRM, SIG_DFL); + if (NX && *NX) + tname = NX; + } +} + +getname() +{ + register int c; + register char *np; + char cs; + int flushin = 1 /*FREAD*/; + + /* + * Interrupt may happen if we use CBREAK mode + */ + if (setjmp(intrupt)) { + signal(SIGINT, SIG_IGN); + return (0); + } + signal(SIGINT, interrupt); + ioctl(0, TIOCFLUSH, &flushin); /* purge any input */ + prompt(); + oflush(); + if (PF > 0) { + sleep(PF); + PF = 0; + } + set_tmode(1); + crmod = digit = lower = upper = 0; + np = name; + for (;;) { + oflush(); + if (read(STDIN_FILENO, &cs, 1) <= 0) + exit(0); + if ((c = cs&0177) == 0) + return (0); + if (c == EOT || c == 4 /*^D*/) + exit(1); + if (c == '\r' || c == '\n' || np >= &name[sizeof name]) { + putf("\r\n"); + break; + } + if (islower(c)) + lower = 1; + else if (isupper(c)) + upper = 1; + else if (c == ERASE || c == '\b' || c == 0177) { + if (np > name) { + np--; + if (tmode.sg_ospeed >= B1200) + puts("\b \b"); + else + putchr(cs); + } + continue; + } else if (c == KILL || c == 025 /*^U*/) { + putchr('\r'); + if (tmode.sg_ospeed < B1200) + putchr('\n'); + /* this is the way they do it down under ... */ + else if (np > name) + puts(" \r"); + prompt(); + np = name; + continue; + } else if (isdigit(c)) + digit++; + if (IG && (c <= ' ' || c > 0176)) + continue; + *np++ = c; + putchr(cs); + } + signal(SIGINT, SIG_IGN); + *np = 0; + if (c == '\r') + crmod = 1; + if (upper && !lower && !LC || UC) + for (np = name; *np; np++) + if (isupper(*np)) + *np = tolower(*np); + return (1); +} + +static +short tmspc10[] = { + 0, 2000, 1333, 909, 743, 666, 500, 333, 166, 83, 55, 41, 20, 10, 5, 15 +}; + +putpad(s) + register char *s; +{ + register pad = 0; + register mspc10; + + if (isdigit(*s)) { + while (isdigit(*s)) { + pad *= 10; + pad += *s++ - '0'; + } + pad *= 10; + if (*s == '.' && isdigit(s[1])) { + pad += s[1] - '0'; + s += 2; + } + } + + puts(s); + /* + * If no delay needed, or output speed is + * not comprehensible, then don't try to delay. + */ + if (pad == 0) + return; + if (tmode.sg_ospeed <= 0 || + tmode.sg_ospeed >= (sizeof tmspc10 / sizeof tmspc10[0])) + return; + + /* + * Round up by a half a character frame, and then do the delay. + * Too bad there are no user program accessible programmed delays. + * Transmitting pad characters slows many terminals down and also + * loads the system. + */ + mspc10 = tmspc10[tmode.sg_ospeed]; + pad += mspc10 / 2; + for (pad /= mspc10; pad > 0; pad--) + putchr(*PC); +} + +puts(s) + register char *s; +{ + while (*s) + putchr(*s++); +} + +char outbuf[OBUFSIZ]; +int obufcnt = 0; + +putchr(cc) +{ + char c; + + c = cc; + if (!NP) { + c |= partab[c&0177] & 0200; + if (OP) + c ^= 0200; + } + if (!UB) { + outbuf[obufcnt++] = c; + if (obufcnt >= OBUFSIZ) + oflush(); + } else + write(STDOUT_FILENO, &c, 1); +} + +oflush() +{ + if (obufcnt) + write(STDOUT_FILENO, outbuf, obufcnt); + obufcnt = 0; +} + +prompt() +{ + + putf(LM); + if (CO) + putchr('\n'); +} + +putf(cp) + register char *cp; +{ + extern char editedhost[]; + time_t t; + char *slash, db[100]; + + while (*cp) { + if (*cp != '%') { + putchr(*cp++); + continue; + } + switch (*++cp) { + + case 't': + slash = rindex(ttyn, '/'); + if (slash == (char *) 0) + puts(ttyn); + else + puts(&slash[1]); + break; + + case 'h': + puts(editedhost); + break; + + case 'd': { + static char fmt[] = "%l:% %p on %A, %d %B %Y"; + + fmt[4] = 'M'; /* I *hate* SCCS... */ + (void)time(&t); + (void)strftime(db, sizeof(db), fmt, localtime(&t)); + puts(db); + break; + } + + case '%': + putchr('%'); + break; + } + cp++; + } +} + +/* + * The conversions from sgttyb to termios make LITOUT and PASS8 affect + * the parity. So every TIOCSETP ioctl has to be paired with a TIOCLSET + * ioctl (at least if LITOUT or PASS8 has changed, and PASS8 may vary + * with 'n'). + */ +set_tmode(n) + int n; +{ + long allflags; + + allflags = setflags(n); + tmode.sg_flags = allflags & 0xffff; + allflags >>= 16; + if (n == 2) { + if (crmod || NL) + tmode.sg_flags |= CRMOD; + if (upper || UC) + tmode.sg_flags |= LCASE; + if (lower || LC) + tmode.sg_flags &= ~LCASE; + } + ioctl(0, TIOCSETP, &tmode); + ioctl(0, TIOCLSET, &allflags); +} diff --git a/libexec/getty/pathnames.h b/libexec/getty/pathnames.h new file mode 100644 index 0000000..e02b8b9 --- /dev/null +++ b/libexec/getty/pathnames.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 5.3 (Berkeley) 6/1/90 + */ + +#include <paths.h> + +#define _PATH_GETTYTAB "/etc/gettytab" +#define _PATH_LOGIN "/usr/bin/login" diff --git a/libexec/getty/subr.c b/libexec/getty/subr.c new file mode 100644 index 0000000..6e4f5f0 --- /dev/null +++ b/libexec/getty/subr.c @@ -0,0 +1,518 @@ +/* + * Copyright (c) 1983 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE + * -------------------- ----- ---------------------- + * CURRENT PATCH LEVEL: 1 00150 + * -------------------- ----- ---------------------- + * + * 22 Apr 93 Rodney W. Grimes support for 57600 and 115200 baud + * + */ + +#ifndef lint +static char sccsid[] = "@(#)subr.c 5.10 (Berkeley) 2/26/91"; +#endif /* not lint */ + +/* + * Melbourne getty. + */ +#include <sys/param.h> +#include <sgtty.h> +#include <unistd.h> +#include <string.h> +#include "gettytab.h" + +extern struct sgttyb tmode; +extern struct tchars tc; +extern struct ltchars ltc; + +/* + * Get a table entry. + */ +gettable(name, buf, area) + char *name, *buf, *area; +{ + register struct gettystrs *sp; + register struct gettynums *np; + register struct gettyflags *fp; + register n; + + hopcount = 0; /* new lookup, start fresh */ + if (getent(buf, name) != 1) + return; + + for (sp = gettystrs; sp->field; sp++) + sp->value = getstr(sp->field, &area); + for (np = gettynums; np->field; np++) { + n = getnum(np->field); + if (n == -1) + np->set = 0; + else { + np->set = 1; + np->value = n; + } + } + for (fp = gettyflags; fp->field; fp++) { + n = getflag(fp->field); + if (n == -1) + fp->set = 0; + else { + fp->set = 1; + fp->value = n ^ fp->invrt; + } + } +} + +gendefaults() +{ + register struct gettystrs *sp; + register struct gettynums *np; + register struct gettyflags *fp; + + for (sp = gettystrs; sp->field; sp++) + if (sp->value) + sp->defalt = sp->value; + for (np = gettynums; np->field; np++) + if (np->set) + np->defalt = np->value; + for (fp = gettyflags; fp->field; fp++) + if (fp->set) + fp->defalt = fp->value; + else + fp->defalt = fp->invrt; +} + +setdefaults() +{ + register struct gettystrs *sp; + register struct gettynums *np; + register struct gettyflags *fp; + + for (sp = gettystrs; sp->field; sp++) + if (!sp->value) + sp->value = sp->defalt; + for (np = gettynums; np->field; np++) + if (!np->set) + np->value = np->defalt; + for (fp = gettyflags; fp->field; fp++) + if (!fp->set) + fp->value = fp->defalt; +} + +static char ** +charnames[] = { + &ER, &KL, &IN, &QU, &XN, &XF, &ET, &BK, + &SU, &DS, &RP, &FL, &WE, &LN, 0 +}; + +static char * +charvars[] = { + &tmode.sg_erase, &tmode.sg_kill, &tc.t_intrc, + &tc.t_quitc, &tc.t_startc, &tc.t_stopc, + &tc.t_eofc, &tc.t_brkc, <c.t_suspc, + <c.t_dsuspc, <c.t_rprntc, <c.t_flushc, + <c.t_werasc, <c.t_lnextc, 0 +}; + +setchars() +{ + register int i; + register char *p; + + for (i = 0; charnames[i]; i++) { + p = *charnames[i]; + if (p && *p) + *charvars[i] = *p; + else + *charvars[i] = '\377'; + } +} + +long +setflags(n) +{ + register long f; + + switch (n) { + case 0: + if (F0set) + return(F0); + break; + case 1: + if (F1set) + return(F1); + break; + default: + if (F2set) + return(F2); + break; + } + + f = 0; + + if (AP) + f |= ANYP; + else if (OP) + f |= ODDP; + else if (EP) + f |= EVENP; + if (NP) + f |= PASS8; + + if (UC) + f |= LCASE; + + if (NL) + f |= CRMOD; + + f |= delaybits(); + + if (n == 1) { /* read mode flags */ + if (RW) + f |= RAW; + else + f |= CBREAK; + return (f); + } + + if (!HT) + f |= XTABS; + + if (n == 0) + return (f); + + if (CB) + f |= CRTBS; + + if (CE) + f |= CRTERA; + + if (CK) + f |= CRTKIL; + + if (PE) + f |= PRTERA; + + if (EC) + f |= ECHO; + + if (XC) + f |= CTLECH; + + if (DX) + f |= DECCTQ; + + return (f); +} + +struct delayval { + unsigned delay; /* delay in ms */ + int bits; +}; + +/* + * below are random guesses, I can't be bothered checking + */ + +struct delayval crdelay[] = { + 1, CR1, + 2, CR2, + 3, CR3, + 83, CR1, + 166, CR2, + 0, CR3, +}; + +struct delayval nldelay[] = { + 1, NL1, /* special, calculated */ + 2, NL2, + 3, NL3, + 100, NL2, + 0, NL3, +}; + +struct delayval bsdelay[] = { + 1, BS1, + 0, 0, +}; + +struct delayval ffdelay[] = { + 1, FF1, + 1750, FF1, + 0, FF1, +}; + +struct delayval tbdelay[] = { + 1, TAB1, + 2, TAB2, + 3, XTABS, /* this is expand tabs */ + 100, TAB1, + 0, TAB2, +}; + +delaybits() +{ + register f; + + f = adelay(CD, crdelay); + f |= adelay(ND, nldelay); + f |= adelay(FD, ffdelay); + f |= adelay(TD, tbdelay); + f |= adelay(BD, bsdelay); + return (f); +} + +adelay(ms, dp) + register ms; + register struct delayval *dp; +{ + if (ms == 0) + return (0); + while (dp->delay && ms > dp->delay) + dp++; + return (dp->bits); +} + +char editedhost[MAXHOSTNAMELEN]; + +edithost(pat) + register char *pat; +{ + register char *host = HN; + register char *res = editedhost; + + if (!pat) + pat = ""; + while (*pat) { + switch (*pat) { + + case '#': + if (*host) + host++; + break; + + case '@': + if (*host) + *res++ = *host++; + break; + + default: + *res++ = *pat; + break; + + } + if (res == &editedhost[sizeof editedhost - 1]) { + *res = '\0'; + return; + } + pat++; + } + if (*host) + strncpy(res, host, sizeof editedhost - (res - editedhost) - 1); + else + *res = '\0'; + editedhost[sizeof editedhost - 1] = '\0'; +} + +struct speedtab { + int speed; + int uxname; +} speedtab[] = { + 50, B50, + 75, B75, + 110, B110, + 134, B134, + 150, B150, + 200, B200, + 300, B300, + 600, B600, + 1200, B1200, + 1800, B1800, + 2400, B2400, + 4800, B4800, + 9600, B9600, + 19200, EXTA, + 19, EXTA, /* for people who say 19.2K */ + 38400, EXTB, + 38, EXTB, + 7200, EXTB, /* alternative */ + 57600, B57600, + 115200, B115200, + 0 +}; + +speed(val) +{ + register struct speedtab *sp; + + if (val <= B115200) + return (val); + + for (sp = speedtab; sp->speed; sp++) + if (sp->speed == val) + return (sp->uxname); + + return (B300); /* default in impossible cases */ +} + +makeenv(env) + char *env[]; +{ + static char termbuf[128] = "TERM="; + register char *p, *q; + register char **ep; + char *index(); + + ep = env; + if (TT && *TT) { + strcat(termbuf, TT); + *ep++ = termbuf; + } + if (p = EV) { + q = p; + while (q = index(q, ',')) { + *q++ = '\0'; + *ep++ = p; + p = q; + } + if (*p) + *ep++ = p; + } + *ep = (char *)0; +} + +/* + * This speed select mechanism is written for the Develcon DATASWITCH. + * The Develcon sends a string of the form "B{speed}\n" at a predefined + * baud rate. This string indicates the user's actual speed. + * The routine below returns the terminal type mapped from derived speed. + */ +struct portselect { + char *ps_baud; + char *ps_type; +} portspeeds[] = { + { "B110", "std.110" }, + { "B134", "std.134" }, + { "B150", "std.150" }, + { "B300", "std.300" }, + { "B600", "std.600" }, + { "B1200", "std.1200" }, + { "B2400", "std.2400" }, + { "B4800", "std.4800" }, + { "B9600", "std.9600" }, + { "B19200", "std.19200" }, + { 0 } +}; + +char * +portselector() +{ + char c, baud[20], *type = "default"; + register struct portselect *ps; + int len; + + alarm(5*60); + for (len = 0; len < sizeof (baud) - 1; len++) { + if (read(STDIN_FILENO, &c, 1) <= 0) + break; + c &= 0177; + if (c == '\n' || c == '\r') + break; + if (c == 'B') + len = 0; /* in case of leading garbage */ + baud[len] = c; + } + baud[len] = '\0'; + for (ps = portspeeds; ps->ps_baud; ps++) + if (strcmp(ps->ps_baud, baud) == 0) { + type = ps->ps_type; + break; + } + sleep(2); /* wait for connection to complete */ + return (type); +} + +/* + * This auto-baud speed select mechanism is written for the Micom 600 + * portselector. Selection is done by looking at how the character '\r' + * is garbled at the different speeds. + */ +#include <sys/time.h> + +char * +autobaud() +{ + int rfds; + struct timeval timeout; + char c, *type = "9600-baud"; + int null = 0; + + ioctl(0, TIOCFLUSH, &null); + rfds = 1 << 0; + timeout.tv_sec = 5; + timeout.tv_usec = 0; + if (select(32, (fd_set *)&rfds, (fd_set *)NULL, + (fd_set *)NULL, &timeout) <= 0) + return (type); + if (read(STDIN_FILENO, &c, sizeof(char)) != sizeof(char)) + return (type); + timeout.tv_sec = 0; + timeout.tv_usec = 20; + (void) select(32, (fd_set *)NULL, (fd_set *)NULL, + (fd_set *)NULL, &timeout); + ioctl(0, TIOCFLUSH, &null); + switch (c & 0377) { + + case 0200: /* 300-baud */ + type = "300-baud"; + break; + + case 0346: /* 1200-baud */ + type = "1200-baud"; + break; + + case 015: /* 2400-baud */ + case 0215: + type = "2400-baud"; + break; + + default: /* 4800-baud */ + type = "4800-baud"; + break; + + case 0377: /* 9600-baud */ + type = "9600-baud"; + break; + } + return (type); +} diff --git a/libexec/getty/ttydefaults.c b/libexec/getty/ttydefaults.c new file mode 100644 index 0000000..518f41b --- /dev/null +++ b/libexec/getty/ttydefaults.c @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)ttydefaults.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/termios.h> + +#include "extern.h" + +void +set_ttydefaults(fd) + int fd; +{ + struct termios term; + + tcgetattr(fd, &term); + term.c_iflag = TTYDEF_IFLAG; + term.c_oflag = TTYDEF_OFLAG; + term.c_lflag = TTYDEF_LFLAG; + term.c_cflag = TTYDEF_CFLAG; + tcsetattr(fd, TCSAFLUSH, &term); +} diff --git a/libexec/getty/ttys.5 b/libexec/getty/ttys.5 new file mode 100644 index 0000000..8d31b23 --- /dev/null +++ b/libexec/getty/ttys.5 @@ -0,0 +1,139 @@ +.\" Copyright (c) 1985, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)ttys.5 6.8 (Berkeley) 5/4/91 +.\" +.Dd May 4, 1991 +.Dt TTYS 5 +.Os +.Sh NAME +.Nm ttys +.Nd terminal initialization information +.Sh DESCRIPTION +The file +.Nm ttys +contains information that is used by various routines to initialize +and control the use of terminal special files. +This information is read with the +.Xr getttyent 3 +library routines. +There is one line in the +.Nm ttys +file per special device file. +Fields are separated by tabs and/or spaces. +Fields comprised of more than one word should be enclosed in double +quotes (``"''). +Blank lines and comments may appear anywhere in the file; comments +are delimited by hash marks (``#'') and new lines. +Any unspecified fields will default to null. +.Pp +The first field is the +name of the terminal special file as it is found in +.Pa /dev . +.Pp +The second field of the file is the command to execute for the line, +usually +.Xr getty 8 , +which initializes and opens the line, setting the speed, waiting for +a user name and executing the +.Xr login 1 +program. +It can be, however, any desired command, for example +the start up for a window system terminal emulator or some other +daemon process, and can contain multiple words if quoted. +.Pp +The third field is the type of terminal usually connected to that +tty line, normally the one found in the +.Xr termcap 5 +data base file. +The environment variable +.Dv TERM +is initialized with the value by +either +.Xr getty 8 +or +.Xr login 1 . +.Pp +The remaining fields set flags in the +.Fa ty_status +entry (see +.Xr getttyent 3 ) +or specify a window system process that +.Xr init 8 +will maintain for the terminal line. +.Pp +As flag values, the strings ``on'' and ``off'' specify that +.Xr init +should (should not) execute the command given in the second field, +while ``secure'' (if ``on'' is also specified) allows users with a +uid of 0 to login on +this line. +These flag fields should not be quoted. +.Pp +The string ``window='' may be followed by a quoted command +string which +.Xr init +will execute +.Em before +starting the command specified by the second field. +.Sh EXAMPLES +.Bd -literal +# root login on console at 1200 baud +console "/usr/libexec/getty std.1200" vt100 on secure +# dialup at 1200 baud, no root logins +ttyd0 "/usr/libexec/getty d1200" dialup on # 555-1234 +# Mike's terminal: hp2621 +ttyh0 "/usr/libexec/getty std.9600" hp2621-nl on # 457 Evans +# John's terminal: vt100 +ttyh1 "/usr/libexec/getty std.9600" vt100 on # 459 Evans +# terminal emulate/window system +ttyv0 "/usr/new/xterm -L :0" vs100 on window="/usr/new/Xvs100 0" +# Network pseudo ttys -- don't enable getty +ttyp0 none network +ttyp1 none network off +.Ed +.Sh FILES +.Bl -tag -width /etc/ttys -compact +.It Pa /etc/ttys +.El +.Sh SEE ALSO +.Xr login 1 , +.Xr getttyent 3 , +.Xr ttyslot 3 , +.Xr gettytab 5 , +.Xr termcap 5 , +.Xr getty 8 , +.Xr init 8 +.Sh HISTORY +A +.Nm +file appeared in +.At v6 . diff --git a/libexec/kpasswdd/Makefile b/libexec/kpasswdd/Makefile new file mode 100644 index 0000000..edb9482 --- /dev/null +++ b/libexec/kpasswdd/Makefile @@ -0,0 +1,20 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 +# +# We do still make this one conditional on kerberos, in case +# the libraries do not exist + +.if exists(${DESTDIR}/usr/lib/libkrb.a) && (defined(MAKE_KERBEROS) \ + || defined(MAKE_EBONES)) + +PROG= kpasswdd +SRCS= kpasswdd.c +CFLAGS+=-DCRYPT -DKERBEROS -I${.CURDIR}/../../usr.bin/passwd +DPADD= ${LIBKDB} ${LIBKRB} ${LIBDES} +LDADD= -lkdb -lkrb -ldes +.PATH: ${.CURDIR}/../../usr.bin/rlogin +MAN8= kpasswdd.8 +DISTRIBUTION= krb + +.endif + +.include <bsd.prog.mk> diff --git a/libexec/kpasswdd/kpasswdd.8 b/libexec/kpasswdd/kpasswdd.8 new file mode 100644 index 0000000..f6a401f --- /dev/null +++ b/libexec/kpasswdd/kpasswdd.8 @@ -0,0 +1,60 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)kpasswdd.8 8.1 (Berkeley) 6/9/93 +.\" +.Dd June 9, 1993 +.Dt KPASSWDD 8 +.Os +.Sh NAME +.Nm kpasswdd +.Nd Kerberos password changing daemon +.Sh SYNOPSIS +.Nm kpasswdd +.Sh DESCRIPTION +.Nm Kpasswdd +is the server for the +.Xr passwd 1 +program. +The server provides a remote password changing facility +with Kerberos authentication. +A user must provide the old Kerberos password, encrypted +in a random session key, to the server. +.Nm Kpasswdd +runs only on the Kerberos server, as it directly updates the +Kerberos database. +.Sh SEE ALSO +.Xr kerberos 1 , +.Xr passwd 1 +.Sh HISTORY +The +.Nm kpasswdd +utility first appeared in 4.4BSD. diff --git a/libexec/kpasswdd/kpasswdd.c b/libexec/kpasswdd/kpasswdd.c new file mode 100644 index 0000000..23ff1f8 --- /dev/null +++ b/libexec/kpasswdd/kpasswdd.c @@ -0,0 +1,271 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)kpasswdd.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * kpasswdd - update a principal's passwd field in the Kerberos + * database. Called from inetd. + * K. Fall + * 12-Dec-88 + */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/signal.h> +#include <netinet/in.h> +#include <pwd.h> +#include <syslog.h> +#include <kerberosIV/des.h> +#include <kerberosIV/krb.h> +#include <kerberosIV/krb_db.h> +#include <stdio.h> +#include "kpasswd_proto.h" + +static struct kpasswd_data kpwd_data; +static des_cblock master_key, key; +static Key_schedule master_key_schedule, + key_schedule, random_sched; +long mkeyversion; +AUTH_DAT kdata; +static Principal principal_data; +static struct update_data ud_data; + +char inst[INST_SZ]; +char version[9]; +KTEXT_ST ticket; + +char *progname; /* for the library */ + +main() +{ + struct sockaddr_in foreign; + int foreign_len = sizeof(foreign); + int rval, more; + static char name[] = "kpasswdd"; + + static struct rlimit rl = { 0, 0 }; + + progname = name; + openlog("kpasswdd", LOG_CONS | LOG_PID, LOG_AUTH); + + signal(SIGHUP, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + if (setrlimit(RLIMIT_CORE, &rl) < 0) { + syslog(LOG_ERR, "setrlimit: %m"); + exit(1); + } + + if (getpeername(0, &foreign, &foreign_len) < 0) { + syslog(LOG_ERR,"getpeername: %m"); + exit(1); + } + + strcpy(inst, "*"); + rval = krb_recvauth( + 0L, /* options--!MUTUAL */ + 0, /* file desc */ + &ticket, /* client's ticket */ + SERVICE, /* expected service */ + inst, /* expected instance */ + &foreign, /* foreign addr */ + (struct sockaddr_in *) 0, /* local addr */ + &kdata, /* returned krb data */ + "", /* service keys file */ + (bit_64 *) NULL, /* returned key schedule */ + version + ); + + + if (rval != KSUCCESS) { + syslog(LOG_NOTICE, "krb_recvauth: %s", krb_err_txt[rval]); + cleanup(); + exit(1); + } + + if (*version == '\0') { + /* indicates error on client's side (no tickets, etc.) */ + cleanup(); + exit(0); + } else if (strcmp(version, "KPWDV0.1") != 0) { + syslog(LOG_NOTICE, + "kpasswdd version conflict (recv'd %s)", + version); + cleanup(); + exit(1); + } + + + /* get master key */ + if (kdb_get_master_key(0, master_key, master_key_schedule) != 0) { + syslog(LOG_ERR, "couldn't get master key"); + cleanup(); + exit(1); + } + + mkeyversion = kdb_get_master_key(NULL, master_key, master_key_schedule); + + if (mkeyversion < 0) { + syslog(LOG_NOTICE, "couldn't verify master key"); + cleanup(); + exit(1); + } + + /* get principal info */ + rval = kerb_get_principal( + kdata.pname, + kdata.pinst, + &principal_data, + 1, + &more + ); + + if (rval < 0) { + syslog(LOG_NOTICE, + "error retrieving principal record for %s.%s", + kdata.pname, kdata.pinst); + cleanup(); + exit(1); + } + + if (rval != 1 || (more != 0)) { + syslog(LOG_NOTICE, "more than 1 dbase entry for %s.%s", + kdata.pname, kdata.pinst); + cleanup(); + exit(1); + } + + /* get the user's key */ + + bcopy(&principal_data.key_low, key, 4); + bcopy(&principal_data.key_high, ((long *) key) + 1, 4); + kdb_encrypt_key(key, key, master_key, master_key_schedule, + DECRYPT); + key_sched(key, key_schedule); + des_set_key(key, key_schedule); + + + /* get random key and send it over {random} Kperson */ + + random_key(kpwd_data.random_key); + strcpy(kpwd_data.secure_msg, SECURE_STRING); + if (des_write(0, &kpwd_data, sizeof(kpwd_data)) != sizeof(kpwd_data)) { + syslog(LOG_NOTICE, "error writing initial data"); + cleanup(); + exit(1); + } + + bzero(key, sizeof(key)); + bzero(key_schedule, sizeof(key_schedule)); + + /* now read update info: { info }Krandom */ + + key_sched(kpwd_data.random_key, random_sched); + des_set_key(kpwd_data.random_key, random_sched); + if (des_read(0, &ud_data, sizeof(ud_data)) != sizeof(ud_data)) { + syslog(LOG_NOTICE, "update aborted"); + cleanup(); + exit(1); + } + + /* validate info string by looking at the embedded string */ + + if (strcmp(ud_data.secure_msg, SECURE_STRING) != 0) { + syslog(LOG_NOTICE, "invalid update from %s", + inet_ntoa(foreign.sin_addr)); + cleanup(); + exit(1); + } + + /* produce the new key entry in the database { key }Kmaster */ + string_to_key(ud_data.pw, key); + kdb_encrypt_key(key, key, + master_key, master_key_schedule, + ENCRYPT); + bcopy(key, &principal_data.key_low, 4); + bcopy(((long *) key) + 1, + &principal_data.key_high, 4); + bzero(key, sizeof(key)); + principal_data.key_version++; + if (kerb_put_principal(&principal_data, 1)) { + syslog(LOG_ERR, "couldn't write new record for %s.%s", + principal_data.name, principal_data.instance); + cleanup(); + exit(1); + } + + syslog(LOG_NOTICE,"wrote new password field for %s.%s from %s", + principal_data.name, + principal_data.instance, + inet_ntoa(foreign.sin_addr) + ); + + send_ack(0, "Update complete.\n"); + cleanup(); + exit(0); +} + +cleanup() +{ + bzero(&kpwd_data, sizeof(kpwd_data)); + bzero(master_key, sizeof(master_key)); + bzero(master_key_schedule, sizeof(master_key_schedule)); + bzero(key, sizeof(key)); + bzero(key_schedule, sizeof(key_schedule)); + bzero(random_sched, sizeof(random_sched)); + bzero(&principal_data, sizeof(principal_data)); + bzero(&ud_data, sizeof(ud_data)); +} + +send_ack(remote, msg) + int remote; + char *msg; +{ + int cc; + cc = des_write(remote, msg, strlen(msg) + 1); + if (cc <= 0) { + syslog(LOG_NOTICE, "error writing ack"); + cleanup(); + exit(1); + } +} diff --git a/libexec/lfs_cleanerd/Makefile b/libexec/lfs_cleanerd/Makefile new file mode 100644 index 0000000..dac065c --- /dev/null +++ b/libexec/lfs_cleanerd/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= lfs_cleanerd +CFLAGS+=-I/sys/ufs/lfs -I${.CURDIR} ${DEBUG} +MAN8= lfs_cleanerd.8 +SRCS= cleanerd.c lfs_cksum.c library.c misc.c print.c + +.PATH: ${.CURDIR}/../../sys/ufs/lfs + +.include <bsd.prog.mk> diff --git a/libexec/lfs_cleanerd/clean.h b/libexec/lfs_cleanerd/clean.h new file mode 100644 index 0000000..729d0f8 --- /dev/null +++ b/libexec/lfs_cleanerd/clean.h @@ -0,0 +1,168 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)clean.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * The LFS user-level library will be used when writing cleaners and + * checkers for LFS file systems. It will have facilities for finding + * and parsing LFS segments. + */ + +#define DUMP_SUM_HEADER 0x0001 +#define DUMP_INODE_ADDRS 0x0002 +#define DUMP_FINFOS 0x0004 +#define DUMP_ALL 0xFFFF + +#define IFILE_NAME "ifile" + +/* + * Cleaner parameters + * BUSY_LIM: lower bound of the number of segments currently available + * as a percentage of the total number of free segments possibly + * available. + * IDLE_LIM: Same as BUSY_LIM but used when the system is idle. + * MIN_SEGS: Minimum number of segments you should always have. + * I have no idea what this should be, but it should probably + * be a function of lfsp. + * NUM_TO_CLEAN: Number of segments to clean at once. Again, this + * should probably be based on the file system size and how + * full or empty the segments being cleaned are. + */ + +#define BUSY_LIM 0.50 +#define IDLE_LIM 0.90 + +#define MIN_SEGS(lfsp) (5) +#define NUM_TO_CLEAN(fsp) (1) + +#define MAXLOADS 3 +#define ONE_MIN 0 +#define FIVE_MIN 1 +#define FIFTEEN_MIN 2 + +typedef struct fs_info { + struct statfs *fi_statfsp; /* fsstat info from getfsstat */ + struct lfs fi_lfs; /* superblock */ + CLEANERINFO *fi_cip; /* Cleaner info from ifile */ + SEGUSE *fi_segusep; /* segment usage table (from ifile) */ + IFILE *fi_ifilep; /* ifile table (from ifile) */ + u_long fi_daddr_shift; /* shift to get byte offset of daddr */ + u_long fi_ifile_count; /* # entries in the ifile table */ + off_t fi_ifile_length; /* length of the ifile */ +} FS_INFO; + +/* + * XXX: size (in bytes) of a segment + * should lfs_bsize be fsbtodb(fs,1), blksize(fs), or lfs_dsize? + */ +#define seg_size(fs) ((fs)->lfs_ssize << (fs)->lfs_bshift) + +/* daddr -> byte offset */ +#define datobyte(fs, da) ((da) << (fs)->fi_daddr_shift) +#define bytetoda(fs, byte) ((byte) >> (fs)->fi_daddr_shift) + +#define CLEANSIZE(fsp) (fsp->fi_lfs.lfs_cleansz << fsp->fi_lfs.lfs_bshift) +#define SEGTABSIZE(fsp) (fsp->fi_lfs.lfs_segtabsz << fsp->fi_lfs.lfs_bshift) + +#define IFILE_ENTRY(fs, if, i) \ + ((IFILE *)((caddr_t)(if) + ((i) / (fs)->lfs_ifpb << (fs)->lfs_bshift)) \ + + (i) % (fs)->lfs_ifpb) + +#define SEGUSE_ENTRY(fs, su, i) \ + ((SEGUSE *)((caddr_t)(su) + (fs)->lfs_bsize * ((i) / (fs)->lfs_sepb)) +\ + (i) % (fs)->lfs_sepb) + +__BEGIN_DECLS +int dump_summary __P((struct lfs *, SEGSUM *, u_long, daddr_t **)); +void err __P((const int, const char *, ...)); +int fs_getmntinfo __P((struct statfs **, char *, int)); +int get __P((int, off_t, void *, size_t)); +FS_INFO *get_fs_info __P((struct statfs *, int)); +int lfs_segmapv __P((FS_INFO *, int, caddr_t, BLOCK_INFO **, int *)); +int mmap_segment __P((FS_INFO *, int, caddr_t *, int)); +void munmap_segment __P((FS_INFO *, caddr_t, int)); +void reread_fs_info __P((FS_INFO *, int)); +void toss __P((void *, int *, size_t, + int (*)(const void *, const void *, const void *), void *)); + +/* + * USEFUL DEBUGGING FUNCTIONS: + */ +#ifdef VERBOSE +#define PRINT_FINFO(fp, ip) { \ + (void)printf(" %s %s%d version %d nblocks %d\n", \ + (ip)->if_version > (fp)->fi_version ? "TOSSING" : "KEEPING", \ + "FINFO for inode: ", (fp)->fi_ino, \ + (fp)->fi_version, (fp)->fi_nblocks); \ + fflush(stdout); \ +} + +#define PRINT_INODE(b, bip) { \ + (void) printf("\t%s inode: %d daddr: 0x%lx create: %s\n", \ + b ? "KEEPING" : "TOSSING", (bip)->bi_inode, (bip)->bi_daddr, \ + ctime((time_t *)&(bip)->bi_segcreate)); \ + fflush(stdout); \ +} + +#define PRINT_BINFO(bip) { \ + (void)printf("\tinode: %d lbn: %d daddr: 0x%lx create: %s\n", \ + (bip)->bi_inode, (bip)->bi_lbn, (bip)->bi_daddr, \ + ctime((time_t *)&(bip)->bi_segcreate)); \ + fflush(stdout); \ +} + +#define PRINT_SEGUSE(sup, n) { \ + (void)printf("Segment %d nbytes=%lu\tflags=%c%c%c ninos=%d nsums=%d lastmod: %s\n", \ + n, (sup)->su_nbytes, \ + (sup)->su_flags & SEGUSE_DIRTY ? 'D' : 'C', \ + (sup)->su_flags & SEGUSE_ACTIVE ? 'A' : ' ', \ + (sup)->su_flags & SEGUSE_SUPERBLOCK ? 'S' : ' ', \ + (sup)->su_ninos, (sup)->su_nsums, \ + ctime((time_t *)&(sup)->su_lastmod)); \ + fflush(stdout); \ +} + +void dump_super __P((struct lfs *)); +void dump_cleaner_info __P((void *)); +void print_SEGSUM __P(( struct lfs *, SEGSUM *)); +void print_CLEANERINFO __P((CLEANERINFO *)); +#else +#define PRINT_FINFO(fp, ip) +#define PRINT_INODE(b, bip) +#define PRINT_BINFO(bip) +#define PRINT_SEGUSE(sup, n) +#define dump_cleaner_info(cip) +#define dump_super(lfsp) +#endif +__END_DECLS diff --git a/libexec/lfs_cleanerd/cleanerd.c b/libexec/lfs_cleanerd/cleanerd.c new file mode 100644 index 0000000..994992e --- /dev/null +++ b/libexec/lfs_cleanerd/cleanerd.c @@ -0,0 +1,500 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)cleanerd.c 8.2 (Berkeley) 1/13/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/time.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/lfs/lfs.h> + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "clean.h" +char *special = "cleanerd"; +int do_small = 0; +int do_mmap = 0; +struct cleaner_stats { + int blocks_read; + int blocks_written; + int segs_cleaned; + int segs_empty; + int segs_error; +} cleaner_stats; + +struct seglist { + int sl_id; /* segment number */ + int sl_cost; /* cleaning cost */ + char sl_empty; /* is segment empty */ +}; + +struct tossstruct { + struct lfs *lfs; + int seg; +}; + +/* function prototypes for system calls; not sure where they should go */ +int lfs_segwait __P((fsid_t *, struct timeval *)); +int lfs_segclean __P((fsid_t *, u_long)); +int lfs_bmapv __P((fsid_t *, BLOCK_INFO *, int)); +int lfs_markv __P((fsid_t *, BLOCK_INFO *, int)); + +/* function prototypes */ +int bi_tossold __P((const void *, const void *, const void *)); +int choose_segments __P((FS_INFO *, struct seglist *, + int (*)(FS_INFO *, SEGUSE *))); +void clean_fs __P((FS_INFO *, int (*)(FS_INFO *, SEGUSE *))); +int clean_loop __P((FS_INFO *)); +int clean_segment __P((FS_INFO *, int)); +int cost_benefit __P((FS_INFO *, SEGUSE *)); +int cost_compare __P((const void *, const void *)); +void sig_report __P((int)); + +/* + * Cleaning Cost Functions: + * + * These return the cost of cleaning a segment. The higher the cost value + * the better it is to clean the segment, so empty segments have the highest + * cost. (It is probably better to think of this as a priority value + * instead). + * + * This is the cost-benefit policy simulated and described in Rosenblum's + * 1991 SOSP paper. + */ + +int +cost_benefit(fsp, su) + FS_INFO *fsp; /* file system information */ + SEGUSE *su; +{ + struct lfs *lfsp; + struct timeval t; + int age; + int live; + + gettimeofday(&t, NULL); + + live = su->su_nbytes; + age = t.tv_sec < su->su_lastmod ? 0 : t.tv_sec - su->su_lastmod; + + lfsp = &fsp->fi_lfs; + if (live == 0) + return (t.tv_sec * lblkno(lfsp, seg_size(lfsp))); + else { + /* + * from lfsSegUsage.c (Mendel's code). + * priority calculation is done using INTEGER arithmetic. + * sizes are in BLOCKS (that is why we use lblkno below). + * age is in seconds. + * + * priority = ((seg_size - live) * age) / (seg_size + live) + */ +#ifdef VERBOSE + if (live < 0 || live > seg_size(lfsp)) { + err(0, "Bad segusage count: %d", live); + live = 0; + } +#endif + return (lblkno(lfsp, seg_size(lfsp) - live) * age) + / lblkno(lfsp, seg_size(lfsp) + live); + } +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + FS_INFO *fsp; + struct statfs *lstatfsp; /* file system stats */ + struct timeval timeout; /* sleep timeout */ + fsid_t fsid; + int i, nodaemon; + int opt, cmd_err; + char *fs_name; /* name of filesystem to clean */ + extern int optind; + + cmd_err = nodaemon = 0; + while ((opt = getopt(argc, argv, "smd")) != EOF) { + switch (opt) { + case 's': /* small writes */ + do_small = 1; + break; + case 'm': + do_mmap = 1; + break; + case 'd': + nodaemon = 1; + break; + default: + ++cmd_err; + } + } + argc -= optind; + argv += optind; + if (cmd_err || (argc != 1)) + err(1, "usage: lfs_cleanerd [-smd] fs_name"); + + fs_name = argv[0]; + + signal(SIGINT, sig_report); + signal(SIGUSR1, sig_report); + signal(SIGUSR2, sig_report); + if (fs_getmntinfo(&lstatfsp, fs_name, MOUNT_LFS) == 0) { + /* didn't find the filesystem */ + err(1, "lfs_cleanerd: filesystem %s isn't an LFS!", fs_name); + } + + if (!nodaemon) /* should we become a daemon, chdir to / & close fd's */ + if (daemon(0, 0) == -1) + err(1, "lfs_cleanerd: couldn't become a daemon!"); + + timeout.tv_sec = 5*60; /* five minutes */ + timeout.tv_usec = 0; + fsid.val[0] = 0; + fsid.val[1] = 0; + + for (fsp = get_fs_info(lstatfsp, do_mmap); ; + reread_fs_info(fsp, do_mmap)) { + /* + * clean the filesystem, and, if it needed cleaning + * (i.e. it returned nonzero) try it again + * to make sure that some nasty process hasn't just + * filled the disk system up. + */ + if (clean_loop(fsp)) + continue; + +#ifdef VERBOSE + (void)printf("Cleaner going to sleep.\n"); +#endif + if (lfs_segwait(&fsid, &timeout) < 0) + err(0, "lfs_segwait: returned error\n"); +#ifdef VERBOSE + (void)printf("Cleaner waking up.\n"); +#endif + } +} + +/* return the number of segments cleaned */ +int +clean_loop(fsp) + FS_INFO *fsp; /* file system information */ +{ + double loadavg[MAXLOADS]; + time_t now; + u_long max_free_segs; + + /* + * Compute the maximum possible number of free segments, given the + * number of free blocks. + */ + max_free_segs = fsp->fi_statfsp->f_bfree / fsp->fi_lfs.lfs_ssize; + + /* + * We will clean if there are not enough free blocks or total clean + * space is less than BUSY_LIM % of possible clean space. + */ + now = time((time_t *)NULL); + if (fsp->fi_cip->clean < max_free_segs && + (fsp->fi_cip->clean <= MIN_SEGS(&fsp->fi_lfs) || + fsp->fi_cip->clean < max_free_segs * BUSY_LIM)) { + printf("Cleaner Running at %s (%d of %d segments available)\n", + ctime(&now), fsp->fi_cip->clean, max_free_segs); + clean_fs(fsp, cost_benefit); + return (1); + } else { + /* + * We will also clean if the system is reasonably idle and + * the total clean space is less then IDLE_LIM % of possible + * clean space. + */ + if (getloadavg(loadavg, MAXLOADS) == -1) { + perror("getloadavg: failed\n"); + return (-1); + } + if (loadavg[ONE_MIN] == 0.2 && loadavg[FIVE_MIN] && + fsp->fi_cip->clean < max_free_segs * IDLE_LIM) { + clean_fs(fsp, cost_benefit); + printf("Cleaner running (system idle) at %s", + ctime(&now)); + return (1); + } + } +#ifdef VERBOSE + printf("Cleaner not running at %s", ctime(&now)); +#endif + return (0); +} + + +void +clean_fs(fsp, cost_func) + FS_INFO *fsp; /* file system information */ + int (*cost_func) __P((FS_INFO *, SEGUSE *)); +{ + struct seglist *segs, *sp; + int i; + + if ((segs = + malloc(fsp->fi_lfs.lfs_nseg * sizeof(struct seglist))) == NULL) { + err(0, "malloc failed"); + return; + } + i = choose_segments(fsp, segs, cost_func); +#ifdef VERBOSE + printf("clean_fs: found %d segments to clean in file system %s\n", + i, fsp->fi_statfsp->f_mntonname); + fflush(stdout); +#endif + if (i) + for (i = MIN(i, NUM_TO_CLEAN(fsp)), sp = segs; i-- ; ++sp) { + if (clean_segment(fsp, sp->sl_id) < 0) + perror("clean_segment failed"); + else if (lfs_segclean(&fsp->fi_statfsp->f_fsid, + sp->sl_id) < 0) + perror("lfs_segclean failed"); + printf("Completed cleaning segment %d\n", sp->sl_id); + } + free(segs); +} + +/* + * Segment with the highest priority get sorted to the beginning of the + * list. This sort assumes that empty segments always have a higher + * cost/benefit than any utilized segment. + */ +int +cost_compare(a, b) + const void *a; + const void *b; +{ + return (((struct seglist *)b)->sl_cost - + ((struct seglist *)a)->sl_cost); +} + + +/* + * Returns the number of segments to be cleaned with the elements of seglist + * filled in. + */ +int +choose_segments(fsp, seglist, cost_func) + FS_INFO *fsp; + struct seglist *seglist; + int (*cost_func) __P((FS_INFO *, SEGUSE *)); +{ + struct lfs *lfsp; + struct seglist *sp; + SEGUSE *sup; + int i, nsegs; + + lfsp = &fsp->fi_lfs; + +#ifdef VERBOSE + (void)printf("Entering choose_segments\n"); +#endif + dump_super(lfsp); + dump_cleaner_info(fsp->fi_cip); + + for (sp = seglist, i = 0; i < lfsp->lfs_nseg; ++i) { + sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, i); + PRINT_SEGUSE(sup, i); + if (!(sup->su_flags & SEGUSE_DIRTY) || + sup->su_flags & SEGUSE_ACTIVE) + continue; +#ifdef VERBOSE + (void)printf("\tchoosing segment %d\n", i); +#endif + sp->sl_cost = (*cost_func)(fsp, sup); + sp->sl_id = i; + sp->sl_empty = sup->su_nbytes ? 0 : 1; + ++sp; + } + nsegs = sp - seglist; + qsort(seglist, nsegs, sizeof(struct seglist), cost_compare); +#ifdef VERBOSE + (void)printf("Returning %d segments\n", nsegs); +#endif + return (nsegs); +} + + +int +clean_segment(fsp, id) + FS_INFO *fsp; /* file system information */ + int id; /* segment number */ +{ + BLOCK_INFO *block_array, *bp; + SEGUSE *sp; + struct lfs *lfsp; + struct tossstruct t; + caddr_t seg_buf; + int num_blocks, maxblocks, clean_blocks; + + lfsp = &fsp->fi_lfs; + sp = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, id); + +#ifdef VERBOSE + (void)printf("cleaning segment %d: contains %lu bytes\n", id, + sp->su_nbytes); + fflush(stdout); +#endif + /* XXX could add debugging to verify that segment is really empty */ + if (sp->su_nbytes == sp->su_nsums * LFS_SUMMARY_SIZE) { + ++cleaner_stats.segs_empty; + return (0); + } + + /* map the segment into a buffer */ + if (mmap_segment(fsp, id, &seg_buf, do_mmap) < 0) { + err(0, "mmap_segment failed"); + ++cleaner_stats.segs_error; + return (-1); + } + /* get a list of blocks that are contained by the segment */ + if (lfs_segmapv(fsp, id, seg_buf, &block_array, &num_blocks) < 0) { + err(0, "clean_segment: lfs_segmapv failed"); + ++cleaner_stats.segs_error; + return (-1); + } + cleaner_stats.blocks_read += fsp->fi_lfs.lfs_ssize; + +#ifdef VERBOSE + (void)printf("lfs_segmapv returned %d blocks\n", num_blocks); + fflush(stdout); +#endif + + /* get the current disk address of blocks contained by the segment */ + if (lfs_bmapv(&fsp->fi_statfsp->f_fsid, block_array, num_blocks) < 0) { + perror("clean_segment: lfs_bmapv failed\n"); + ++cleaner_stats.segs_error; + return -1; + } + + /* Now toss any blocks not in the current segment */ + t.lfs = lfsp; + t.seg = id; + toss(block_array, &num_blocks, sizeof(BLOCK_INFO), bi_tossold, &t); + + /* Check if last element should be tossed */ + if (num_blocks && bi_tossold(&t, block_array + num_blocks - 1, NULL)) + --num_blocks; + +#ifdef VERBOSE + { + BLOCK_INFO *_bip; + u_long *lp; + int i; + + (void)printf("after bmapv still have %d blocks\n", num_blocks); + fflush(stdout); + if (num_blocks) + printf("BLOCK INFOS\n"); + for (_bip = block_array, i=0; i < num_blocks; ++_bip, ++i) { + PRINT_BINFO(_bip); + lp = (u_long *)_bip->bi_bp; + } + } +#endif + cleaner_stats.blocks_written += num_blocks; + if (do_small) + maxblocks = MAXPHYS / fsp->fi_lfs.lfs_bsize - 1; + else + maxblocks = num_blocks; + + for (bp = block_array; num_blocks > 0; bp += clean_blocks) { + clean_blocks = maxblocks < num_blocks ? maxblocks : num_blocks; + if (lfs_markv(&fsp->fi_statfsp->f_fsid, + bp, clean_blocks) < 0) { + err(0, "clean_segment: lfs_markv failed"); + ++cleaner_stats.segs_error; + return (-1); + } + num_blocks -= clean_blocks; + } + + free(block_array); + munmap_segment(fsp, seg_buf, do_mmap); + ++cleaner_stats.segs_cleaned; + return (0); +} + + +int +bi_tossold(client, a, b) + const void *client; + const void *a; + const void *b; +{ + const struct tossstruct *t; + + t = (struct tossstruct *)client; + + return (((BLOCK_INFO *)a)->bi_daddr == LFS_UNUSED_DADDR || + datosn(t->lfs, ((BLOCK_INFO *)a)->bi_daddr) != t->seg); +} + +void +sig_report(sig) + int sig; +{ + printf("lfs_cleanerd:\t%s%d\n\t\t%s%d\n\t\t%s%d\n\t\t%s%d\n\t\t%s%d\n", + "blocks_read ", cleaner_stats.blocks_read, + "blocks_written ", cleaner_stats.blocks_written, + "segs_cleaned ", cleaner_stats.segs_cleaned, + "segs_empty ", cleaner_stats.segs_empty, + "seg_error ", cleaner_stats.segs_error); + if (sig == SIGUSR2) { + cleaner_stats.blocks_read = 0; + cleaner_stats.blocks_written = 0; + cleaner_stats.segs_cleaned = 0; + cleaner_stats.segs_empty = 0; + cleaner_stats.segs_error = 0; + } + if (sig == SIGINT) + exit(0); +} diff --git a/libexec/lfs_cleanerd/lfs_cleanerd.8 b/libexec/lfs_cleanerd/lfs_cleanerd.8 new file mode 100644 index 0000000..3d134db --- /dev/null +++ b/libexec/lfs_cleanerd/lfs_cleanerd.8 @@ -0,0 +1,77 @@ +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)lfs_cleanerd.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd "December 11, 1993" +.Dt LFS_CLEANERD 8 +.Os BSD 4.4 +.Sh NAME +.Nm lfs_cleanerd +.Nd garbage collect a log-structured file system +.Sh SYNOPSIS +.Nm lfs_cleanerd +.Op Fl ds +.Pa node +.Sh DESCRIPTION +The +.Nm lfs_cleanerd +command starts a daemon process which garbage-collects +the log-structured file system residing at the point named by +.Ar node +in the global file system namespace. +This command is normally executed by +.Xr mount_lfs 8 +when the log-structured file system is mounted. +The daemon will exit within a few minutes +of when the file system it was cleaning is unmounted. +.Pp +Garbage collection on a log-structured file system is done by scanning +the file system's segments for active, i.e. referenced, data and copying +it to new segments. +When all of the active data in a given segment has been copied to a new +segment that segment can be marked as empty, thus reclaiming the space +taken by the inactive data which was in it. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl d +Run in debug mode. +Do not become a daemon process, and print debugging information. +.It Fl s +When cleaning the file system, read data in small chunks. +.El +.Sh SEE ALSO +.Xr mount_lfs 8 +.Sh HISTORY +The +.Nm lfs_cleanerd +utility first appeared in 4.4BSD. diff --git a/libexec/lfs_cleanerd/library.c b/libexec/lfs_cleanerd/library.c new file mode 100644 index 0000000..62f345a --- /dev/null +++ b/libexec/lfs_cleanerd/library.c @@ -0,0 +1,671 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)library.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/types.h> +#include <sys/mman.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/lfs/lfs.h> + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "clean.h" + +void add_blocks __P((FS_INFO *, BLOCK_INFO *, int *, SEGSUM *, caddr_t, + daddr_t, daddr_t)); +void add_inodes __P((FS_INFO *, BLOCK_INFO *, int *, SEGSUM *, caddr_t, + daddr_t)); +int bi_compare __P((const void *, const void *)); +int bi_toss __P((const void *, const void *, const void *)); +void get_ifile __P((FS_INFO *, int)); +int get_superblock __P((FS_INFO *, struct lfs *)); +int pseg_valid __P((FS_INFO *, SEGSUM *)); + +/* + * This function will get information on a a filesystem which matches + * the name and type given. If a "name" is in a filesystem of the given + * type, then buf is filled with that filesystem's info, and the + * a non-zero value is returned. + */ +int +fs_getmntinfo(buf, name, type) + struct statfs **buf; + char *name; + int type; +{ + /* allocate space for the filesystem info */ + *buf = (struct statfs *)malloc(sizeof(struct statfs)); + if (*buf == NULL) + return 0; + + /* grab the filesystem info */ + if (statfs(name, *buf) < 0) { + free(*buf); + return 0; + } + + /* check to see if it's the one we want */ + if (((*buf)->f_type != type) || + strncmp(name, (*buf)->f_mntonname, MNAMELEN)) { + /* "this is not the filesystem you're looking for" */ + free(*buf); + return 0; + } + + return 1; +} + +/* + * Get all the information available on an LFS file system. + * Returns an pointer to an FS_INFO structure, NULL on error. + */ +FS_INFO * +get_fs_info (lstatfsp, use_mmap) + struct statfs *lstatfsp; /* IN: pointer to statfs struct */ + int use_mmap; /* IN: mmap or read */ +{ + FS_INFO *fsp; + int i; + + fsp = (FS_INFO *)malloc(sizeof(FS_INFO)); + if (fsp == NULL) + return NULL; + bzero(fsp, sizeof(FS_INFO)); + + fsp->fi_statfsp = lstatfsp; + if (get_superblock (fsp, &fsp->fi_lfs)) + err(1, "get_fs_info: get_superblock failed"); + fsp->fi_daddr_shift = + fsp->fi_lfs.lfs_bshift - fsp->fi_lfs.lfs_fsbtodb; + get_ifile (fsp, use_mmap); + return (fsp); +} + +/* + * If we are reading the ifile then we need to refresh it. Even if + * we are mmapping it, it might have grown. Finally, we need to + * refresh the file system information (statfs) info. + */ +void +reread_fs_info(fsp, use_mmap) + FS_INFO *fsp; /* IN: prointer fs_infos to reread */ + int use_mmap; +{ + int i; + + if (statfs(fsp->fi_statfsp->f_mntonname, fsp->fi_statfsp)) + err(1, "reread_fs_info: statfs failed"); + get_ifile (fsp, use_mmap); +} + +/* + * Gets the superblock from disk (possibly in face of errors) + */ +int +get_superblock (fsp, sbp) + FS_INFO *fsp; /* local file system info structure */ + struct lfs *sbp; +{ + char mntfromname[MNAMELEN+1]; + int fid; + + strcpy(mntfromname, "/dev/r"); + strcat(mntfromname, fsp->fi_statfsp->f_mntfromname+5); + + if ((fid = open(mntfromname, O_RDONLY, (mode_t)0)) < 0) { + err(0, "get_superblock: bad open"); + return (-1); + } + + get(fid, LFS_LABELPAD, sbp, sizeof(struct lfs)); + close (fid); + + return (0); +} + +/* + * This function will map the ifile into memory. It causes a + * fatal error on failure. + */ +void +get_ifile (fsp, use_mmap) + FS_INFO *fsp; + int use_mmap; + +{ + struct stat file_stat; + caddr_t ifp; + char *ifile_name; + int count, fid; + + ifp = NULL; + ifile_name = malloc(strlen(fsp->fi_statfsp->f_mntonname) + + strlen(IFILE_NAME)+2); + strcat(strcat(strcpy(ifile_name, fsp->fi_statfsp->f_mntonname), "/"), + IFILE_NAME); + + if ((fid = open(ifile_name, O_RDWR, (mode_t)0)) < 0) + err(1, "get_ifile: bad open"); + + if (fstat (fid, &file_stat)) + err(1, "get_ifile: fstat failed"); + + if (use_mmap && file_stat.st_size == fsp->fi_ifile_length) { + (void) close(fid); + return; + } + + /* get the ifile */ + if (use_mmap) { + if (fsp->fi_cip) + munmap((caddr_t)fsp->fi_cip, fsp->fi_ifile_length); + ifp = mmap ((caddr_t)0, file_stat.st_size, + PROT_READ|PROT_WRITE, 0, fid, (off_t)0); + if (ifp == (caddr_t)(-1)) + err(1, "get_ifile: mmap failed"); + } else { + if (fsp->fi_cip) + free(fsp->fi_cip); + if (!(ifp = malloc (file_stat.st_size))) + err (1, "get_ifile: malloc failed"); +redo_read: + count = read (fid, ifp, (size_t) file_stat.st_size); + + if (count < 0) + err(1, "get_ifile: bad ifile read"); + else if (count < file_stat.st_size) { + err(0, "get_ifile"); + if (lseek(fid, 0, SEEK_SET) < 0) + err(1, "get_ifile: bad ifile lseek"); + goto redo_read; + } + } + fsp->fi_ifile_length = file_stat.st_size; + close (fid); + + fsp->fi_cip = (CLEANERINFO *)ifp; + fsp->fi_segusep = (SEGUSE *)(ifp + CLEANSIZE(fsp)); + fsp->fi_ifilep = (IFILE *)((caddr_t)fsp->fi_segusep + SEGTABSIZE(fsp)); + + /* + * The number of ifile entries is equal to the number of blocks + * blocks in the ifile minus the ones allocated to cleaner info + * and segment usage table multiplied by the number of ifile + * entries per page. + */ + fsp->fi_ifile_count = (fsp->fi_ifile_length >> fsp->fi_lfs.lfs_bshift - + fsp->fi_lfs.lfs_cleansz - fsp->fi_lfs.lfs_segtabsz) * + fsp->fi_lfs.lfs_ifpb; + + free (ifile_name); +} + +/* + * This function will scan a segment and return a list of + * <inode, blocknum> pairs which indicate which blocks were + * contained as live data within the segment when the segment + * summary was read (it may have "died" since then). Any given + * pair will be listed at most once. + */ +int +lfs_segmapv(fsp, seg, seg_buf, blocks, bcount) + FS_INFO *fsp; /* pointer to local file system information */ + int seg; /* the segment number */ + caddr_t seg_buf; /* the buffer containing the segment's data */ + BLOCK_INFO **blocks; /* OUT: array of block_info for live blocks */ + int *bcount; /* OUT: number of active blocks in segment */ +{ + BLOCK_INFO *bip; + SEGSUM *sp; + SEGUSE *sup; + FINFO *fip; + struct lfs *lfsp; + caddr_t s, segend; + daddr_t pseg_addr, seg_addr; + int i, nelem, nblocks, sumsize; + time_t timestamp; + + lfsp = &fsp->fi_lfs; + nelem = 2 * lfsp->lfs_ssize; + if (!(bip = malloc(nelem * sizeof(BLOCK_INFO)))) + goto err0; + + sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, seg); + s = seg_buf + (sup->su_flags & SEGUSE_SUPERBLOCK ? LFS_SBPAD : 0); + seg_addr = sntoda(lfsp, seg); + pseg_addr = seg_addr + (sup->su_flags & SEGUSE_SUPERBLOCK ? btodb(LFS_SBPAD) : 0); +#ifdef VERBOSE + printf("\tsegment buffer at: 0x%x\tseg_addr 0x%x\n", s, seg_addr); +#endif /* VERBOSE */ + + *bcount = 0; + for (segend = seg_buf + seg_size(lfsp), timestamp = 0; s < segend; ) { + sp = (SEGSUM *)s; + +#ifdef VERBOSE + printf("\tpartial at: 0x%x\n", pseg_addr); + print_SEGSUM(lfsp, sp); + fflush(stdout); +#endif /* VERBOSE */ + + nblocks = pseg_valid(fsp, sp); + if (nblocks <= 0) + break; + + /* Check if we have hit old data */ + if (timestamp > ((SEGSUM*)s)->ss_create) + break; + timestamp = ((SEGSUM*)s)->ss_create; + +#ifdef DIAGNOSTIC + /* Verify size of summary block */ + sumsize = sizeof(SEGSUM) + + (sp->ss_ninos + INOPB(lfsp) - 1) / INOPB(lfsp); + for (fip = (FINFO *)(sp + 1); i < sp->ss_nfinfo; ++i) { + sumsize += sizeof(FINFO) + + (fip->fi_nblocks - 1) * sizeof(daddr_t); + fip = (FINFO *)(&fip->fi_blocks[fip->fi_nblocks]); + } + if (sumsize > LFS_SUMMARY_SIZE) { + fprintf(stderr, + "Segment %d summary block too big: %d\n", + seg, sumsize); + exit(1); + } +#endif + + if (*bcount + nblocks + sp->ss_ninos > nelem) { + nelem = *bcount + nblocks + sp->ss_ninos; + bip = realloc (bip, nelem * sizeof(BLOCK_INFO)); + if (!bip) + goto err0; + } + add_blocks(fsp, bip, bcount, sp, seg_buf, seg_addr, pseg_addr); + add_inodes(fsp, bip, bcount, sp, seg_buf, seg_addr); + pseg_addr += fsbtodb(lfsp, nblocks) + + bytetoda(fsp, LFS_SUMMARY_SIZE); + s += (nblocks << lfsp->lfs_bshift) + LFS_SUMMARY_SIZE; + } + qsort(bip, *bcount, sizeof(BLOCK_INFO), bi_compare); + toss(bip, bcount, sizeof(BLOCK_INFO), bi_toss, NULL); +#ifdef VERBOSE + { + BLOCK_INFO *_bip; + int i; + + printf("BLOCK INFOS\n"); + for (_bip = bip, i=0; i < *bcount; ++_bip, ++i) + PRINT_BINFO(_bip); + } +#endif + *blocks = bip; + return (0); + +err0: *bcount = 0; + return (-1); + +} + +/* + * This will parse a partial segment and fill in BLOCK_INFO structures + * for each block described in the segment summary. It will not include + * blocks or inodes from files with new version numbers. + */ +void +add_blocks (fsp, bip, countp, sp, seg_buf, segaddr, psegaddr) + FS_INFO *fsp; /* pointer to super block */ + BLOCK_INFO *bip; /* Block info array */ + int *countp; /* IN/OUT: number of blocks in array */ + SEGSUM *sp; /* segment summmary pointer */ + caddr_t seg_buf; /* buffer containing segment */ + daddr_t segaddr; /* address of this segment */ + daddr_t psegaddr; /* address of this partial segment */ +{ + IFILE *ifp; + FINFO *fip; + caddr_t bp; + daddr_t *dp, *iaddrp; + int db_per_block, i, j; + u_long page_size; + +#ifdef VERBOSE + printf("FILE INFOS\n"); +#endif + db_per_block = fsbtodb(&fsp->fi_lfs, 1); + page_size = fsp->fi_lfs.lfs_bsize; + bp = seg_buf + datobyte(fsp, psegaddr - segaddr) + LFS_SUMMARY_SIZE; + bip += *countp; + psegaddr += bytetoda(fsp, LFS_SUMMARY_SIZE); + iaddrp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE); + --iaddrp; + for (fip = (FINFO *)(sp + 1), i = 0; i < sp->ss_nfinfo; + ++i, fip = (FINFO *)(&fip->fi_blocks[fip->fi_nblocks])) { + + ifp = IFILE_ENTRY(&fsp->fi_lfs, fsp->fi_ifilep, fip->fi_ino); + PRINT_FINFO(fip, ifp); + if (ifp->if_version > fip->fi_version) + continue; + dp = &(fip->fi_blocks[0]); + for (j = 0; j < fip->fi_nblocks; j++, dp++) { + while (psegaddr == *iaddrp) { + psegaddr += db_per_block; + bp += page_size; + --iaddrp; + } + bip->bi_inode = fip->fi_ino; + bip->bi_lbn = *dp; + bip->bi_daddr = psegaddr; + bip->bi_segcreate = (time_t)(sp->ss_create); + bip->bi_bp = bp; + bip->bi_version = ifp->if_version; + psegaddr += db_per_block; + bp += page_size; + ++bip; + ++(*countp); + } + } +} + +/* + * For a particular segment summary, reads the inode blocks and adds + * INODE_INFO structures to the array. Returns the number of inodes + * actually added. + */ +void +add_inodes (fsp, bip, countp, sp, seg_buf, seg_addr) + FS_INFO *fsp; /* pointer to super block */ + BLOCK_INFO *bip; /* block info array */ + int *countp; /* pointer to current number of inodes */ + SEGSUM *sp; /* segsum pointer */ + caddr_t seg_buf; /* the buffer containing the segment's data */ + daddr_t seg_addr; /* disk address of seg_buf */ +{ + struct dinode *di; + struct lfs *lfsp; + IFILE *ifp; + BLOCK_INFO *bp; + daddr_t *daddrp; + ino_t inum; + int i; + + if (sp->ss_ninos <= 0) + return; + + bp = bip + *countp; + lfsp = &fsp->fi_lfs; +#ifdef VERBOSE + (void) printf("INODES:\n"); +#endif + daddrp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE); + for (i = 0; i < sp->ss_ninos; ++i) { + if (i % INOPB(lfsp) == 0) { + --daddrp; + di = (struct dinode *)(seg_buf + + ((*daddrp - seg_addr) << fsp->fi_daddr_shift)); + } else + ++di; + + inum = di->di_inumber; + bp->bi_lbn = LFS_UNUSED_LBN; + bp->bi_inode = inum; + bp->bi_daddr = *daddrp; + bp->bi_bp = di; + bp->bi_segcreate = sp->ss_create; + + if (inum == LFS_IFILE_INUM) { + bp->bi_version = 1; /* Ifile version should be 1 */ + bp++; + ++(*countp); + PRINT_INODE(1, bp); + } else { + ifp = IFILE_ENTRY(lfsp, fsp->fi_ifilep, inum); + PRINT_INODE(ifp->if_daddr == *daddrp, bp); + bp->bi_version = ifp->if_version; + if (ifp->if_daddr == *daddrp) { + bp++; + ++(*countp); + } + } + } +} + +/* + * Checks the summary checksum and the data checksum to determine if the + * segment is valid or not. Returns the size of the partial segment if it + * is valid, * and 0 otherwise. Use dump_summary to figure out size of the + * the partial as well as whether or not the checksum is valid. + */ +int +pseg_valid (fsp, ssp) + FS_INFO *fsp; /* pointer to file system info */ + SEGSUM *ssp; /* pointer to segment summary block */ +{ + caddr_t p; + int i, nblocks; + u_long *datap; + + if ((nblocks = dump_summary(&fsp->fi_lfs, ssp, 0, NULL)) <= 0 || + nblocks > fsp->fi_lfs.lfs_ssize - 1) + return(0); + + /* check data/inode block(s) checksum too */ + datap = (u_long *)malloc(nblocks * sizeof(u_long)); + p = (caddr_t)ssp + LFS_SUMMARY_SIZE; + for (i = 0; i < nblocks; ++i) { + datap[i] = *((u_long *)p); + p += fsp->fi_lfs.lfs_bsize; + } + if (cksum ((void *)datap, nblocks * sizeof(u_long)) != ssp->ss_datasum) + return (0); + + return (nblocks); +} + + +/* #define MMAP_SEGMENT */ +/* + * read a segment into a memory buffer + */ +int +mmap_segment (fsp, segment, segbuf, use_mmap) + FS_INFO *fsp; /* file system information */ + int segment; /* segment number */ + caddr_t *segbuf; /* pointer to buffer area */ + int use_mmap; /* mmap instead of read */ +{ + struct lfs *lfsp; + int fid; /* fildes for file system device */ + daddr_t seg_daddr; /* base disk address of segment */ + off_t seg_byte; + size_t ssize; + char mntfromname[MNAMELEN+2]; + + lfsp = &fsp->fi_lfs; + + /* get the disk address of the beginning of the segment */ + seg_daddr = sntoda(lfsp, segment); + seg_byte = datobyte(fsp, seg_daddr); + ssize = seg_size(lfsp); + + strcpy(mntfromname, "/dev/r"); + strcat(mntfromname, fsp->fi_statfsp->f_mntfromname+5); + + if ((fid = open(mntfromname, O_RDONLY, (mode_t)0)) < 0) { + err(0, "mmap_segment: bad open"); + return (-1); + } + + if (use_mmap) { + *segbuf = mmap ((caddr_t)0, seg_size(lfsp), PROT_READ, + 0, fid, seg_byte); + if (*(long *)segbuf < 0) { + err(0, "mmap_segment: mmap failed"); + return (NULL); + } + } else { +#ifdef VERBOSE + printf("mmap_segment\tseg_daddr: %lu\tseg_size: %lu\tseg_offset: %qu\n", + seg_daddr, ssize, seg_byte); +#endif + /* malloc the space for the buffer */ + *segbuf = malloc(ssize); + if (!*segbuf) { + err(0, "mmap_segment: malloc failed"); + return(NULL); + } + + /* read the segment data into the buffer */ + if (lseek (fid, seg_byte, SEEK_SET) != seg_byte) { + err (0, "mmap_segment: bad lseek"); + free(*segbuf); + return (-1); + } + + if (read (fid, *segbuf, ssize) != ssize) { + err (0, "mmap_segment: bad read"); + free(*segbuf); + return (-1); + } + } + close (fid); + + return (0); +} + +void +munmap_segment (fsp, seg_buf, use_mmap) + FS_INFO *fsp; /* file system information */ + caddr_t seg_buf; /* pointer to buffer area */ + int use_mmap; /* mmap instead of read/write */ +{ + if (use_mmap) + munmap (seg_buf, seg_size(&fsp->fi_lfs)); + else + free (seg_buf); +} + + +/* + * USEFUL DEBUGGING TOOLS: + */ +void +print_SEGSUM (lfsp, p) + struct lfs *lfsp; + SEGSUM *p; +{ + if (p) + (void) dump_summary(lfsp, p, DUMP_ALL, NULL); + else printf("0x0"); + fflush(stdout); +} + +int +bi_compare(a, b) + const void *a; + const void *b; +{ + const BLOCK_INFO *ba, *bb; + int diff; + + ba = a; + bb = b; + + if (diff = (int)(ba->bi_inode - bb->bi_inode)) + return (diff); + if (diff = (int)(ba->bi_lbn - bb->bi_lbn)) { + if (ba->bi_lbn == LFS_UNUSED_LBN) + return(-1); + else if (bb->bi_lbn == LFS_UNUSED_LBN) + return(1); + else if (ba->bi_lbn < 0 && bb->bi_lbn >= 0) + return(1); + else if (bb->bi_lbn < 0 && ba->bi_lbn >= 0) + return(-1); + else + return (diff); + } + if (diff = (int)(ba->bi_segcreate - bb->bi_segcreate)) + return (diff); + diff = (int)(ba->bi_daddr - bb->bi_daddr); + return (diff); +} + +int +bi_toss(dummy, a, b) + const void *dummy; + const void *a; + const void *b; +{ + const BLOCK_INFO *ba, *bb; + + ba = a; + bb = b; + + return(ba->bi_inode == bb->bi_inode && ba->bi_lbn == bb->bi_lbn); +} + +void +toss(p, nump, size, dotoss, client) + void *p; + int *nump; + size_t size; + int (*dotoss) __P((const void *, const void *, const void *)); + void *client; +{ + int i; + void *p1; + + if (*nump == 0) + return; + + for (i = *nump; --i > 0;) { + p1 = p + size; + if (dotoss(client, p, p1)) { + memmove(p, p1, i * size); + --(*nump); + } else + p += size; + } +} diff --git a/libexec/lfs_cleanerd/misc.c b/libexec/lfs_cleanerd/misc.c new file mode 100644 index 0000000..ad6e11a --- /dev/null +++ b/libexec/lfs_cleanerd/misc.c @@ -0,0 +1,94 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +extern char *special; + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +err(const int fatal, const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "%s: ", special); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + if (errno) + (void)fprintf(stderr, " %s", strerror(errno)); + (void)fprintf(stderr, "\n"); + if (fatal) + exit(1); +} + +void +get(fd, off, p, len) + int fd; + off_t off; + void *p; + size_t len; +{ + int rbytes; + + if (lseek(fd, off, SEEK_SET) < 0) + err(1, "%s: %s", special, strerror(errno)); + if ((rbytes = read(fd, p, len)) < 0) + err(1, "%s: %s", special, strerror(errno)); + if (rbytes != len) + err(1, "%s: short read (%d, not %d)", special, rbytes, len); +} diff --git a/libexec/lfs_cleanerd/print.c b/libexec/lfs_cleanerd/print.c new file mode 100644 index 0000000..64320c5 --- /dev/null +++ b/libexec/lfs_cleanerd/print.c @@ -0,0 +1,218 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/ucred.h> +#include <sys/mount.h> +#include <sys/time.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/lfs/lfs.h> + +#include <stdlib.h> +#include <stdio.h> +#include "clean.h" + +/* + * Print out a summary block; return number of blocks in segment; 0 + * for empty segment or corrupt segment. + * Returns a pointer to the array of inode addresses. + */ +int +dump_summary(lfsp, sp, flags, iaddrp) + struct lfs *lfsp; + SEGSUM *sp; + u_long flags; + daddr_t **iaddrp; +{ + int i, j, numblocks; + daddr_t *dp; + + FINFO *fp; + int ck; + + if (sp->ss_sumsum != (ck = cksum(&sp->ss_datasum, + LFS_SUMMARY_SIZE - sizeof(sp->ss_sumsum)))) + return(-1); + + if (flags & DUMP_SUM_HEADER) { + (void)printf(" %s0x%X\t%s%d\t%s%d\n %s0x%X\t%s0x%X", + "next ", sp->ss_next, + "nfinfo ", sp->ss_nfinfo, + "ninos ", sp->ss_ninos, + "sumsum ", sp->ss_sumsum, + "datasum ", sp->ss_datasum ); + (void)printf("\tcreate %s", ctime((time_t *)&sp->ss_create)); + } + + numblocks = (sp->ss_ninos + INOPB(lfsp) - 1) / INOPB(lfsp); + + /* Dump out inode disk addresses */ + if (flags & DUMP_INODE_ADDRS) + printf(" Inode addresses:"); + + dp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE); + for (--dp, i = 0; i < sp->ss_ninos; --dp) + if (flags & DUMP_INODE_ADDRS) { + (void)printf("\t0x%lx", *dp); + if (++i % 7 == 0) + (void)printf("\n"); + } else + ++i; + if (iaddrp) + *iaddrp = ++dp; + if (flags & DUMP_INODE_ADDRS) + printf("\n"); + + for (fp = (FINFO *)(sp + 1), i = 0; i < sp->ss_nfinfo; ++i) { + numblocks += fp->fi_nblocks; + if (flags & DUMP_FINFOS) { + (void)printf(" %s%d version %d nblocks %d\n", + "FINFO for inode: ", fp->fi_ino, + fp->fi_version, fp->fi_nblocks); + dp = &(fp->fi_blocks[0]); + for (j = 0; j < fp->fi_nblocks; j++, dp++) { + (void)printf("\t%d", *dp); + if ((j % 8) == 7) + (void)printf("\n"); + } + if ((j % 8) != 0) + (void)printf("\n"); + fp = (FINFO *)dp; + } else { + fp = (FINFO *)(&fp->fi_blocks[fp->fi_nblocks]); + } + } + return (numblocks); +} + +#ifdef VERBOSE +void +dump_cleaner_info(ipage) + void *ipage; +{ + CLEANERINFO *cip; + + cip = (CLEANERINFO *)ipage; + (void)printf("segments clean\t%d\tsegments dirty\t%d\n\n", + cip->clean, cip->dirty); +} + +void +dump_super(lfsp) + struct lfs *lfsp; +{ + int i; + + (void)printf("%s0x%X\t%s0x%X\t%s%d\t%s%d\n", + "magic ", lfsp->lfs_magic, + "version ", lfsp->lfs_version, + "size ", lfsp->lfs_size, + "ssize ", lfsp->lfs_ssize); + (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n", + "dsize ", lfsp->lfs_dsize, + "bsize ", lfsp->lfs_bsize, + "fsize ", lfsp->lfs_fsize, + "frag ", lfsp->lfs_frag); + + (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n", + "minfree ", lfsp->lfs_minfree, + "inopb ", lfsp->lfs_inopb, + "ifpb ", lfsp->lfs_ifpb, + "nindir ", lfsp->lfs_nindir); + + (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n", + "nseg ", lfsp->lfs_nseg, + "nspf ", lfsp->lfs_nspf, + "cleansz ", lfsp->lfs_cleansz, + "segtabsz ", lfsp->lfs_segtabsz); + + (void)printf("%s0x%X\t%s%d\t%s0x%X\t%s%d\n", + "segmask ", lfsp->lfs_segmask, + "segshift ", lfsp->lfs_segshift, + "bmask ", lfsp->lfs_bmask, + "bshift ", lfsp->lfs_bshift); + + (void)printf("%s0x%X\t\t%s%d\t%s0x%X\t%s%d\n", + "ffmask ", lfsp->lfs_ffmask, + "ffshift ", lfsp->lfs_ffshift, + "fbmask ", lfsp->lfs_fbmask, + "fbshift ", lfsp->lfs_fbshift); + + (void)printf("%s%d\t\t%s0x%X\t%s0x%qx\n", + "fsbtodb ", lfsp->lfs_fsbtodb, + "cksum ", lfsp->lfs_cksum, + "maxfilesize ", lfsp->lfs_maxfilesize); + + (void)printf("Superblock disk addresses:\t"); + for (i = 0; i < LFS_MAXNUMSB; i++) { + (void)printf(" 0x%X", lfsp->lfs_sboffs[i]); + if ( i == (LFS_MAXNUMSB >> 1)) + (void)printf("\n\t\t\t\t"); + } + (void)printf("\n"); + + (void)printf("Checkpoint Info\n"); + (void)printf("%s%d\t%s0x%X\t%s%d\n", + "free ", lfsp->lfs_free, + "idaddr ", lfsp->lfs_idaddr, + "ifile ", lfsp->lfs_ifile); + (void)printf("%s%d\t%s%d\t%s%d\n", + "bfree ", lfsp->lfs_bfree, + "avail ", lfsp->lfs_avail, + "uinodes ", lfsp->lfs_uinodes); + (void)printf("%s%d\t%s0x%X\t%s0x%X\n%s0x%X\t%s0x%X\t", + "nfiles ", lfsp->lfs_nfiles, + "lastseg ", lfsp->lfs_lastseg, + "nextseg ", lfsp->lfs_nextseg, + "curseg ", lfsp->lfs_curseg, + "offset ", lfsp->lfs_offset); + (void)printf("tstamp %s", ctime((time_t *)&lfsp->lfs_tstamp)); + (void)printf("\nIn-Memory Information\n"); + (void)printf("%s%d\t%s0x%X\t%s%d\t%s%d\t%s%d\n", + "seglock ", lfsp->lfs_seglock, + "iocount ", lfsp->lfs_iocount, + "writer ", lfsp->lfs_writer, + "dirops ", lfsp->lfs_dirops, + "doifile ", lfsp->lfs_doifile ); + (void)printf("%s%d\t%s%d\t%s0x%X\t%s%d\n", + "nactive ", lfsp->lfs_nactive, + "fmod ", lfsp->lfs_fmod, + "clean ", lfsp->lfs_clean, + "ronly ", lfsp->lfs_ronly); +} +#endif /* VERBOSE */ diff --git a/libexec/mail.local/Makefile b/libexec/mail.local/Makefile new file mode 100644 index 0000000..10371e4 --- /dev/null +++ b/libexec/mail.local/Makefile @@ -0,0 +1,12 @@ +# @(#)Makefile 8.1 (Berkeley) 7/19/93 + +PROG= mail.local +MAN8= mail.local.8 +.if defined(DONT_FSYNC) +CFLAGS+= -DDONT_FSYNC +.endif +BINOWN= root +BINMODE=4555 +INSTALLFLAGS=-fschg + +.include <bsd.prog.mk> diff --git a/libexec/mail.local/mail.local.8 b/libexec/mail.local/mail.local.8 new file mode 100644 index 0000000..661615c --- /dev/null +++ b/libexec/mail.local/mail.local.8 @@ -0,0 +1,105 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mail.local.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt MAIL.LOCAL 8 +.Os +.Sh NAME +.Nm mail.local +.Nd store mail in a mailbox +.Sh SYNOPSIS +.Nm mail.local +.Op Fl f Ar from +.Ar user ... +.Sh DESCRIPTION +.Nm Mail.local +reads the standard input up to an end-of-file and appends it to each +.Ar user's +.Pa mail +file. +The +.Ar user +must be a valid user name. +.Pp +The options are as follows: +.Bl -tag -width xxxfrom +.It Fl f Ar from +Specify the sender's name. +.El +.Pp +Individual mail messages in the mailbox are delimited by an empty +line followed by a line beginning with the string ``From ''. +A line containing the string ``From '', the sender's name and a time stamp +is prepended to each delivered mail message. +A blank line is appended to each message. +A greater-than character (``>'') is prepended to any line in the message +which could be mistaken for a ``From '' delimiter line. +.Pp +The mail files are exclusively locked with +.Xr flock 2 +while mail is appended. +.Pp +If the ``biff'' service is returned by +.Xr getservbyname 3 , +the biff server is notified of delivered mail. +.Pp +The +.Nm mail.local +utility exits 0 on success, and >0 if an error occurs. +.Sh ENVIRONMENT +.Bl -tag -width indent +.It Ev TZ +Used to set the appropriate time zone on the timestamp. +.El +.Sh FILES +.Bl -tag -width /tmp/local.XXXXXX -compact +.It Pa /tmp/local.XXXXXX +temporary files +.It Pa /var/mail/user +user's mailbox directory +.El +.Sh SEE ALSO +.Xr mail 1 , +.Xr xsend 1 , +.Xr flock 2 , +.Xr getservbyname 3 , +.Xr comsat 8 , +.Xr sendmail 8 +.Sh HISTORY +A superset of +.Nm mail.local +(handling mailbox reading as well as mail delivery) +appeared in +.At v7 . +as the program +.Nm mail . diff --git a/libexec/mail.local/mail.local.c b/libexec/mail.local/mail.local.c new file mode 100644 index 0000000..0d80972 --- /dev/null +++ b/libexec/mail.local/mail.local.c @@ -0,0 +1,513 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1990, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mail.local.c 8.6 (Berkeley) 4/8/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include "pathnames.h" + +int eval = EX_OK; /* sysexits.h error value. */ + +void deliver __P((int, char *)); +void e_to_sys __P((int)); +__dead void err __P((const char *, ...)); +void notifybiff __P((char *)); +int store __P((char *)); +void usage __P((void)); +void vwarn __P((const char *, _BSD_VA_LIST_)); +void warn __P((const char *, ...)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct passwd *pw; + int ch, fd; + uid_t uid; + char *from; + + openlog("mail.local", 0, LOG_MAIL); + + from = NULL; + while ((ch = getopt(argc, argv, "df:r:")) != EOF) + switch(ch) { + case 'd': /* Backward compatible. */ + break; + case 'f': + case 'r': /* Backward compatible. */ + if (from != NULL) { + warn("multiple -f options"); + usage(); + } + from = optarg; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (!*argv) + usage(); + + /* + * If from not specified, use the name from getlogin() if the + * uid matches, otherwise, use the name from the password file + * corresponding to the uid. + */ + uid = getuid(); + if (!from && (!(from = getlogin()) || + !(pw = getpwnam(from)) || pw->pw_uid != uid)) + from = (pw = getpwuid(uid)) ? pw->pw_name : "???"; + + /* + * There is no way to distinguish the error status of one delivery + * from the rest of the deliveries. So, if we failed hard on one + * or more deliveries, but had no failures on any of the others, we + * return a hard failure. If we failed temporarily on one or more + * deliveries, we return a temporary failure regardless of the other + * failures. This results in the delivery being reattempted later + * at the expense of repeated failures and multiple deliveries. + */ + for (fd = store(from); *argv; ++argv) + deliver(fd, *argv); + exit(eval); +} + +int +store(from) + char *from; +{ + FILE *fp; + time_t tval; + int fd, eline; + char *tn, line[2048]; + + tn = strdup(_PATH_LOCTMP); + if ((fd = mkstemp(tn)) == -1 || (fp = fdopen(fd, "w+")) == NULL) { + e_to_sys(errno); + err("unable to open temporary file"); + } + (void)unlink(tn); + free(tn); + + (void)time(&tval); + (void)fprintf(fp, "From %s %s", from, ctime(&tval)); + + line[0] = '\0'; + for (eline = 1; fgets(line, sizeof(line), stdin);) { + if (line[0] == '\n') + eline = 1; + else { + if (eline && line[0] == 'F' && + !memcmp(line, "From ", 5)) + (void)putc('>', fp); + eline = 0; + } + (void)fprintf(fp, "%s", line); + if (ferror(fp)) { + e_to_sys(errno); + err("temporary file write error"); + } + } + + /* If message not newline terminated, need an extra. */ + if (!strchr(line, '\n')) + (void)putc('\n', fp); + /* Output a newline; note, empty messages are allowed. */ + (void)putc('\n', fp); + + if (fflush(fp) == EOF || ferror(fp)) { + e_to_sys(errno); + err("temporary file write error"); + } + return (fd); +} + +void +deliver(fd, name) + int fd; + char *name; +{ + struct stat fsb, sb; + struct passwd *pw; + int mbfd, nr, nw, off; + char biffmsg[100], buf[8*1024], path[MAXPATHLEN]; + off_t curoff; + + /* + * Disallow delivery to unknown names -- special mailboxes can be + * handled in the sendmail aliases file. + */ + if (!(pw = getpwnam(name))) { + if (eval != EX_TEMPFAIL) + eval = EX_UNAVAILABLE; + warn("unknown name: %s", name); + return; + } + + (void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name); + + /* + * If the mailbox is linked or a symlink, fail. There's an obvious + * race here, that the file was replaced with a symbolic link after + * the lstat returned, but before the open. We attempt to detect + * this by comparing the original stat information and information + * returned by an fstat of the file descriptor returned by the open. + * + * NB: this is a symptom of a larger problem, that the mail spooling + * directory is writeable by the wrong users. If that directory is + * writeable, system security is compromised for other reasons, and + * it cannot be fixed here. + * + * If we created the mailbox, set the owner/group. If that fails, + * just return. Another process may have already opened it, so we + * can't unlink it. Historically, binmail set the owner/group at + * each mail delivery. We no longer do this, assuming that if the + * ownership or permissions were changed there was a reason. + * + * XXX + * open(2) should support flock'ing the file. + */ +tryagain: + if (lstat(path, &sb)) { + mbfd = open(path, + O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR); + if (mbfd == -1) { + if (errno == EEXIST) + goto tryagain; + } else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) { + e_to_sys(errno); + warn("chown %u.%u: %s", pw->pw_uid, pw->pw_gid, name); + return; + } + } else if (sb.st_nlink != 1 || S_ISLNK(sb.st_mode)) { + e_to_sys(errno); + warn("%s: linked file", path); + return; + } else { + mbfd = open(path, O_APPEND|O_WRONLY, 0); + if (mbfd != -1 && + (fstat(mbfd, &fsb) || fsb.st_nlink != 1 || + S_ISLNK(fsb.st_mode) || sb.st_dev != fsb.st_dev || + sb.st_ino != fsb.st_ino)) { + warn("%s: file changed after open", path); + (void)close(mbfd); + return; + } + } + + if (mbfd == -1) { + e_to_sys(errno); + warn("%s: %s", path, strerror(errno)); + return; + } + + /* Wait until we can get a lock on the file. */ + if (flock(mbfd, LOCK_EX)) { + e_to_sys(errno); + warn("%s: %s", path, strerror(errno)); + goto err1; + } + + /* Get the starting offset of the new message for biff. */ + curoff = lseek(mbfd, (off_t)0, SEEK_END); + (void)snprintf(biffmsg, sizeof(biffmsg), "%s@%qd\n", name, curoff); + + /* Copy the message into the file. */ + if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) { + e_to_sys(errno); + warn("temporary file: %s", strerror(errno)); + goto err1; + } + while ((nr = read(fd, buf, sizeof(buf))) > 0) + for (off = 0; off < nr; nr -= nw, off += nw) + if ((nw = write(mbfd, buf + off, nr)) < 0) { + e_to_sys(errno); + warn("%s: %s", path, strerror(errno)); + goto err2; + } + if (nr < 0) { + e_to_sys(errno); + warn("temporary file: %s", strerror(errno)); +err2: (void)ftruncate(mbfd, curoff); +err1: (void)close(mbfd); + return; + } + +#ifndef DONT_FSYNC + /* Flush to disk, don't wait for update. */ + if (fsync(mbfd)) { + e_to_sys(errno); + warn("%s: %s", path, strerror(errno)); + goto err2; + } +#endif + + /* Close and check -- NFS doesn't write until the close. */ + if (close(mbfd)) { + e_to_sys(errno); + warn("%s: %s", path, strerror(errno)); + return; + } + + notifybiff(biffmsg); +} + +void +notifybiff(msg) + char *msg; +{ + static struct sockaddr_in addr; + static int f = -1; + struct hostent *hp; + struct servent *sp; + int len; + + if (!addr.sin_family) { + /* Be silent if biff service not available. */ + if (!(sp = getservbyname("biff", "udp"))) + return; + if (!(hp = gethostbyname("localhost"))) { + warn("localhost: %s", strerror(errno)); + return; + } + addr.sin_family = hp->h_addrtype; + memmove(&addr.sin_addr, hp->h_addr, hp->h_length); + addr.sin_port = sp->s_port; + } + if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + warn("socket: %s", strerror(errno)); + return; + } + len = strlen(msg) + 1; + if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr)) + != len) + warn("sendto biff: %s", strerror(errno)); +} + +void +usage() +{ + eval = EX_USAGE; + err("usage: mail.local [-f from] user ..."); +} + +#if __STDC__ +void +err(const char *fmt, ...) +#else +void +err(fmt, va_alist) + const char *fmt; + va_dcl +#endif +{ + va_list ap; + +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vwarn(fmt, ap); + va_end(ap); + + exit(eval); +} + +void +#if __STDC__ +warn(const char *fmt, ...) +#else +warn(fmt, va_alist) + const char *fmt; + va_dcl +#endif +{ + va_list ap; + +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vwarn(fmt, ap); + va_end(ap); +} + +void +vwarn(fmt, ap) + const char *fmt; + _BSD_VA_LIST_ ap; +{ + /* + * Log the message to stderr. + * + * Don't use LOG_PERROR as an openlog() flag to do this, + * it's not portable enough. + */ + if (eval != EX_USAGE) + (void)fprintf(stderr, "mail.local: "); + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, "\n"); + + /* Log the message to syslog. */ + vsyslog(LOG_ERR, fmt, ap); +} + +/* + * e_to_sys -- + * Guess which errno's are temporary. Gag me. + */ +void +e_to_sys(num) + int num; +{ + /* Temporary failures override hard errors. */ + if (eval == EX_TEMPFAIL) + return; + + switch(num) { /* Hopefully temporary errors. */ +#ifdef EAGAIN + case EAGAIN: /* Resource temporarily unavailable */ +#endif +#ifdef EDQUOT + case EDQUOT: /* Disc quota exceeded */ +#endif +#ifdef EBUSY + case EBUSY: /* Device busy */ +#endif +#ifdef EPROCLIM + case EPROCLIM: /* Too many processes */ +#endif +#ifdef EUSERS + case EUSERS: /* Too many users */ +#endif +#ifdef ECONNABORTED + case ECONNABORTED: /* Software caused connection abort */ +#endif +#ifdef ECONNREFUSED + case ECONNREFUSED: /* Connection refused */ +#endif +#ifdef ECONNRESET + case ECONNRESET: /* Connection reset by peer */ +#endif +#ifdef EDEADLK + case EDEADLK: /* Resource deadlock avoided */ +#endif +#ifdef EFBIG + case EFBIG: /* File too large */ +#endif +#ifdef EHOSTDOWN + case EHOSTDOWN: /* Host is down */ +#endif +#ifdef EHOSTUNREACH + case EHOSTUNREACH: /* No route to host */ +#endif +#ifdef EMFILE + case EMFILE: /* Too many open files */ +#endif +#ifdef ENETDOWN + case ENETDOWN: /* Network is down */ +#endif +#ifdef ENETRESET + case ENETRESET: /* Network dropped connection on reset */ +#endif +#ifdef ENETUNREACH + case ENETUNREACH: /* Network is unreachable */ +#endif +#ifdef ENFILE + case ENFILE: /* Too many open files in system */ +#endif +#ifdef ENOBUFS + case ENOBUFS: /* No buffer space available */ +#endif +#ifdef ENOMEM + case ENOMEM: /* Cannot allocate memory */ +#endif +#ifdef ENOSPC + case ENOSPC: /* No space left on device */ +#endif +#ifdef EROFS + case EROFS: /* Read-only file system */ +#endif +#ifdef ESTALE + case ESTALE: /* Stale NFS file handle */ +#endif +#ifdef ETIMEDOUT + case ETIMEDOUT: /* Connection timed out */ +#endif +#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) + case EWOULDBLOCK: /* Operation would block. */ +#endif + eval = EX_TEMPFAIL; + break; + default: + eval = EX_UNAVAILABLE; + break; + } +} diff --git a/libexec/mail.local/pathnames.h b/libexec/mail.local/pathnames.h new file mode 100644 index 0000000..8e43925 --- /dev/null +++ b/libexec/mail.local/pathnames.h @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + */ +#include <paths.h> + +#define _PATH_LOCTMP "/tmp/local.XXXXXX" diff --git a/libexec/makekey/Makefile b/libexec/makekey/Makefile new file mode 100644 index 0000000..47cc1c6 --- /dev/null +++ b/libexec/makekey/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= makekey +MAN8= makekey.8 + +DPADD+= ${LIBCRYPT} +LDADD+= -lcrypt + +.include <bsd.prog.mk> diff --git a/libexec/makekey/makekey.8 b/libexec/makekey/makekey.8 new file mode 100644 index 0000000..c6ded2d --- /dev/null +++ b/libexec/makekey/makekey.8 @@ -0,0 +1,59 @@ +.\" Copyright (c) 1990, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)makekey.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt MAKEKEY 8 +.Os +.Sh NAME +.Nm makekey +.Nd make encrypted keys or passwords +.Sh SYNOPSIS +.Nm makekey +.Sh DESCRIPTION +.Nm Makekey +encrypts a key and salt which it reads from the standard input +and writes the result to the standard output. +The key is expected to be +ten bytes; the salt is expected to be two bytes. +See +.Xr crypt 3 +for more information on what characters the key and salt can contain +and how the encrypted value is calculated. +.Sh SEE ALSO +.Xr login 1 , +.Xr crypt 1 , +.Xr crypt 3 +.Sh HISTORY +A +.Nm +command appeared in Version 7 AT&T UNIX. diff --git a/libexec/makekey/makekey.c b/libexec/makekey/makekey.c new file mode 100644 index 0000000..95febcf --- /dev/null +++ b/libexec/makekey/makekey.c @@ -0,0 +1,82 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)makekey.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static void get __P((char *, int)); + +int +main() +{ + int len; + char *r, key[9], salt[3]; + + get(key, sizeof(key) - 1); + get(salt, sizeof(salt) - 1); + len = strlen(r = crypt(key, salt)); + if (write(STDOUT_FILENO, r, len) != len) + err(1, "stdout"); + exit(0); +} + +static void +get(bp, len) + char *bp; + register int len; +{ + register int nr; + + bp[len] = '\0'; + if ((nr = read(STDIN_FILENO, bp, len)) == len) + return; + if (nr >= 0) + errno = EFTYPE; + err(1, "stdin"); +} diff --git a/libexec/rbootd/Makefile b/libexec/rbootd/Makefile new file mode 100644 index 0000000..369b1e7 --- /dev/null +++ b/libexec/rbootd/Makefile @@ -0,0 +1,12 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= rbootd +SRCS= bpf.c conf.c parseconf.c rbootd.c rmpproto.c utils.c +MAN8= rbootd.8 + +# XXX BROKEN: afterinstall: +XXXafterinstall: + (cd ${.CURDIR}/bootdir && ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} \ + -m 444 * ${DESTDIR}/usr/mdec/) + +.include <bsd.prog.mk> diff --git a/libexec/rbootd/bpf.c b/libexec/rbootd/bpf.c new file mode 100644 index 0000000..f5e8bce --- /dev/null +++ b/libexec/rbootd/bpf.c @@ -0,0 +1,422 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)bpf.c 8.1 (Berkeley) 6/4/93 + * + * Utah $Hdr: bpf.c 3.1 92/07/06$ + * Author: Jeff Forys, University of Utah CSS + */ + +#ifndef lint +static char sccsid[] = "@(#)bpf.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/bpf.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include "defs.h" +#include "pathnames.h" + +static int BpfFd = -1; +static unsigned BpfLen = 0; +static u_char *BpfPkt = NULL; + +/* +** BpfOpen -- Open and initialize a BPF device. +** +** Parameters: +** None. +** +** Returns: +** File descriptor of opened BPF device (for select() etc). +** +** Side Effects: +** If an error is encountered, the program terminates here. +*/ +int +BpfOpen() +{ + struct ifreq ifr; + char bpfdev[32]; + int n = 0; + + /* + * Open the first available BPF device. + */ + do { + (void) sprintf(bpfdev, _PATH_BPF, n++); + BpfFd = open(bpfdev, O_RDWR); + } while (BpfFd < 0 && (errno == EBUSY || errno == EPERM)); + + if (BpfFd < 0) { + syslog(LOG_ERR, "bpf: no available devices: %m"); + Exit(0); + } + + /* + * Set interface name for bpf device, get data link layer + * type and make sure it's type Ethernet. + */ + (void) strncpy(ifr.ifr_name, IntfName, sizeof(ifr.ifr_name)); + if (ioctl(BpfFd, BIOCSETIF, (caddr_t)&ifr) < 0) { + syslog(LOG_ERR, "bpf: ioctl(BIOCSETIF,%s): %m", IntfName); + Exit(0); + } + + /* + * Make sure we are dealing with an Ethernet device. + */ + if (ioctl(BpfFd, BIOCGDLT, (caddr_t)&n) < 0) { + syslog(LOG_ERR, "bpf: ioctl(BIOCGDLT): %m"); + Exit(0); + } + if (n != DLT_EN10MB) { + syslog(LOG_ERR,"bpf: %s: data-link type %d unsupported", + IntfName, n); + Exit(0); + } + + /* + * On read(), return packets immediately (do not buffer them). + */ + n = 1; + if (ioctl(BpfFd, BIOCIMMEDIATE, (caddr_t)&n) < 0) { + syslog(LOG_ERR, "bpf: ioctl(BIOCIMMEDIATE): %m"); + Exit(0); + } + + /* + * Try to enable the chip/driver's multicast address filter to + * grab our RMP address. If this fails, try promiscuous mode. + * If this fails, there's no way we are going to get any RMP + * packets so just exit here. + */ +#ifdef MSG_EOR + ifr.ifr_addr.sa_len = RMP_ADDRLEN + 2; +#endif + ifr.ifr_addr.sa_family = AF_UNSPEC; + bcopy(&RmpMcastAddr[0], (char *)&ifr.ifr_addr.sa_data[0], RMP_ADDRLEN); + if (ioctl(BpfFd, SIOCADDMULTI, (caddr_t)&ifr) < 0) { + syslog(LOG_WARNING, + "bpf: can't add mcast addr (%m), setting promiscuous mode"); + + if (ioctl(BpfFd, BIOCPROMISC, (caddr_t)0) < 0) { + syslog(LOG_ERR, "bpf: can't set promiscuous mode: %m"); + Exit(0); + } + } + + /* + * Ask BPF how much buffer space it requires and allocate one. + */ + if (ioctl(BpfFd, BIOCGBLEN, (caddr_t)&BpfLen) < 0) { + syslog(LOG_ERR, "bpf: ioctl(BIOCGBLEN): %m"); + Exit(0); + } + if (BpfPkt == NULL) + BpfPkt = (u_char *)malloc(BpfLen); + + if (BpfPkt == NULL) { + syslog(LOG_ERR, "bpf: out of memory (%u bytes for bpfpkt)", + BpfLen); + Exit(0); + } + + /* + * Write a little program to snarf RMP Boot packets and stuff + * it down BPF's throat (i.e. set up the packet filter). + */ + { +#define RMP ((struct rmp_packet *)0) + static struct bpf_insn bpf_insn[] = { + { BPF_LD|BPF_B|BPF_ABS, 0, 0, (long)&RMP->hp_llc.dsap }, + { BPF_JMP|BPF_JEQ|BPF_K, 0, 5, IEEE_DSAP_HP }, + { BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&RMP->hp_llc.cntrl }, + { BPF_JMP|BPF_JEQ|BPF_K, 0, 3, IEEE_CNTL_HP }, + { BPF_LD|BPF_H|BPF_ABS, 0, 0, (long)&RMP->hp_llc.dxsap }, + { BPF_JMP|BPF_JEQ|BPF_K, 0, 1, HPEXT_DXSAP }, + { BPF_RET|BPF_K, 0, 0, RMP_MAX_PACKET }, + { BPF_RET|BPF_K, 0, 0, 0x0 } + }; +#undef RMP + static struct bpf_program bpf_pgm = { + sizeof(bpf_insn)/sizeof(bpf_insn[0]), bpf_insn + }; + + if (ioctl(BpfFd, BIOCSETF, (caddr_t)&bpf_pgm) < 0) { + syslog(LOG_ERR, "bpf: ioctl(BIOCSETF): %m"); + Exit(0); + } + } + + return(BpfFd); +} + +/* +** BPF GetIntfName -- Return the name of a network interface attached to +** the system, or 0 if none can be found. The interface +** must be configured up; the lowest unit number is +** preferred; loopback is ignored. +** +** Parameters: +** errmsg - if no network interface found, *errmsg explains why. +** +** Returns: +** A (static) pointer to interface name, or NULL on error. +** +** Side Effects: +** None. +*/ +char * +BpfGetIntfName(errmsg) + char **errmsg; +{ + struct ifreq ibuf[8], *ifrp, *ifend, *mp; + struct ifconf ifc; + int fd; + int minunit, n; + char *cp; + static char device[sizeof(ifrp->ifr_name)]; + static char errbuf[128] = "No Error!"; + + if (errmsg != NULL) + *errmsg = errbuf; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + (void) strcpy(errbuf, "bpf: socket: %m"); + return(NULL); + } + ifc.ifc_len = sizeof ibuf; + ifc.ifc_buf = (caddr_t)ibuf; + +#ifdef OSIOCGIFCONF + if (ioctl(fd, OSIOCGIFCONF, (char *)&ifc) < 0 || + ifc.ifc_len < sizeof(struct ifreq)) { + (void) strcpy(errbuf, "bpf: ioctl(OSIOCGIFCONF): %m"); + return(NULL); + } +#else + if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) < 0 || + ifc.ifc_len < sizeof(struct ifreq)) { + (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFCONF): %m"); + return(NULL); + } +#endif + ifrp = ibuf; + ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len); + + mp = 0; + minunit = 666; + for (; ifrp < ifend; ++ifrp) { + if (ioctl(fd, SIOCGIFFLAGS, (char *)ifrp) < 0) { + (void) strcpy(errbuf, "bpf: ioctl(SIOCGIFFLAGS): %m"); + return(NULL); + } + + /* + * If interface is down or this is the loopback interface, + * ignore it. + */ + if ((ifrp->ifr_flags & IFF_UP) == 0 || +#ifdef IFF_LOOPBACK + (ifrp->ifr_flags & IFF_LOOPBACK)) +#else + (strcmp(ifrp->ifr_name, "lo0") == 0)) +#endif + continue; + + for (cp = ifrp->ifr_name; !isdigit(*cp); ++cp) + ; + n = atoi(cp); + if (n < minunit) { + minunit = n; + mp = ifrp; + } + } + + (void) close(fd); + if (mp == 0) { + (void) strcpy(errbuf, "bpf: no interfaces found"); + return(NULL); + } + + (void) strcpy(device, mp->ifr_name); + return(device); +} + +/* +** BpfRead -- Read packets from a BPF device and fill in `rconn'. +** +** Parameters: +** rconn - filled in with next packet. +** doread - is True if we can issue a read() syscall. +** +** Returns: +** True if `rconn' contains a new packet, False otherwise. +** +** Side Effects: +** None. +*/ +int +BpfRead(rconn, doread) + RMPCONN *rconn; + int doread; +{ + register int datlen, caplen, hdrlen; + static u_char *bp = NULL, *ep = NULL; + int cc; + + /* + * The read() may block, or it may return one or more packets. + * We let the caller decide whether or not we can issue a read(). + */ + if (doread) { + if ((cc = read(BpfFd, (char *)BpfPkt, (int)BpfLen)) < 0) { + syslog(LOG_ERR, "bpf: read: %m"); + return(0); + } else { + bp = BpfPkt; + ep = BpfPkt + cc; + } + } + +#define bhp ((struct bpf_hdr *)bp) + /* + * If there is a new packet in the buffer, stuff it into `rconn' + * and return a success indication. + */ + if (bp < ep) { + datlen = bhp->bh_datalen; + caplen = bhp->bh_caplen; + hdrlen = bhp->bh_hdrlen; + + if (caplen != datlen) + syslog(LOG_ERR, + "bpf: short packet dropped (%d of %d bytes)", + caplen, datlen); + else if (caplen > sizeof(struct rmp_packet)) + syslog(LOG_ERR, "bpf: large packet dropped (%d bytes)", + caplen); + else { + rconn->rmplen = caplen; + bcopy((char *)&bhp->bh_tstamp, (char *)&rconn->tstamp, + sizeof(struct timeval)); + bcopy((char *)bp + hdrlen, (char *)&rconn->rmp, caplen); + } + bp += BPF_WORDALIGN(caplen + hdrlen); + return(1); + } +#undef bhp + + return(0); +} + +/* +** BpfWrite -- Write packet to BPF device. +** +** Parameters: +** rconn - packet to send. +** +** Returns: +** True if write succeeded, False otherwise. +** +** Side Effects: +** None. +*/ +int +BpfWrite(rconn) + RMPCONN *rconn; +{ + if (write(BpfFd, (char *)&rconn->rmp, rconn->rmplen) < 0) { + syslog(LOG_ERR, "write: %s: %m", EnetStr(rconn)); + return(0); + } + + return(1); +} + +/* +** BpfClose -- Close a BPF device. +** +** Parameters: +** None. +** +** Returns: +** Nothing. +** +** Side Effects: +** None. +*/ +void +BpfClose() +{ + struct ifreq ifr; + + if (BpfPkt != NULL) { + free((char *)BpfPkt); + BpfPkt = NULL; + } + + if (BpfFd == -1) + return; + +#ifdef MSG_EOR + ifr.ifr_addr.sa_len = RMP_ADDRLEN + 2; +#endif + ifr.ifr_addr.sa_family = AF_UNSPEC; + bcopy(&RmpMcastAddr[0], (char *)&ifr.ifr_addr.sa_data[0], RMP_ADDRLEN); + if (ioctl(BpfFd, SIOCDELMULTI, (caddr_t)&ifr) < 0) + (void) ioctl(BpfFd, BIOCPROMISC, (caddr_t)0); + + (void) close(BpfFd); + BpfFd = -1; +} diff --git a/libexec/rbootd/conf.c b/libexec/rbootd/conf.c new file mode 100644 index 0000000..b890eea --- /dev/null +++ b/libexec/rbootd/conf.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)conf.c 8.1 (Berkeley) 6/4/93 + * + * Utah $Hdr: conf.c 3.1 92/07/06$ + * Author: Jeff Forys, University of Utah CSS + */ + +#ifndef lint +static char sccsid[] = "@(#)conf.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> + +#include <stdio.h> +#include "defs.h" +#include "pathnames.h" + +/* +** Define (and possibly initialize) global variables here. +** +** Caveat: +** The maximum number of bootable files (`char *BootFiles[]') is +** limited to C_MAXFILE (i.e. the maximum number of files that +** can be spec'd in the configuration file). This was done to +** simplify the boot file search code. +*/ + +char *ProgName; /* path-stripped argv[0] */ +char MyHost[MAXHOSTNAMELEN+1]; /* host name */ +int MyPid; /* process id */ +int DebugFlg = 0; /* set true if debugging */ +int BootAny = 0; /* set true if we boot anyone */ + +char *ConfigFile = NULL; /* configuration file */ +char *DfltConfig = _PATH_RBOOTDCONF; /* default configuration file */ +char *PidFile = _PATH_RBOOTDPID; /* file w/pid of server */ +char *BootDir = _PATH_RBOOTDLIB; /* directory w/boot files */ +char *DbgFile = _PATH_RBOOTDDBG; /* debug output file */ + +FILE *DbgFp = NULL; /* debug file pointer */ +char *IntfName = NULL; /* intf we are attached to */ + +u_short SessionID = 0; /* generated session ID */ + +char *BootFiles[C_MAXFILE]; /* list of boot files */ + +CLIENT *Clients = NULL; /* list of addrs we'll accept */ +RMPCONN *RmpConns = NULL; /* list of active connections */ + +char RmpMcastAddr[RMP_ADDRLEN] = RMP_ADDR; /* RMP multicast address */ diff --git a/libexec/rbootd/defs.h b/libexec/rbootd/defs.h new file mode 100644 index 0000000..a2e0cd0 --- /dev/null +++ b/libexec/rbootd/defs.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)defs.h 8.1 (Berkeley) 6/4/93 + * + * Utah $Hdr: defs.h 3.1 92/07/06$ + * Author: Jeff Forys, University of Utah CSS + */ + +#include "rmp.h" +#include "rmp_var.h" + +/* +** Common #define's and external variables. All other files should +** include this. +*/ + +/* + * This may be defined in <sys/param.h>, if not, it's defined here. + */ +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 +#endif + +/* + * SIGUSR1 and SIGUSR2 are defined in <signal.h> for 4.3BSD systems. + */ +#ifndef SIGUSR1 +#define SIGUSR1 SIGEMT +#endif +#ifndef SIGUSR2 +#define SIGUSR2 SIGFPE +#endif + +/* + * These can be faster & more efficient than strcmp()/strncmp()... + */ +#define STREQN(s1,s2) ((*s1 == *s2) && (strcmp(s1,s2) == 0)) +#define STRNEQN(s1,s2,n) ((*s1 == *s2) && (strncmp(s1,s2,n) == 0)) + +/* + * Configuration file limitations. + */ +#define C_MAXFILE 10 /* max number of boot-able files */ +#define C_LINELEN 1024 /* max length of line */ + +/* + * Direction of packet (used as argument to DispPkt). + */ +#define DIR_RCVD 0 +#define DIR_SENT 1 +#define DIR_NONE 2 + +/* + * These need not be functions, so... + */ +#define FreeStr(str) free(str) +#define FreeClient(cli) free(cli) +#define GenSessID() (++SessionID ? SessionID: ++SessionID) + +/* + * Converting an Ethernet address to a string is done in many routines. + * Using `rmp.hp_hdr.saddr' works because this field is *never* changed; + * it will *always* contain the source address of the packet. + */ +#define EnetStr(rptr) GetEtherAddr(&(rptr)->rmp.hp_hdr.saddr[0]) + +/* + * Every machine we can boot will have one of these allocated for it + * (unless there are no restrictions on who we can boot). + */ +typedef struct client_s { + u_char addr[RMP_ADDRLEN]; /* addr of machine */ + char *files[C_MAXFILE]; /* boot-able files */ + struct client_s *next; /* ptr to next */ +} CLIENT; + +/* + * Every active connection has one of these allocated for it. + */ +typedef struct rmpconn_s { + struct rmp_packet rmp; /* RMP packet */ + int rmplen; /* length of packet */ + struct timeval tstamp; /* last time active */ + int bootfd; /* open boot file */ + struct rmpconn_s *next; /* ptr to next */ +} RMPCONN; + +/* + * All these variables are defined in "conf.c". + */ +extern char *ProgName; /* path-stripped argv[0] */ +extern char MyHost[]; /* this hosts' name */ +extern int MyPid; /* this processes' ID */ +extern int DebugFlg; /* set true if debugging */ +extern int BootAny; /* set true if we can boot anyone */ + +extern char *ConfigFile; /* configuration file */ +extern char *DfltConfig; /* default configuration file */ +extern char *DbgFile; /* debug output file */ +extern char *PidFile; /* file containing pid of server */ +extern char *BootDir; /* directory w/boot files */ + +extern FILE *DbgFp; /* debug file pointer */ +extern char *IntfName; /* interface we are attached to */ + +extern u_short SessionID; /* generated session ID */ + +extern char *BootFiles[]; /* list of boot files */ + +extern CLIENT *Clients; /* list of addrs we'll accept */ +extern RMPCONN *RmpConns; /* list of active connections */ + +extern char RmpMcastAddr[]; /* RMP multicast address */ + +void AddConn __P((RMPCONN *)); +int BootDone __P((RMPCONN *)); +void BpfClose __P((void)); +char *BpfGetIntfName __P((char **)); +int BpfOpen __P((void)); +int BpfRead __P((RMPCONN *, int)); +int BpfWrite __P((RMPCONN *)); +void DebugOff __P((int)); +void DebugOn __P((int)); +void DispPkt __P((RMPCONN *, int)); +void DoTimeout __P((void)); +void DspFlnm __P((u_int, char *)); +void Exit __P((int)); +CLIENT *FindClient __P((RMPCONN *)); +RMPCONN *FindConn __P((RMPCONN *)); +void FreeClients __P((void)); +void FreeConn __P((RMPCONN *)); +void FreeConns __P((void)); +int GetBootFiles __P((void)); +char *GetEtherAddr __P((u_char *)); +CLIENT *NewClient __P((u_char *)); +RMPCONN *NewConn __P((RMPCONN *)); +char *NewStr __P((char *)); +u_char *ParseAddr __P((char *)); +int ParseConfig __P((void)); +void ProcessPacket __P((RMPCONN *, CLIENT *)); +void ReConfig __P((int)); +void RemoveConn __P((RMPCONN *)); +int SendBootRepl __P((struct rmp_packet *, RMPCONN *, char *[])); +int SendFileNo __P((struct rmp_packet *, RMPCONN *, char *[])); +int SendPacket __P((RMPCONN *)); +int SendReadRepl __P((RMPCONN *)); +int SendServerID __P((RMPCONN *)); diff --git a/libexec/rbootd/parseconf.c b/libexec/rbootd/parseconf.c new file mode 100644 index 0000000..a01a4a5 --- /dev/null +++ b/libexec/rbootd/parseconf.c @@ -0,0 +1,359 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)parseconf.c 8.1 (Berkeley) 6/4/93 + * + * Utah $Hdr: parseconf.c 3.1 92/07/06$ + * Author: Jeff Forys, University of Utah CSS + */ + +#ifndef lint +static char sccsid[] = "@(#)parseconf.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <dirent.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include "defs.h" + +/* +** ParseConfig -- parse the config file into linked list of clients. +** +** Parameters: +** None. +** +** Returns: +** 1 on success, 0 otherwise. +** +** Side Effects: +** - Linked list of clients will be (re)allocated. +** +** Warnings: +** - GetBootFiles() must be called before this routine +** to create a linked list of default boot files. +*/ +int +ParseConfig() +{ + FILE *fp; + CLIENT *client; + u_char *addr; + char line[C_LINELEN]; + register char *cp, *bcp; + register int i, j; + int omask, linecnt = 0; + + if (BootAny) /* ignore config file */ + return(1); + + FreeClients(); /* delete old list of clients */ + + if ((fp = fopen(ConfigFile, "r")) == NULL) { + syslog(LOG_ERR, "ParseConfig: can't open config file (%s)", + ConfigFile); + return(0); + } + + /* + * We've got to block SIGHUP to prevent reconfiguration while + * dealing with the linked list of Clients. This can be done + * when actually linking the new client into the list, but + * this could have unexpected results if the server was HUP'd + * whilst reconfiguring. Hence, it is done here. + */ + omask = sigblock(sigmask(SIGHUP)); + + /* + * GETSTR positions `bcp' at the start of the current token, + * and null terminates it. `cp' is positioned at the start + * of the next token. spaces & commas are separators. + */ +#define GETSTR while (isspace(*cp) || *cp == ',') cp++; \ + bcp = cp; \ + while (*cp && *cp!=',' && !isspace(*cp)) cp++; \ + if (*cp) *cp++ = '\0' + + /* + * For each line, parse it into a new CLIENT struct. + */ + while (fgets(line, C_LINELEN, fp) != NULL) { + linecnt++; /* line counter */ + + if (*line == '\0' || *line == '#') /* ignore comment */ + continue; + + if ((cp = index(line,'#')) != NULL) /* trash comments */ + *cp = '\0'; + + cp = line; /* init `cp' */ + GETSTR; /* get RMP addr */ + if (bcp == cp) /* all delimiters */ + continue; + + /* + * Get an RMP address from a string. Abort on failure. + */ + if ((addr = ParseAddr(bcp)) == NULL) { + syslog(LOG_ERR, + "ParseConfig: line %d: cant parse <%s>", + linecnt, bcp); + continue; + } + + if ((client = NewClient(addr)) == NULL) /* alloc new client */ + continue; + + GETSTR; /* get first file */ + + /* + * If no boot files are spec'd, use the default list. + * Otherwise, validate each file (`bcp') against the + * list of boot-able files. + */ + i = 0; + if (bcp == cp) /* no files spec'd */ + for (; i < C_MAXFILE && BootFiles[i] != NULL; i++) + client->files[i] = BootFiles[i]; + else { + do { + /* + * For each boot file spec'd, make sure it's + * in our list. If so, include a pointer to + * it in the CLIENT's list of boot files. + */ + for (j = 0; ; j++) { + if (j==C_MAXFILE||BootFiles[j]==NULL) { + syslog(LOG_ERR, "ParseConfig: line %d: no boot file (%s)", + linecnt, bcp); + break; + } + if (STREQN(BootFiles[j], bcp)) { + if (i < C_MAXFILE) + client->files[i++] = + BootFiles[j]; + else + syslog(LOG_ERR, "ParseConfig: line %d: too many boot files (%s)", + linecnt, bcp); + break; + } + } + GETSTR; /* get next file */ + } while (bcp != cp); + + /* + * Restricted list of boot files were spec'd, + * however, none of them were found. Since we + * apparently cant let them boot "just anything", + * the entire record is invalidated. + */ + if (i == 0) { + FreeClient(client); + continue; + } + } + + /* + * Link this client into the linked list of clients. + * SIGHUP has already been blocked. + */ + if (Clients) + client->next = Clients; + Clients = client; + } + + (void) fclose(fp); /* close config file */ + + (void) sigsetmask(omask); /* reset signal mask */ + + return(1); /* return success */ +} + +/* +** ParseAddr -- Parse a string containing an RMP address. +** +** This routine is fairly liberal at parsing an RMP address. The +** address must contain 6 octets consisting of between 0 and 2 hex +** chars (upper/lower case) separated by colons. If two colons are +** together (e.g. "::", the octet between them is recorded as being +** zero. Hence, the following addrs are all valid and parse to the +** same thing: +** +** 08:00:09:00:66:ad 8::9:0:66:AD 8::9::66:aD +** +** For clarity, an RMP address is really an Ethernet address, but +** since the HP boot code uses IEEE 802.3, it's really an IEEE +** 802.3 address. Of course, all of these are identical. +** +** Parameters: +** str - string representation of an RMP address. +** +** Returns: +** pointer to a static array of RMP_ADDRLEN bytes. +** +** Side Effects: +** None. +** +** Warnings: +** - The return value points to a static buffer; it must +** be copied if it's to be saved. +** - For speed, we assume a u_char consists of 8 bits. +*/ +u_char * +ParseAddr(str) + char *str; +{ + static u_char addr[RMP_ADDRLEN]; + register char *cp; + register unsigned i; + register int part, subpart; + + bzero((char *)&addr[0], RMP_ADDRLEN); /* zero static buffer */ + + part = subpart = 0; + for (cp = str; *cp; cp++) { + /* + * A colon (`:') must be used to delimit each octet. + */ + if (*cp == ':') { + if (++part == RMP_ADDRLEN) /* too many parts */ + return(NULL); + subpart = 0; + continue; + } + + /* + * Convert hex character to an integer. + */ + if (isdigit(*cp)) + i = *cp - '0'; + else { + i = (isupper(*cp)? tolower(*cp): *cp) - 'a' + 10; + if (i < 10 || i > 15) /* not a hex char */ + return(NULL); + } + + if (subpart++) { + if (subpart > 2) /* too many hex chars */ + return(NULL); + addr[part] <<= 4; + } + addr[part] |= i; + } + + if (part != (RMP_ADDRLEN-1)) /* too few parts */ + return(NULL); + + return(&addr[0]); +} + +/* +** GetBootFiles -- record list of files in current (boot) directory. +** +** Parameters: +** None. +** +** Returns: +** Number of boot files on success, 0 on failure. +** +** Side Effects: +** Strings in `BootFiles' are freed/allocated. +** +** Warnings: +** - After this routine is called, ParseConfig() must be +** called to re-order it's list of boot file pointers. +*/ +int +GetBootFiles() +{ + DIR *dfd; + struct stat statb; + register struct dirent *dp; + register int i; + + /* + * Free the current list of boot files. + */ + for (i = 0; i < C_MAXFILE && BootFiles[i] != NULL; i++) { + FreeStr(BootFiles[i]); + BootFiles[i] = NULL; + } + + /* + * Open current directory to read boot file names. + */ + if ((dfd = opendir(".")) == NULL) { /* open BootDir */ + syslog(LOG_ERR, "GetBootFiles: can't open directory (%s)\n", + BootDir); + return(0); + } + + /* + * Read each boot file name and allocate space for it in the + * list of boot files (BootFiles). All boot files read after + * C_MAXFILE will be ignored. + */ + i = 0; + for (dp = readdir(dfd); dp != NULL; dp = readdir(dfd)) { + if (stat(dp->d_name, &statb) < 0 || + (statb.st_mode & S_IFMT) != S_IFREG) + continue; + if (i == C_MAXFILE) + syslog(LOG_ERR, + "GetBootFiles: too many boot files (%s ignored)", + dp->d_name); + else if ((BootFiles[i] = NewStr(dp->d_name)) != NULL) + i++; + } + + (void) closedir(dfd); /* close BootDir */ + + if (i == 0) /* cant find any boot files */ + syslog(LOG_ERR, "GetBootFiles: no boot files (%s)\n", BootDir); + + return(i); +} diff --git a/libexec/rbootd/pathnames.h b/libexec/rbootd/pathnames.h new file mode 100644 index 0000000..d960dce --- /dev/null +++ b/libexec/rbootd/pathnames.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + * + * Utah $Hdr: pathnames.h 3.1 92/07/06$ + * Author: Jeff Forys, University of Utah CSS + */ + +#define _PATH_BPF "/dev/bpf%d" +#define _PATH_RBOOTDCONF "/etc/rbootd.conf" +#define _PATH_RBOOTDDBG "/tmp/rbootd.dbg" +#define _PATH_RBOOTDLIB "/usr/mdec/rbootd" +#define _PATH_RBOOTDPID "/var/run/rbootd.pid" diff --git a/libexec/rbootd/rbootd.8 b/libexec/rbootd/rbootd.8 new file mode 100644 index 0000000..f4eb364 --- /dev/null +++ b/libexec/rbootd/rbootd.8 @@ -0,0 +1,156 @@ +.\" Copyright (c) 1988, 1992 The University of Utah and the Center +.\" for Software Science (CSS). +.\" Copyright (c) 1992, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Center for Software Science of the University of Utah Computer +.\" Science Department. CSS requests users of this software to return +.\" to css-dist@cs.utah.edu any improvements that they make and grant +.\" CSS redistribution rights. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)rbootd.8 8.2 (Berkeley) 12/11/93 +.\" +.\" Utah $Hdr: rbootd.man 3.1 92/07/06$ +.\" Author: Jeff Forys, University of Utah CSS +.\" +.Dd "December 11, 1993" +.Dt RBOOTD 8 +.Os +.Sh NAME +.Nm rbootd +.Nd HP remote boot server +.Sh SYNOPSIS +.Nm rbootd +.Op Fl ad +.Op Fl i Ar interface +.Op config_file +.Sh DESCRIPTION +The +.Nm rbootd +utility services boot requests from Hewlett-Packard workstations over a +local area network. +All boot files must reside in the boot file directory; further, if a +client supplies path information in its boot request, it will be silently +stripped away before processing. +By default, +.Nm rbootd +only responds to requests from machines listed in its configuration file. +.Pp +The options are as follows: +.Bl -tag -width Fl +.It Fl a +Respond to boot requests from any machine. +The configuration file is ignored if this option is specified. +.It Fl d +Run +.Nm rbootd +in debug mode. +Packets sent and received are displayed to the terminal. +.It Fl i Ar interface +Service boot requests on specified interface. +If unspecified, +.Nm rbootd +searches the system interface list for the lowest numbered, configured +``up'' interface (excluding loopback). +Ties are broken by choosing the earliest match. +.El +.Pp +Specifying +.Ar config_file +on the command line causes +.Nm rbootd +to use a different configuration file from the default. +.Pp +The configuration file is a text file where each line describes a particular +machine. +A line must start with a machine's Ethernet address followed by an optional +list of boot file names. +An Ethernet address is specified in hexadecimal with each of its six octets +separated by a colon. +The boot file names come from the boot file directory. +The ethernet address and boot file(s) must be separated by white-space +and/or comma characters. +A pound sign causes the remainder of a line to be ignored. +.Pp +Here is a sample configuration file: +.Bl -column 08:00:09:0:66:ad SYSHPBSD,SYSHPUX "# vandy (anything)" +.It # +.It # ethernet addr boot file(s) comments +.It # +.It 08:00:09:0:66:ad SYSHPBSD # snake (4.3BSD) +.It 08:00:09:0:59:5b # vandy (anything) +.It 8::9:1:C6:75 SYSHPBSD,SYSHPUX # jaguar (either) +.El +.Pp +.Nm Rbootd +logs status and error messages via +.Xr syslog 3 . +A startup message is always logged, and in the case of fatal errors (or +deadly signals) a message is logged announcing the server's termination. +In general, a non-fatal error is handled by ignoring the event that caused +it (e.g. an invalid Ethernet address in the config file causes that line +to be invalidated). +.Pp +The following signals have the specified effect when sent to the server +process using the +.Xr kill 1 +command: +.Bl -tag -width SIGUSR1 -offset -compact +.It SIGHUP +Drop all active connections and reconfigure. +.It SIGUSR1 +Turn on debugging, do nothing if already on. +.It SIGUSR2 +Turn off debugging, do nothing if already off. +.El +.Sh "FILES" +.Bl -tag -width /usr/libexec/rbootd -compact +.It /dev/bpf# +packet-filter device +.It /etc/rbootd.conf +configuration file +.It /tmp/rbootd.dbg +debug output +.It /usr/mdec/rbootd +directory containing boot files +.It /var/run/rbootd.pid +process id +.El +.Sh SEE ALSO +.Xr kill 1 , +.Xr socket 2 , +.Xr signal 3 , +.Xr syslog 3 , +.Xr rmp 4 +.Sh BUGS +If multiple servers are started on the same interface, each will receive +and respond to the same boot packets. diff --git a/libexec/rbootd/rbootd.c b/libexec/rbootd/rbootd.c new file mode 100644 index 0000000..0164345 --- /dev/null +++ b/libexec/rbootd/rbootd.c @@ -0,0 +1,508 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)rbootd.c 8.2 (Berkeley) 2/22/94 + * + * Utah $Hdr: rbootd.c 3.1 92/07/06$ + * Author: Jeff Forys, University of Utah CSS + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rbootd.c 8.2 (Berkeley) 2/22/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/time.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include "defs.h" + + +/* fd mask macros (backward compatibility with 4.2BSD) */ +#ifndef FD_SET +#ifdef notdef +typedef struct fd_set { /* this should already be in 4.2 */ + int fds_bits[1]; +} fd_set; +#endif +#define FD_ZERO(p) ((p)->fds_bits[0] = 0) +#define FD_SET(n, p) ((p)->fds_bits[0] |= (1 << (n))) +#define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1 << (n))) +#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1 << (n))) +#endif + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int c, fd, omask, maxfds; + fd_set rset; + + /* + * Find what name we are running under. + */ + ProgName = (ProgName = rindex(argv[0],'/')) ? ++ProgName : *argv; + + /* + * Close any open file descriptors. + * Temporarily leave stdin & stdout open for `-d', + * and stderr open for any pre-syslog error messages. + */ + { + int i, nfds = getdtablesize(); + + for (i = 0; i < nfds; i++) + if (i != fileno(stdin) && i != fileno(stdout) && + i != fileno(stderr)) + (void) close(i); + } + + /* + * Parse any arguments. + */ + while ((c = getopt(argc, argv, "adi:")) != EOF) + switch(c) { + case 'a': + BootAny++; + break; + case 'd': + DebugFlg++; + break; + case 'i': + IntfName = optarg; + break; + } + for (; optind < argc; optind++) { + if (ConfigFile == NULL) + ConfigFile = argv[optind]; + else { + fprintf(stderr, + "%s: too many config files (`%s' ignored)\n", + ProgName, argv[optind]); + } + } + + if (ConfigFile == NULL) /* use default config file */ + ConfigFile = DfltConfig; + + if (DebugFlg) { + DbgFp = stdout; /* output to stdout */ + + (void) signal(SIGUSR1, SIG_IGN); /* dont muck w/DbgFp */ + (void) signal(SIGUSR2, SIG_IGN); + } else { + (void) fclose(stdin); /* dont need these */ + (void) fclose(stdout); + + /* + * Fork off a child to do the work & exit. + */ + switch(fork()) { + case -1: /* fork failed */ + fprintf(stderr, "%s: ", ProgName); + perror("fork"); + Exit(0); + case 0: /* this is the CHILD */ + break; + default: /* this is the PARENT */ + _exit(0); + } + + /* + * Try to disassociate from the current tty. + */ + { + char *devtty = "/dev/tty"; + int i; + + if ((i = open(devtty, O_RDWR)) < 0) { + /* probably already disassociated */ + if (setpgrp(0, 0) < 0) { + fprintf(stderr, "%s: ", ProgName); + perror("setpgrp"); + } + } else { + if (ioctl(i, (u_long)TIOCNOTTY, (char *)0) < 0){ + fprintf(stderr, "%s: ", ProgName); + perror("ioctl"); + } + (void) close(i); + } + } + + (void) signal(SIGUSR1, DebugOn); + (void) signal(SIGUSR2, DebugOff); + } + + (void) fclose(stderr); /* finished with it */ + +#ifdef SYSLOG4_2 + openlog(ProgName, LOG_PID); +#else + openlog(ProgName, LOG_PID, LOG_DAEMON); +#endif + + /* + * If no interface was specified, get one now. + * + * This is convoluted because we want to get the default interface + * name for the syslog("restarted") message. If BpfGetIntfName() + * runs into an error, it will return a syslog-able error message + * (in `errmsg') which will be displayed here. + */ + if (IntfName == NULL) { + char *errmsg; + + if ((IntfName = BpfGetIntfName(&errmsg)) == NULL) { + syslog(LOG_NOTICE, "restarted (??)"); + syslog(LOG_ERR, errmsg); + Exit(0); + } + } + + syslog(LOG_NOTICE, "restarted (%s)", IntfName); + + (void) signal(SIGHUP, ReConfig); + (void) signal(SIGINT, Exit); + (void) signal(SIGTERM, Exit); + + /* + * Grab our host name and pid. + */ + if (gethostname(MyHost, MAXHOSTNAMELEN) < 0) { + syslog(LOG_ERR, "gethostname: %m"); + Exit(0); + } + MyHost[MAXHOSTNAMELEN] = '\0'; + + MyPid = getpid(); + + /* + * Write proc's pid to a file. + */ + { + FILE *fp; + + if ((fp = fopen(PidFile, "w")) != NULL) { + (void) fprintf(fp, "%d\n", MyPid); + (void) fclose(fp); + } else { + syslog(LOG_WARNING, "fopen: failed (%s)", PidFile); + } + } + + /* + * All boot files are relative to the boot directory, we might + * as well chdir() there to make life easier. + */ + if (chdir(BootDir) < 0) { + syslog(LOG_ERR, "chdir: %m (%s)", BootDir); + Exit(0); + } + + /* + * Initial configuration. + */ + omask = sigblock(sigmask(SIGHUP)); /* prevent reconfig's */ + if (GetBootFiles() == 0) /* get list of boot files */ + Exit(0); + if (ParseConfig() == 0) /* parse config file */ + Exit(0); + + /* + * Open and initialize a BPF device for the appropriate interface. + * If an error is encountered, a message is displayed and Exit() + * is called. + */ + fd = BpfOpen(); + + (void) sigsetmask(omask); /* allow reconfig's */ + + /* + * Main loop: receive a packet, determine where it came from, + * and if we service this host, call routine to handle request. + */ + maxfds = fd + 1; + FD_ZERO(&rset); + FD_SET(fd, &rset); + for (;;) { + struct timeval timeout; + fd_set r; + int nsel; + + r = rset; + + if (RmpConns == NULL) { /* timeout isnt necessary */ + nsel = select(maxfds, &r, (fd_set *)0, (fd_set *)0, + (struct timeval *)0); + } else { + timeout.tv_sec = RMP_TIMEOUT; + timeout.tv_usec = 0; + nsel = select(maxfds, &r, (fd_set *)0, (fd_set *)0, + &timeout); + } + + if (nsel < 0) { + if (errno == EINTR) + continue; + syslog(LOG_ERR, "select: %m"); + Exit(0); + } else if (nsel == 0) { /* timeout */ + DoTimeout(); /* clear stale conns */ + continue; + } + + if (FD_ISSET(fd, &r)) { + RMPCONN rconn; + CLIENT *client, *FindClient(); + int doread = 1; + + while (BpfRead(&rconn, doread)) { + doread = 0; + + if (DbgFp != NULL) /* display packet */ + DispPkt(&rconn,DIR_RCVD); + + omask = sigblock(sigmask(SIGHUP)); + + /* + * If we do not restrict service, set the + * client to NULL (ProcessPacket() handles + * this). Otherwise, check that we can + * service this host; if not, log a message + * and ignore the packet. + */ + if (BootAny) { + client = NULL; + } else if ((client=FindClient(&rconn))==NULL) { + syslog(LOG_INFO, + "%s: boot packet ignored", + EnetStr(&rconn)); + (void) sigsetmask(omask); + continue; + } + + ProcessPacket(&rconn,client); + + (void) sigsetmask(omask); + } + } + } +} + +/* +** DoTimeout -- Free any connections that have timed out. +** +** Parameters: +** None. +** +** Returns: +** Nothing. +** +** Side Effects: +** - Timed out connections in `RmpConns' will be freed. +*/ +void +DoTimeout() +{ + register RMPCONN *rtmp; + struct timeval now; + + (void) gettimeofday(&now, (struct timezone *)0); + + /* + * For each active connection, if RMP_TIMEOUT seconds have passed + * since the last packet was sent, delete the connection. + */ + for (rtmp = RmpConns; rtmp != NULL; rtmp = rtmp->next) + if ((rtmp->tstamp.tv_sec + RMP_TIMEOUT) < now.tv_sec) { + syslog(LOG_WARNING, "%s: connection timed out (%u)", + EnetStr(rtmp), rtmp->rmp.r_type); + RemoveConn(rtmp); + } +} + +/* +** FindClient -- Find client associated with a packet. +** +** Parameters: +** rconn - the new packet. +** +** Returns: +** Pointer to client info if found, NULL otherwise. +** +** Side Effects: +** None. +** +** Warnings: +** - This routine must be called with SIGHUP blocked since +** a reconfigure can invalidate the information returned. +*/ + +CLIENT * +FindClient(rconn) + register RMPCONN *rconn; +{ + register CLIENT *ctmp; + + for (ctmp = Clients; ctmp != NULL; ctmp = ctmp->next) + if (bcmp((char *)&rconn->rmp.hp_hdr.saddr[0], + (char *)&ctmp->addr[0], RMP_ADDRLEN) == 0) + break; + + return(ctmp); +} + +/* +** Exit -- Log an error message and exit. +** +** Parameters: +** sig - caught signal (or zero if not dying on a signal). +** +** Returns: +** Does not return. +** +** Side Effects: +** - This process ceases to exist. +*/ +void +Exit(sig) + int sig; +{ + if (sig > 0) + syslog(LOG_ERR, "going down on signal %d", sig); + else + syslog(LOG_ERR, "going down with fatal error"); + BpfClose(); + exit(1); +} + +/* +** ReConfig -- Get new list of boot files and reread config files. +** +** Parameters: +** None. +** +** Returns: +** Nothing. +** +** Side Effects: +** - All active connections are dropped. +** - List of boot-able files is changed. +** - List of clients is changed. +** +** Warnings: +** - This routine must be called with SIGHUP blocked. +*/ +void +ReConfig(signo) + int signo; +{ + syslog(LOG_NOTICE, "reconfiguring boot server"); + + FreeConns(); + + if (GetBootFiles() == 0) + Exit(0); + + if (ParseConfig() == 0) + Exit(0); +} + +/* +** DebugOff -- Turn off debugging. +** +** Parameters: +** None. +** +** Returns: +** Nothing. +** +** Side Effects: +** - Debug file is closed. +*/ +void +DebugOff(signo) + int signo; +{ + if (DbgFp != NULL) + (void) fclose(DbgFp); + + DbgFp = NULL; +} + +/* +** DebugOn -- Turn on debugging. +** +** Parameters: +** None. +** +** Returns: +** Nothing. +** +** Side Effects: +** - Debug file is opened/truncated if not already opened, +** otherwise do nothing. +*/ +void +DebugOn(signo) + int signo; +{ + if (DbgFp == NULL) { + if ((DbgFp = fopen(DbgFile, "w")) == NULL) + syslog(LOG_ERR, "can't open debug file (%s)", DbgFile); + } +} diff --git a/libexec/rbootd/rmp.h b/libexec/rbootd/rmp.h new file mode 100644 index 0000000..d3b0167 --- /dev/null +++ b/libexec/rbootd/rmp.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)rmp.h 8.1 (Berkeley) 6/4/93 + * + * Utah $Hdr: rmp.h 3.1 92/07/06$ + * Author: Jeff Forys, University of Utah CSS + */ + +/* + * Define MIN/MAX sizes of RMP (ethernet) packet. + * For ease of computation, the 4 octet CRC field is not included. + * + * MCLBYTES is for bpfwrite(); it is adamant about using a cluster. + */ + +#define RMP_MAX_PACKET MIN(1514,MCLBYTES) +#define RMP_MIN_PACKET 60 + +/* + * Define RMP/Ethernet Multicast address (9:0:9:0:0:4) and its length. + */ +#define RMP_ADDR { 0x9, 0x0, 0x9, 0x0, 0x0, 0x4 } +#define RMP_ADDRLEN 6 + +/* + * Define IEEE802.2 (Logical Link Control) information. + */ +#define IEEE_DSAP_HP 0xF8 /* Destination Service Access Point */ +#define IEEE_SSAP_HP 0xF8 /* Source Service Access Point */ +#define IEEE_CNTL_HP 0x0300 /* Type 1 / I format control information */ + +#define HPEXT_DXSAP 0x608 /* HP Destination Service Access Point */ +#define HPEXT_SXSAP 0x609 /* HP Source Service Access Point */ + +/* + * 802.3-style "Ethernet" header. + */ + +struct hp_hdr { + u_char daddr[RMP_ADDRLEN]; + u_char saddr[RMP_ADDRLEN]; + u_short len; +}; + +/* + * HP uses 802.2 LLC with their own local extensions. This struct makes + * sence out of this data (encapsulated in the above 802.3 packet). + */ + +struct hp_llc { + u_char dsap; /* 802.2 DSAP */ + u_char ssap; /* 802.2 SSAP */ + u_short cntrl; /* 802.2 control field */ + u_short filler; /* HP filler (must be zero) */ + u_short dxsap; /* HP extended DSAP */ + u_short sxsap; /* HP extended SSAP */ +}; diff --git a/libexec/rbootd/rmp_var.h b/libexec/rbootd/rmp_var.h new file mode 100644 index 0000000..7df1e87 --- /dev/null +++ b/libexec/rbootd/rmp_var.h @@ -0,0 +1,244 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)rmp_var.h 8.1 (Berkeley) 6/4/93 + * + * Utah $Hdr: rmp_var.h 3.1 92/07/06$ + * Author: Jeff Forys, University of Utah CSS + */ + +/* + * Possible values for "rmp_type" fields. + */ + +#define RMP_BOOT_REQ 1 /* boot request packet */ +#define RMP_BOOT_REPL 129 /* boot reply packet */ +#define RMP_READ_REQ 2 /* read request packet */ +#define RMP_READ_REPL 130 /* read reply packet */ +#define RMP_BOOT_DONE 3 /* boot complete packet */ + +/* + * Useful constants. + */ + +#define RMP_VERSION 2 /* protocol version */ +#define RMP_TIMEOUT 600 /* timeout connection after ten minutes */ +#define RMP_PROBESID 0xffff /* session ID for probes */ +#define RMP_HOSTLEN 13 /* max length of server's name */ +#define RMP_MACHLEN 20 /* length of machine type field */ + +/* + * RMP error codes + */ + +#define RMP_E_OKAY 0 +#define RMP_E_EOF 2 /* read reply: returned end of file */ +#define RMP_E_ABORT 3 /* abort operation */ +#define RMP_E_BUSY 4 /* boot reply: server busy */ +#define RMP_E_TIMEOUT 5 /* lengthen time out (not implemented) */ +#define RMP_E_NOFILE 16 /* boot reply: file does not exist */ +#define RMP_E_OPENFILE 17 /* boot reply: file open failed */ +#define RMP_E_NODFLT 18 /* boot reply: default file does not exist */ +#define RMP_E_OPENDFLT 19 /* boot reply: default file open failed */ +#define RMP_E_BADSID 25 /* read reply: bad session ID */ +#define RMP_E_BADPACKET 27 /* Bad packet detected */ + +/* + * RMPDATALEN is the maximum number of data octets that can be stuffed + * into an RMP packet. This excludes the 802.2 LLC w/HP extensions. + */ +#define RMPDATALEN (RMP_MAX_PACKET - (sizeof(struct hp_hdr) + \ + sizeof(struct hp_llc))) + +/* + * Define sizes of packets we send. Boot and Read replies are variable + * in length depending on the length of `s'. + * + * Also, define how much space `restofpkt' can take up for outgoing + * Boot and Read replies. Boot Request packets are effectively + * limited to 255 bytes due to the preceding 1-byte length field. + */ + +#define RMPBOOTSIZE(s) (sizeof(struct hp_hdr) + sizeof(struct hp_llc) + \ + sizeof(struct rmp_boot_repl) + s - sizeof(restofpkt)) +#define RMPREADSIZE(s) (sizeof(struct hp_hdr) + sizeof(struct hp_llc) + \ + sizeof(struct rmp_read_repl) + s - sizeof(restofpkt) \ + - sizeof(u_char)) +#define RMPDONESIZE (sizeof(struct hp_hdr) + sizeof(struct hp_llc) + \ + sizeof(struct rmp_boot_done)) +#define RMPBOOTDATA 255 +#define RMPREADDATA (RMPDATALEN - \ + (2*sizeof(u_char)+sizeof(u_short)+sizeof(u_word))) + +/* + * This protocol defines some field sizes as "rest of ethernet packet". + * There is no easy way to specify this in C, so we use a one character + * field to denote it, and index past it to the end of the packet. + */ + +typedef char restofpkt; + +/* + * Due to the RMP packet layout, we'll run into alignment problems + * on machines that cant access words on half-word boundaries. If + * you know that your machine does not suffer from this problem, + * add it to the hp300 #define below. + * + * The following macros are used to deal with this problem: + * WORDZE(w) Return True if u_word `w' is zero, False otherwise. + * ZEROWORD(w) Set u_word `w' to zero. + * COPYWORD(w1,w2) Copy u_word `w1' to `w2'. + * GETWORD(w,i) Copy u_word `w' into int `i'. + * PUTWORD(i,w) Copy int `i' into u_word `w'. + * + * N.B. We do not support little endian alignment-challenged machines. + */ +#if defined(vax) || defined(tahoe) || defined(hp300) + +typedef u_int u_word; + +#define WORDZE(w) ((w) == 0) +#define ZEROWORD(w) (w) = 0 +#define COPYWORD(w1,w2) (w2) = (w1) +#define GETWORD(w, i) (i) = (w) +#define PUTWORD(i, w) (w) = (i) + +#else + +#define _WORD_HIGHPART 0 /* XXX: assume Big Endian for now */ +#define _WORD_LOWPART 1 + +typedef struct _uword { u_short val[2]; } u_word; + +#define WORDZE(w) \ + ((w.val[_WORD_HIGHPART] == 0) && (w.val[_WORD_LOWPART] == 0)) +#define ZEROWORD(w) \ + (w).val[_WORD_HIGHPART] = (w).val[_WORD_LOWPART] = 0 +#define COPYWORD(w1, w2) \ + { (w2).val[_WORD_HIGHPART] = (w1).val[_WORD_HIGHPART]; \ + (w2).val[_WORD_LOWPART] = (w1).val[_WORD_LOWPART]; \ + } +#define GETWORD(w, i) \ + (i) = (((u_int)(w).val[_WORD_HIGHPART]) << 16) | (w).val[_WORD_LOWPART] +#define PUTWORD(i, w) \ + { (w).val[_WORD_HIGHPART] = (u_short) (((i) >> 16) & 0xffff); \ + (w).val[_WORD_LOWPART] = (u_short) (i & 0xffff); \ + } + +#endif + +/* + * Packet structures. + */ + +struct rmp_raw { /* generic RMP packet */ + u_char rmp_type; /* packet type */ + u_char rmp_rawdata[RMPDATALEN-1]; +}; + +struct rmp_boot_req { /* boot request */ + u_char rmp_type; /* packet type (RMP_BOOT_REQ) */ + u_char rmp_retcode; /* return code (0) */ + u_word rmp_seqno; /* sequence number (real time clock) */ + u_short rmp_session; /* session id (normally 0) */ + u_short rmp_version; /* protocol version (RMP_VERSION) */ + char rmp_machtype[RMP_MACHLEN]; /* machine type */ + u_char rmp_flnmsize; /* length of rmp_flnm */ + restofpkt rmp_flnm; /* name of file to be read */ +}; + +struct rmp_boot_repl { /* boot reply */ + u_char rmp_type; /* packet type (RMP_BOOT_REPL) */ + u_char rmp_retcode; /* return code (normally 0) */ + u_word rmp_seqno; /* sequence number (from boot req) */ + u_short rmp_session; /* session id (generated) */ + u_short rmp_version; /* protocol version (RMP_VERSION) */ + u_char rmp_flnmsize; /* length of rmp_flnm */ + restofpkt rmp_flnm; /* name of file (from boot req) */ +}; + +struct rmp_read_req { /* read request */ + u_char rmp_type; /* packet type (RMP_READ_REQ) */ + u_char rmp_retcode; /* return code (0) */ + u_word rmp_offset; /* file relative byte offset */ + u_short rmp_session; /* session id (from boot repl) */ + u_short rmp_size; /* max no of bytes to send */ +}; + +struct rmp_read_repl { /* read reply */ + u_char rmp_type; /* packet type (RMP_READ_REPL) */ + u_char rmp_retcode; /* return code (normally 0) */ + u_word rmp_offset; /* byte offset (from read req) */ + u_short rmp_session; /* session id (from read req) */ + restofpkt rmp_data; /* data (max size from read req) */ + u_char rmp_unused; /* padding to 16-bit boundary */ +}; + +struct rmp_boot_done { /* boot complete */ + u_char rmp_type; /* packet type (RMP_BOOT_DONE) */ + u_char rmp_retcode; /* return code (0) */ + u_word rmp_unused; /* not used (0) */ + u_short rmp_session; /* session id (from read repl) */ +}; + +struct rmp_packet { + struct hp_hdr hp_hdr; + struct hp_llc hp_llc; + union { + struct rmp_boot_req rmp_brq; /* boot request */ + struct rmp_boot_repl rmp_brpl; /* boot reply */ + struct rmp_read_req rmp_rrq; /* read request */ + struct rmp_read_repl rmp_rrpl; /* read reply */ + struct rmp_boot_done rmp_done; /* boot complete */ + struct rmp_raw rmp_raw; /* raw data */ + } rmp_proto; +}; + +/* + * Make life easier... + */ + +#define r_type rmp_proto.rmp_raw.rmp_type +#define r_data rmp_proto.rmp_raw.rmp_data +#define r_brq rmp_proto.rmp_brq +#define r_brpl rmp_proto.rmp_brpl +#define r_rrq rmp_proto.rmp_rrq +#define r_rrpl rmp_proto.rmp_rrpl +#define r_done rmp_proto.rmp_done diff --git a/libexec/rbootd/rmpproto.c b/libexec/rbootd/rmpproto.c new file mode 100644 index 0000000..d0a8d4a --- /dev/null +++ b/libexec/rbootd/rmpproto.c @@ -0,0 +1,593 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)rmpproto.c 8.1 (Berkeley) 6/4/93 + * + * Utah $Hdr: rmpproto.c 3.1 92/07/06$ + * Author: Jeff Forys, University of Utah CSS + */ + +#ifndef lint +static char sccsid[] = "@(#)rmpproto.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include "defs.h" + +/* +** ProcessPacket -- determine packet type and do what's required. +** +** An RMP BOOT packet has been received. Look at the type field +** and process Boot Requests, Read Requests, and Boot Complete +** packets. Any other type will be dropped with a warning msg. +** +** Parameters: +** rconn - the new connection +** client - list of files available to this host +** +** Returns: +** Nothing. +** +** Side Effects: +** - If this is a valid boot request, it will be added to +** the linked list of outstanding requests (RmpConns). +** - If this is a valid boot complete, its associated +** entry in RmpConns will be deleted. +** - Also, unless we run out of memory, a reply will be +** sent to the host that sent the packet. +*/ +void +ProcessPacket(rconn, client) + RMPCONN *rconn; + CLIENT *client; +{ + struct rmp_packet *rmp; + RMPCONN *rconnout; + + rmp = &rconn->rmp; /* cache pointer to RMP packet */ + + switch(rmp->r_type) { /* do what we came here to do */ + case RMP_BOOT_REQ: /* boot request */ + if ((rconnout = NewConn(rconn)) == NULL) + return; + + /* + * If the Session ID is 0xffff, this is a "probe" + * packet and we do not want to add the connection + * to the linked list of active connections. There + * are two types of probe packets, if the Sequence + * Number is 0 they want to know our host name, o/w + * they want the name of the file associated with + * the number spec'd by the Sequence Number. + * + * If this is an actual boot request, open the file + * and send a reply. If SendBootRepl() does not + * return 0, add the connection to the linked list + * of active connections, otherwise delete it since + * an error was encountered. + */ + if (rmp->r_brq.rmp_session == RMP_PROBESID) { + if (WORDZE(rmp->r_brq.rmp_seqno)) + (void) SendServerID(rconnout); + else + (void) SendFileNo(rmp, rconnout, + client? client->files: + BootFiles); + FreeConn(rconnout); + } else { + if (SendBootRepl(rmp, rconnout, + client? client->files: BootFiles)) + AddConn(rconnout); + else + FreeConn(rconnout); + } + break; + + case RMP_BOOT_REPL: /* boot reply (not valid) */ + syslog(LOG_WARNING, "%s: sent a boot reply", + EnetStr(rconn)); + break; + + case RMP_READ_REQ: /* read request */ + /* + * Send a portion of the boot file. + */ + (void) SendReadRepl(rconn); + break; + + case RMP_READ_REPL: /* read reply (not valid) */ + syslog(LOG_WARNING, "%s: sent a read reply", + EnetStr(rconn)); + break; + + case RMP_BOOT_DONE: /* boot complete */ + /* + * Remove the entry from the linked list of active + * connections. + */ + (void) BootDone(rconn); + break; + + default: /* unknown RMP packet type */ + syslog(LOG_WARNING, "%s: unknown packet type (%u)", + EnetStr(rconn), rmp->r_type); + } +} + +/* +** SendServerID -- send our host name to who ever requested it. +** +** Parameters: +** rconn - the reply packet to be formatted. +** +** Returns: +** 1 on success, 0 on failure. +** +** Side Effects: +** none. +*/ +int +SendServerID(rconn) + RMPCONN *rconn; +{ + register struct rmp_packet *rpl; + register char *src, *dst; + register u_char *size; + + rpl = &rconn->rmp; /* cache ptr to RMP packet */ + + /* + * Set up assorted fields in reply packet. + */ + rpl->r_brpl.rmp_type = RMP_BOOT_REPL; + rpl->r_brpl.rmp_retcode = RMP_E_OKAY; + ZEROWORD(rpl->r_brpl.rmp_seqno); + rpl->r_brpl.rmp_session = 0; + rpl->r_brpl.rmp_version = RMP_VERSION; + + size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of host name */ + + /* + * Copy our host name into the reply packet incrementing the + * length as we go. Stop at RMP_HOSTLEN or the first dot. + */ + src = MyHost; + dst = (char *) &rpl->r_brpl.rmp_flnm; + for (*size = 0; *size < RMP_HOSTLEN; (*size)++) { + if (*src == '.' || *src == '\0') + break; + *dst++ = *src++; + } + + rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */ + + return(SendPacket(rconn)); /* send packet */ +} + +/* +** SendFileNo -- send the name of a bootable file to the requester. +** +** Parameters: +** req - RMP BOOT packet containing the request. +** rconn - the reply packet to be formatted. +** filelist - list of files available to the requester. +** +** Returns: +** 1 on success, 0 on failure. +** +** Side Effects: +** none. +*/ +int +SendFileNo(req, rconn, filelist) + struct rmp_packet *req; + RMPCONN *rconn; + char *filelist[]; +{ + register struct rmp_packet *rpl; + register char *src, *dst; + register u_char *size, i; + + GETWORD(req->r_brpl.rmp_seqno, i); /* SeqNo is really FileNo */ + rpl = &rconn->rmp; /* cache ptr to RMP packet */ + + /* + * Set up assorted fields in reply packet. + */ + rpl->r_brpl.rmp_type = RMP_BOOT_REPL; + PUTWORD(i, rpl->r_brpl.rmp_seqno); + i--; + rpl->r_brpl.rmp_session = 0; + rpl->r_brpl.rmp_version = RMP_VERSION; + + size = &rpl->r_brpl.rmp_flnmsize; /* ptr to length of filename */ + *size = 0; /* init length to zero */ + + /* + * Copy the file name into the reply packet incrementing the + * length as we go. Stop at end of string or when RMPBOOTDATA + * characters have been copied. Also, set return code to + * indicate success or "no more files". + */ + if (i < C_MAXFILE && filelist[i] != NULL) { + src = filelist[i]; + dst = (char *)&rpl->r_brpl.rmp_flnm; + for (; *src && *size < RMPBOOTDATA; (*size)++) { + if (*src == '\0') + break; + *dst++ = *src++; + } + rpl->r_brpl.rmp_retcode = RMP_E_OKAY; + } else + rpl->r_brpl.rmp_retcode = RMP_E_NODFLT; + + rconn->rmplen = RMPBOOTSIZE(*size); /* set packet length */ + + return(SendPacket(rconn)); /* send packet */ +} + +/* +** SendBootRepl -- open boot file and respond to boot request. +** +** Parameters: +** req - RMP BOOT packet containing the request. +** rconn - the reply packet to be formatted. +** filelist - list of files available to the requester. +** +** Returns: +** 1 on success, 0 on failure. +** +** Side Effects: +** none. +*/ +int +SendBootRepl(req, rconn, filelist) + struct rmp_packet *req; + RMPCONN *rconn; + char *filelist[]; +{ + int retval; + char *filename, filepath[RMPBOOTDATA+1]; + RMPCONN *oldconn; + register struct rmp_packet *rpl; + register char *src, *dst1, *dst2; + register u_char i; + + /* + * If another connection already exists, delete it since we + * are obviously starting again. + */ + if ((oldconn = FindConn(rconn)) != NULL) { + syslog(LOG_WARNING, "%s: dropping existing connection", + EnetStr(oldconn)); + RemoveConn(oldconn); + } + + rpl = &rconn->rmp; /* cache ptr to RMP packet */ + + /* + * Set up assorted fields in reply packet. + */ + rpl->r_brpl.rmp_type = RMP_BOOT_REPL; + COPYWORD(req->r_brq.rmp_seqno, rpl->r_brpl.rmp_seqno); + rpl->r_brpl.rmp_session = GenSessID(); + rpl->r_brpl.rmp_version = RMP_VERSION; + rpl->r_brpl.rmp_flnmsize = req->r_brq.rmp_flnmsize; + + /* + * Copy file name to `filepath' string, and into reply packet. + */ + src = &req->r_brq.rmp_flnm; + dst1 = filepath; + dst2 = &rpl->r_brpl.rmp_flnm; + for (i = 0; i < req->r_brq.rmp_flnmsize; i++) + *dst1++ = *dst2++ = *src++; + *dst1 = '\0'; + + /* + * If we are booting HP-UX machines, their secondary loader will + * ask for files like "/hp-ux". As a security measure, we do not + * allow boot files to lay outside the boot directory (unless they + * are purposely link'd out. So, make `filename' become the path- + * stripped file name and spoof the client into thinking that it + * really got what it wanted. + */ + filename = (filename = rindex(filepath,'/'))? ++filename: filepath; + + /* + * Check that this is a valid boot file name. + */ + for (i = 0; i < C_MAXFILE && filelist[i] != NULL; i++) + if (STREQN(filename, filelist[i])) + goto match; + + /* + * Invalid boot file name, set error and send reply packet. + */ + rpl->r_brpl.rmp_retcode = RMP_E_NOFILE; + retval = 0; + goto sendpkt; + +match: + /* + * This is a valid boot file. Open the file and save the file + * descriptor associated with this connection and set success + * indication. If the file couldnt be opened, set error: + * "no such file or dir" - RMP_E_NOFILE + * "file table overflow" - RMP_E_BUSY + * "too many open files" - RMP_E_BUSY + * anything else - RMP_E_OPENFILE + */ + if ((rconn->bootfd = open(filename, O_RDONLY, 0600)) < 0) { + rpl->r_brpl.rmp_retcode = (errno == ENOENT)? RMP_E_NOFILE: + (errno == EMFILE || errno == ENFILE)? RMP_E_BUSY: + RMP_E_OPENFILE; + retval = 0; + } else { + rpl->r_brpl.rmp_retcode = RMP_E_OKAY; + retval = 1; + } + +sendpkt: + syslog(LOG_INFO, "%s: request to boot %s (%s)", + EnetStr(rconn), filename, retval? "granted": "denied"); + + rconn->rmplen = RMPBOOTSIZE(rpl->r_brpl.rmp_flnmsize); + + return (retval & SendPacket(rconn)); +} + +/* +** SendReadRepl -- send a portion of the boot file to the requester. +** +** Parameters: +** rconn - the reply packet to be formatted. +** +** Returns: +** 1 on success, 0 on failure. +** +** Side Effects: +** none. +*/ +int +SendReadRepl(rconn) + RMPCONN *rconn; +{ + int retval; + RMPCONN *oldconn; + register struct rmp_packet *rpl, *req; + register int size = 0; + int madeconn = 0; + + /* + * Find the old connection. If one doesnt exist, create one only + * to return the error code. + */ + if ((oldconn = FindConn(rconn)) == NULL) { + if ((oldconn = NewConn(rconn)) == NULL) + return(0); + syslog(LOG_ERR, "SendReadRepl: no active connection (%s)", + EnetStr(rconn)); + madeconn++; + } + + req = &rconn->rmp; /* cache ptr to request packet */ + rpl = &oldconn->rmp; /* cache ptr to reply packet */ + + if (madeconn) { /* no active connection above; abort */ + rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; + retval = 1; + goto sendpkt; + } + + /* + * Make sure Session ID's match. + */ + if (req->r_rrq.rmp_session != + ((rpl->r_type == RMP_BOOT_REPL)? rpl->r_brpl.rmp_session: + rpl->r_rrpl.rmp_session)) { + syslog(LOG_ERR, "SendReadRepl: bad session id (%s)", + EnetStr(rconn)); + rpl->r_rrpl.rmp_retcode = RMP_E_BADSID; + retval = 1; + goto sendpkt; + } + + /* + * If the requester asks for more data than we can fit, + * silently clamp the request size down to RMPREADDATA. + * + * N.B. I do not know if this is "legal", however it seems + * to work. This is necessary for bpfwrite() on machines + * with MCLBYTES less than 1514. + */ + if (req->r_rrq.rmp_size > RMPREADDATA) + req->r_rrq.rmp_size = RMPREADDATA; + + /* + * Position read head on file according to info in request packet. + */ + GETWORD(req->r_rrq.rmp_offset, size); + if (lseek(oldconn->bootfd, (off_t)size, L_SET) < 0) { + syslog(LOG_ERR, "SendReadRepl: lseek: %m (%s)", + EnetStr(rconn)); + rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; + retval = 1; + goto sendpkt; + } + + /* + * Read data directly into reply packet. + */ + if ((size = read(oldconn->bootfd, &rpl->r_rrpl.rmp_data, + (int) req->r_rrq.rmp_size)) <= 0) { + if (size < 0) { + syslog(LOG_ERR, "SendReadRepl: read: %m (%s)", + EnetStr(rconn)); + rpl->r_rrpl.rmp_retcode = RMP_E_ABORT; + } else { + rpl->r_rrpl.rmp_retcode = RMP_E_EOF; + } + retval = 1; + goto sendpkt; + } + + /* + * Set success indication. + */ + rpl->r_rrpl.rmp_retcode = RMP_E_OKAY; + +sendpkt: + /* + * Set up assorted fields in reply packet. + */ + rpl->r_rrpl.rmp_type = RMP_READ_REPL; + COPYWORD(req->r_rrq.rmp_offset, rpl->r_rrpl.rmp_offset); + rpl->r_rrpl.rmp_session = req->r_rrq.rmp_session; + + oldconn->rmplen = RMPREADSIZE(size); /* set size of packet */ + + retval &= SendPacket(oldconn); /* send packet */ + + if (madeconn) /* clean up after ourself */ + FreeConn(oldconn); + + return (retval); +} + +/* +** BootDone -- free up memory allocated for a connection. +** +** Parameters: +** rconn - incoming boot complete packet. +** +** Returns: +** 1 on success, 0 on failure. +** +** Side Effects: +** none. +*/ +int +BootDone(rconn) + RMPCONN *rconn; +{ + RMPCONN *oldconn; + struct rmp_packet *rpl; + + /* + * If we cant find the connection, ignore the request. + */ + if ((oldconn = FindConn(rconn)) == NULL) { + syslog(LOG_ERR, "BootDone: no existing connection (%s)", + EnetStr(rconn)); + return(0); + } + + rpl = &oldconn->rmp; /* cache ptr to RMP packet */ + + /* + * Make sure Session ID's match. + */ + if (rconn->rmp.r_rrq.rmp_session != + ((rpl->r_type == RMP_BOOT_REPL)? rpl->r_brpl.rmp_session: + rpl->r_rrpl.rmp_session)) { + syslog(LOG_ERR, "BootDone: bad session id (%s)", + EnetStr(rconn)); + return(0); + } + + RemoveConn(oldconn); /* remove connection */ + + syslog(LOG_INFO, "%s: boot complete", EnetStr(rconn)); + + return(1); +} + +/* +** SendPacket -- send an RMP packet to a remote host. +** +** Parameters: +** rconn - packet to be sent. +** +** Returns: +** 1 on success, 0 on failure. +** +** Side Effects: +** none. +*/ +int +SendPacket(rconn) + register RMPCONN *rconn; +{ + /* + * Set Ethernet Destination address to Source (BPF and the enet + * driver will take care of getting our source address set). + */ + bcopy((char *)&rconn->rmp.hp_hdr.saddr[0], + (char *)&rconn->rmp.hp_hdr.daddr[0], RMP_ADDRLEN); + rconn->rmp.hp_hdr.len = rconn->rmplen - sizeof(struct hp_hdr); + + /* + * Reverse 802.2/HP Extended Source & Destination Access Pts. + */ + rconn->rmp.hp_llc.dxsap = HPEXT_SXSAP; + rconn->rmp.hp_llc.sxsap = HPEXT_DXSAP; + + /* + * Last time this connection was active. + */ + (void) gettimeofday(&rconn->tstamp, (struct timezone *)0); + + if (DbgFp != NULL) /* display packet */ + DispPkt(rconn,DIR_SENT); + + /* + * Send RMP packet to remote host. + */ + return(BpfWrite(rconn)); +} diff --git a/libexec/rbootd/utils.c b/libexec/rbootd/utils.c new file mode 100644 index 0000000..58ead7c --- /dev/null +++ b/libexec/rbootd/utils.c @@ -0,0 +1,557 @@ +/* + * Copyright (c) 1988, 1992 The University of Utah and the Center + * for Software Science (CSS). + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Center for Software Science of the University of Utah Computer + * Science Department. CSS requests users of this software to return + * to css-dist@cs.utah.edu any improvements that they make and grant + * CSS redistribution rights. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)utils.c 8.2 (Berkeley) 2/22/94 + * + * Utah $Hdr: utils.c 3.1 92/07/06$ + * Author: Jeff Forys, University of Utah CSS + */ + +#ifndef lint +static char sccsid[] = "@(#)utils.c 8.2 (Berkeley) 2/22/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> + +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> +#include "defs.h" + +/* +** DispPkt -- Display the contents of an RMPCONN packet. +** +** Parameters: +** rconn - packet to be displayed. +** direct - direction packet is going (DIR_*). +** +** Returns: +** Nothing. +** +** Side Effects: +** None. +*/ +void +DispPkt(rconn, direct) + RMPCONN *rconn; + int direct; +{ + static char BootFmt[] = "\t\tRetCode:%u SeqNo:%lx SessID:%x Vers:%u"; + static char ReadFmt[] = "\t\tRetCode:%u Offset:%lx SessID:%x\n"; + + struct tm *tmp; + register struct rmp_packet *rmp; + int i, omask; + u_int t; + + /* + * Since we will be working with RmpConns as well as DbgFp, we + * must block signals that can affect either. + */ + omask = sigblock(sigmask(SIGHUP)|sigmask(SIGUSR1)|sigmask(SIGUSR2)); + + if (DbgFp == NULL) { /* sanity */ + (void) sigsetmask(omask); + return; + } + + /* display direction packet is going using '>>>' or '<<<' */ + fputs((direct==DIR_RCVD)?"<<< ":(direct==DIR_SENT)?">>> ":"", DbgFp); + + /* display packet timestamp */ + tmp = localtime((time_t *)&rconn->tstamp.tv_sec); + fprintf(DbgFp, "%02d:%02d:%02d.%06ld ", tmp->tm_hour, tmp->tm_min, + tmp->tm_sec, rconn->tstamp.tv_usec); + + /* display src or dst addr and information about network interface */ + fprintf(DbgFp, "Addr: %s Intf: %s\n", EnetStr(rconn), IntfName); + + rmp = &rconn->rmp; + + /* display IEEE 802.2 Logical Link Control header */ + (void) fprintf(DbgFp, "\t802.2 LLC: DSAP:%x SSAP:%x CTRL:%x\n", + rmp->hp_llc.dsap, rmp->hp_llc.ssap, rmp->hp_llc.cntrl); + + /* display HP extensions to 802.2 Logical Link Control header */ + (void) fprintf(DbgFp, "\tHP Ext: DXSAP:%x SXSAP:%x\n", + rmp->hp_llc.dxsap, rmp->hp_llc.sxsap); + + /* + * Display information about RMP packet using type field to + * determine what kind of packet this is. + */ + switch(rmp->r_type) { + case RMP_BOOT_REQ: /* boot request */ + (void) fprintf(DbgFp, "\tBoot Request:"); + GETWORD(rmp->r_brq.rmp_seqno, t); + if (rmp->r_brq.rmp_session == RMP_PROBESID) { + if (WORDZE(rmp->r_brq.rmp_seqno)) + fputs(" (Send Server ID)", DbgFp); + else + fprintf(DbgFp," (Send Filename #%u)",t); + } + (void) fputc('\n', DbgFp); + (void) fprintf(DbgFp, BootFmt, rmp->r_brq.rmp_retcode, + t, rmp->r_brq.rmp_session, + rmp->r_brq.rmp_version); + (void) fprintf(DbgFp, "\n\t\tMachine Type: "); + for (i = 0; i < RMP_MACHLEN; i++) + (void) fputc(rmp->r_brq.rmp_machtype[i], DbgFp); + DspFlnm(rmp->r_brq.rmp_flnmsize, &rmp->r_brq.rmp_flnm); + break; + case RMP_BOOT_REPL: /* boot reply */ + fprintf(DbgFp, "\tBoot Reply:\n"); + GETWORD(rmp->r_brpl.rmp_seqno, t); + (void) fprintf(DbgFp, BootFmt, rmp->r_brpl.rmp_retcode, + t, rmp->r_brpl.rmp_session, + rmp->r_brpl.rmp_version); + DspFlnm(rmp->r_brpl.rmp_flnmsize,&rmp->r_brpl.rmp_flnm); + break; + case RMP_READ_REQ: /* read request */ + (void) fprintf(DbgFp, "\tRead Request:\n"); + GETWORD(rmp->r_rrq.rmp_offset, t); + (void) fprintf(DbgFp, ReadFmt, rmp->r_rrq.rmp_retcode, + t, rmp->r_rrq.rmp_session); + (void) fprintf(DbgFp, "\t\tNoOfBytes: %u\n", + rmp->r_rrq.rmp_size); + break; + case RMP_READ_REPL: /* read reply */ + (void) fprintf(DbgFp, "\tRead Reply:\n"); + GETWORD(rmp->r_rrpl.rmp_offset, t); + (void) fprintf(DbgFp, ReadFmt, rmp->r_rrpl.rmp_retcode, + t, rmp->r_rrpl.rmp_session); + (void) fprintf(DbgFp, "\t\tNoOfBytesSent: %d\n", + rconn->rmplen - RMPREADSIZE(0)); + break; + case RMP_BOOT_DONE: /* boot complete */ + (void) fprintf(DbgFp, "\tBoot Complete:\n"); + (void) fprintf(DbgFp, "\t\tRetCode:%u SessID:%x\n", + rmp->r_done.rmp_retcode, + rmp->r_done.rmp_session); + break; + default: /* ??? */ + (void) fprintf(DbgFp, "\tUnknown Type:(%d)\n", + rmp->r_type); + } + (void) fputc('\n', DbgFp); + (void) fflush(DbgFp); + + (void) sigsetmask(omask); /* reset old signal mask */ +} + + +/* +** GetEtherAddr -- convert an RMP (Ethernet) address into a string. +** +** An RMP BOOT packet has been received. Look at the type field +** and process Boot Requests, Read Requests, and Boot Complete +** packets. Any other type will be dropped with a warning msg. +** +** Parameters: +** addr - array of RMP_ADDRLEN bytes. +** +** Returns: +** Pointer to static string representation of `addr'. +** +** Side Effects: +** None. +** +** Warnings: +** - The return value points to a static buffer; it must +** be copied if it's to be saved. +** - For speed, we assume a u_char consists of 8 bits. +*/ +char * +GetEtherAddr(addr) + u_char *addr; +{ + static char Hex[] = "0123456789abcdef"; + static char etherstr[RMP_ADDRLEN*3]; + register int i; + register char *cp1, *cp2; + + /* + * For each byte in `addr', convert it to "<hexchar><hexchar>:". + * The last byte does not get a trailing `:' appended. + */ + i = 0; + cp1 = (char *)addr; + cp2 = etherstr; + for(;;) { + *cp2++ = Hex[*cp1 >> 4 & 0xf]; + *cp2++ = Hex[*cp1++ & 0xf]; + if (++i == RMP_ADDRLEN) + break; + *cp2++ = ':'; + } + *cp2 = '\0'; + + return(etherstr); +} + + +/* +** DispFlnm -- Print a string of bytes to DbgFp (often, a file name). +** +** Parameters: +** size - number of bytes to print. +** flnm - address of first byte. +** +** Returns: +** Nothing. +** +** Side Effects: +** - Characters are sent to `DbgFp'. +*/ +void +DspFlnm(size, flnm) + register u_int size; + register char *flnm; +{ + register int i; + + (void) fprintf(DbgFp, "\n\t\tFile Name (%d): <", size); + for (i = 0; i < size; i++) + (void) fputc(*flnm++, DbgFp); + (void) fputs(">\n", DbgFp); +} + + +/* +** NewClient -- allocate memory for a new CLIENT. +** +** Parameters: +** addr - RMP (Ethernet) address of new client. +** +** Returns: +** Ptr to new CLIENT or NULL if we ran out of memory. +** +** Side Effects: +** - Memory will be malloc'd for the new CLIENT. +** - If malloc() fails, a log message will be generated. +*/ +CLIENT * +NewClient(addr) + u_char *addr; +{ + CLIENT *ctmp; + + if ((ctmp = (CLIENT *) malloc(sizeof(CLIENT))) == NULL) { + syslog(LOG_ERR, "NewClient: out of memory (%s)", + GetEtherAddr(addr)); + return(NULL); + } + + bzero(ctmp, sizeof(CLIENT)); + bcopy(addr, &ctmp->addr[0], RMP_ADDRLEN); + return(ctmp); +} + +/* +** FreeClient -- free linked list of Clients. +** +** Parameters: +** None. +** +** Returns: +** Nothing. +** +** Side Effects: +** - All malloc'd memory associated with the linked list of +** CLIENTS will be free'd; `Clients' will be set to NULL. +** +** Warnings: +** - This routine must be called with SIGHUP blocked. +*/ +void +FreeClients() +{ + register CLIENT *ctmp; + + while (Clients != NULL) { + ctmp = Clients; + Clients = Clients->next; + FreeClient(ctmp); + } +} + +/* +** NewStr -- allocate memory for a character array. +** +** Parameters: +** str - null terminated character array. +** +** Returns: +** Ptr to new character array or NULL if we ran out of memory. +** +** Side Effects: +** - Memory will be malloc'd for the new character array. +** - If malloc() fails, a log message will be generated. +*/ +char * +NewStr(str) + char *str; +{ + char *stmp; + + if ((stmp = (char *)malloc((unsigned) (strlen(str)+1))) == NULL) { + syslog(LOG_ERR, "NewStr: out of memory (%s)", str); + return(NULL); + } + + (void) strcpy(stmp, str); + return(stmp); +} + +/* +** To save time, NewConn and FreeConn maintain a cache of one RMPCONN +** in `LastFree' (defined below). +*/ + +static RMPCONN *LastFree = NULL; + +/* +** NewConn -- allocate memory for a new RMPCONN connection. +** +** Parameters: +** rconn - initialization template for new connection. +** +** Returns: +** Ptr to new RMPCONN or NULL if we ran out of memory. +** +** Side Effects: +** - Memory may be malloc'd for the new RMPCONN (if not cached). +** - If malloc() fails, a log message will be generated. +*/ +RMPCONN * +NewConn(rconn) + RMPCONN *rconn; +{ + RMPCONN *rtmp; + + if (LastFree == NULL) { /* nothing cached; make a new one */ + if ((rtmp = (RMPCONN *) malloc(sizeof(RMPCONN))) == NULL) { + syslog(LOG_ERR, "NewConn: out of memory (%s)", + EnetStr(rconn)); + return(NULL); + } + } else { /* use the cached RMPCONN */ + rtmp = LastFree; + LastFree = NULL; + } + + /* + * Copy template into `rtmp', init file descriptor to `-1' and + * set ptr to next elem NULL. + */ + bcopy((char *)rconn, (char *)rtmp, sizeof(RMPCONN)); + rtmp->bootfd = -1; + rtmp->next = NULL; + + return(rtmp); +} + +/* +** FreeConn -- Free memory associated with an RMPCONN connection. +** +** Parameters: +** rtmp - ptr to RMPCONN to be free'd. +** +** Returns: +** Nothing. +** +** Side Effects: +** - Memory associated with `rtmp' may be free'd (or cached). +** - File desc associated with `rtmp->bootfd' will be closed. +*/ +void +FreeConn(rtmp) + register RMPCONN *rtmp; +{ + /* + * If the file descriptor is in use, close the file. + */ + if (rtmp->bootfd >= 0) { + (void) close(rtmp->bootfd); + rtmp->bootfd = -1; + } + + if (LastFree == NULL) /* cache for next time */ + rtmp = LastFree; + else /* already one cached; free this one */ + free((char *)rtmp); +} + +/* +** FreeConns -- free linked list of RMPCONN connections. +** +** Parameters: +** None. +** +** Returns: +** Nothing. +** +** Side Effects: +** - All malloc'd memory associated with the linked list of +** connections will be free'd; `RmpConns' will be set to NULL. +** - If LastFree is != NULL, it too will be free'd & NULL'd. +** +** Warnings: +** - This routine must be called with SIGHUP blocked. +*/ +void +FreeConns() +{ + register RMPCONN *rtmp; + + while (RmpConns != NULL) { + rtmp = RmpConns; + RmpConns = RmpConns->next; + FreeConn(rtmp); + } + + if (LastFree != NULL) { + free((char *)LastFree); + LastFree = NULL; + } +} + +/* +** AddConn -- Add a connection to the linked list of connections. +** +** Parameters: +** rconn - connection to be added. +** +** Returns: +** Nothing. +** +** Side Effects: +** - RmpConn will point to new connection. +** +** Warnings: +** - This routine must be called with SIGHUP blocked. +*/ +void +AddConn(rconn) + register RMPCONN *rconn; +{ + if (RmpConns != NULL) + rconn->next = RmpConns; + RmpConns = rconn; +} + +/* +** FindConn -- Find a connection in the linked list of connections. +** +** We use the RMP (Ethernet) address as the basis for determining +** if this is the same connection. According to the Remote Maint +** Protocol, we can only have one connection with any machine. +** +** Parameters: +** rconn - connection to be found. +** +** Returns: +** Matching connection from linked list or NULL if not found. +** +** Side Effects: +** None. +** +** Warnings: +** - This routine must be called with SIGHUP blocked. +*/ +RMPCONN * +FindConn(rconn) + register RMPCONN *rconn; +{ + register RMPCONN *rtmp; + + for (rtmp = RmpConns; rtmp != NULL; rtmp = rtmp->next) + if (bcmp((char *)&rconn->rmp.hp_hdr.saddr[0], + (char *)&rtmp->rmp.hp_hdr.saddr[0], RMP_ADDRLEN) == 0) + break; + + return(rtmp); +} + +/* +** RemoveConn -- Remove a connection from the linked list of connections. +** +** Parameters: +** rconn - connection to be removed. +** +** Returns: +** Nothing. +** +** Side Effects: +** - If found, an RMPCONN will cease to exist and it will +** be removed from the linked list. +** +** Warnings: +** - This routine must be called with SIGHUP blocked. +*/ +void +RemoveConn(rconn) + register RMPCONN *rconn; +{ + register RMPCONN *thisrconn, *lastrconn; + + if (RmpConns == rconn) { /* easy case */ + RmpConns = RmpConns->next; + FreeConn(rconn); + } else { /* must traverse linked list */ + lastrconn = RmpConns; /* set back ptr */ + thisrconn = lastrconn->next; /* set current ptr */ + while (thisrconn != NULL) { + if (rconn == thisrconn) { /* found it */ + lastrconn->next = thisrconn->next; + FreeConn(thisrconn); + break; + } + lastrconn = thisrconn; + thisrconn = thisrconn->next; + } + } +} diff --git a/libexec/revnetgroup/Makefile b/libexec/revnetgroup/Makefile new file mode 100644 index 0000000..dec0e7b --- /dev/null +++ b/libexec/revnetgroup/Makefile @@ -0,0 +1,8 @@ +# $Id$ + +PROG= revnetgroup +SRCS= revnetgroup.c hash.c parse_netgroup.c + +MAN8= revnetgroup.8 + +.include <bsd.prog.mk> diff --git a/libexec/revnetgroup/hash.c b/libexec/revnetgroup/hash.c new file mode 100644 index 0000000..b875843 --- /dev/null +++ b/libexec/revnetgroup/hash.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include "hash.h" + +/* + * This hash function is stolen directly from the + * Berkeley DB package. It already exists inside libc, but + * it's declared static which prevents us from calling it + * from here. + */ +/* + * OZ's original sdbm hash + */ +u_int32_t +hash(keyarg, len) + const void *keyarg; + register size_t len; +{ + register const u_char *key; + register size_t loop; + register u_int32_t h; + +#define HASHC h = *key++ + 65599 * h + + h = 0; + key = keyarg; + if (len > 0) { + loop = (len + 8 - 1) >> 3; + + switch (len & (8 - 1)) { + case 0: + do { + HASHC; + /* FALLTHROUGH */ + case 7: + HASHC; + /* FALLTHROUGH */ + case 6: + HASHC; + /* FALLTHROUGH */ + case 5: + HASHC; + /* FALLTHROUGH */ + case 4: + HASHC; + /* FALLTHROUGH */ + case 3: + HASHC; + /* FALLTHROUGH */ + case 2: + HASHC; + /* FALLTHROUGH */ + case 1: + HASHC; + } while (--loop); + } + } + return (h); +} + +/* + * Generate a hash value for a given key (character string). + * We mask off all but the lower 8 bits since our table array + * can only hole 256 elements. + */ +u_int32_t hashkey(key) + char *key; +{ + + if (key == NULL) + return (-1); + return(hash((void *)key, strlen(key)) & HASH_MASK); +} + +/* Find an entry in the hash table (may be hanging off a linked list). */ +char *lookup(table, key) + struct group_entry *table[]; + char *key; +{ + struct group_entry *cur; + + cur = table[hashkey(key)]; + + while (cur) { + if (!strcmp(cur->key, key)) + return(cur->data); + cur = cur->next; + } + + return(NULL); +} + +/* + * Store an entry in the main netgroup hash table. Here's how this + * works: the table can only be so big when we initialize it (TABLESIZE) + * but the number of netgroups in the /etc/netgroup file could easily be + * much larger than the table. Since our hash values are adjusted to + * never be greater than TABLESIZE too, this means it won't be long before + * we find ourselves with two keys that hash to the same value. + * + * One way to deal with this is to malloc(2) a second table and start + * doing indirection, but this is a pain in the butt and it's not worth + * going to all that trouble for a dinky littke program like this. Instead, + * we turn each table entry into a linked list and simply link keys + * with the same hash value together at the same index location within + * the table. + * + * That's a lot of comment for such a small piece of code, isn't it. + */ +void store (table, key, data) + struct group_entry *table[]; + char *key, *data; +{ + struct group_entry *new; + u_int32_t i; + + i = hashkey(key); + + new = (struct group_entry *)malloc(sizeof(struct group_entry)); + new->key = strdup(key); + new->data = strdup(data); + new->next = table[i]; + table[i] = new; + + return; +} + +/* + * Store an group member entry and/or update its grouplist. This is + * a bit more complicated than the previous function since we have to + * maintain not only the hash table of group members, each group member + * structure also has a linked list of groups hung off it. If handed + * a member name that we haven't encountered before, we have to do + * two things: add that member to the table (possibly hanging them + * off the end of a linked list, as above), and add a group name to + * the member's grouplist list. If we're handed a name that already has + * an entry in the table, then we just have to do one thing, which is + * to update its grouplist. + */ +void mstore (table, key, data, domain) + struct member_entry *table[]; + char *key, *data, *domain; +{ + struct member_entry *cur, *new; + struct grouplist *tmp; + u_int32_t i; + + i = hashkey(key); + cur = table[i]; + + tmp = (struct grouplist *)malloc(sizeof(struct grouplist)); + tmp->groupname = strdup(data); + tmp->next = NULL; + + /* Check if all we have to do is insert a new groupname. */ + while (cur) { + if (!strcmp(cur->key, key)) { + tmp->next = cur->groups; + cur->groups = tmp; + return; + } + cur = cur->next; + } + + /* Didn't find a match -- add the whole mess to the table. */ + new = (struct member_entry *)malloc(sizeof(struct member_entry)); + new->key = strdup(key); + new->domain = domain ? strdup(domain) : "*"; + new->groups = tmp; + new->next = table[i]; + table[i] = new; + + return; +} diff --git a/libexec/revnetgroup/hash.h b/libexec/revnetgroup/hash.h new file mode 100644 index 0000000..2212534 --- /dev/null +++ b/libexec/revnetgroup/hash.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +/* Groupname entry hung off a member_entry node. */ +struct grouplist { + char *groupname; + struct grouplist *next; +}; + +/* Entry in the cooked member list hash table. */ +struct member_entry { + char *key; + char *domain; + struct grouplist *groups; + struct member_entry *next; +}; + +/* Entry in the raw netgroup table. */ +struct group_entry { + char *key; + char *data; + struct grps *groups; + struct group_entry *next; +}; + +/* Table size (chosen arbitrarily). Not too big, not too small. */ +#define TABLESIZE 256 +#define HASH_MASK 0x000000FF diff --git a/libexec/revnetgroup/parse_netgroup.c b/libexec/revnetgroup/parse_netgroup.c new file mode 100644 index 0000000..99dfaeb --- /dev/null +++ b/libexec/revnetgroup/parse_netgroup.c @@ -0,0 +1,370 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Rick Macklem at The University of Guelph. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "$Id$"; +#endif /* LIBC_SCCS and not lint */ + +/* + * This is a specially hacked-up version of getnetgrent.c used to parse + * data from the stored hash table of netgroup info rather than from a + * file. It's used mainly for the parse_netgroup() function. All the YP + * stuff and file support has been stripped out since it isn't needed. + */ + +#include <stdio.h> +#include <strings.h> +#include <stdlib.h> +#include <unistd.h> +#include "hash.h" + +/* + * Static Variables and functions used by setnetgrent(), getnetgrent() and + * __endnetgrent(). + * There are two linked lists: + * - linelist is just used by setnetgrent() to parse the net group file via. + * parse_netgrp() + * - netgrp is the list of entries for the current netgroup + */ +struct linelist { + struct linelist *l_next; /* Chain ptr. */ + int l_parsed; /* Flag for cycles */ + char *l_groupname; /* Name of netgroup */ + char *l_line; /* Netgroup entrie(s) to be parsed */ +}; + +struct netgrp { + struct netgrp *ng_next; /* Chain ptr */ + char *ng_str[3]; /* Field pointers, see below */ +}; +#define NG_HOST 0 /* Host name */ +#define NG_USER 1 /* User name */ +#define NG_DOM 2 /* and Domain name */ + +static struct linelist *linehead = (struct linelist *)0; +static struct netgrp *nextgrp = (struct netgrp *)0; +static struct { + struct netgrp *gr; + char *grname; +} grouphead = { + (struct netgrp *)0, + (char *)0, +}; +static int parse_netgrp(); +static struct linelist *read_for_group(); +void __setnetgrent(), __endnetgrent(); +int __getnetgrent(); +extern struct group_entry *gtable[]; +extern char *lookup __P(( struct group_entry *[], char * )); +#define LINSIZ 1024 /* Length of netgroup file line */ + +/* + * setnetgrent() + * Parse the netgroup file looking for the netgroup and build the list + * of netgrp structures. Let parse_netgrp() and read_for_group() do + * most of the work. + */ +void +__setnetgrent(group) + char *group; +{ + /* Sanity check */ + + if (group == NULL || !strlen(group)) + return; + + if (grouphead.gr == (struct netgrp *)0 || + strcmp(group, grouphead.grname)) { + __endnetgrent(); + if (parse_netgrp(group)) + __endnetgrent(); + else { + grouphead.grname = (char *) + malloc(strlen(group) + 1); + strcpy(grouphead.grname, group); + } + } + nextgrp = grouphead.gr; +} + +/* + * Get the next netgroup off the list. + */ +int +__getnetgrent(hostp, userp, domp) + char **hostp, **userp, **domp; +{ + if (nextgrp) { + *hostp = nextgrp->ng_str[NG_HOST]; + *userp = nextgrp->ng_str[NG_USER]; + *domp = nextgrp->ng_str[NG_DOM]; + nextgrp = nextgrp->ng_next; + return (1); + } + return (0); +} + +/* + * __endnetgrent() - cleanup + */ +void +__endnetgrent() +{ + register struct linelist *lp, *olp; + register struct netgrp *gp, *ogp; + + lp = linehead; + while (lp) { + olp = lp; + lp = lp->l_next; + free(olp->l_groupname); + free(olp->l_line); + free((char *)olp); + } + linehead = (struct linelist *)0; + if (grouphead.grname) { + free(grouphead.grname); + grouphead.grname = (char *)0; + } + gp = grouphead.gr; + while (gp) { + ogp = gp; + gp = gp->ng_next; + if (ogp->ng_str[NG_HOST]) + free(ogp->ng_str[NG_HOST]); + if (ogp->ng_str[NG_USER]) + free(ogp->ng_str[NG_USER]); + if (ogp->ng_str[NG_DOM]) + free(ogp->ng_str[NG_DOM]); + free((char *)ogp); + } + grouphead.gr = (struct netgrp *)0; +} + +/* + * Parse the netgroup file setting up the linked lists. + */ +static int +parse_netgrp(group) + char *group; +{ + register char *spos, *epos; + register int len, strpos; +#ifdef DEBUG + register int fields; +#endif + char *pos, *gpos; + struct netgrp *grp; + struct linelist *lp = linehead; + + /* + * First, see if the line has already been read in. + */ + while (lp) { + if (!strcmp(group, lp->l_groupname)) + break; + lp = lp->l_next; + } + if (lp == (struct linelist *)0 && + (lp = read_for_group(group)) == (struct linelist *)0) + return (1); + if (lp->l_parsed) { +#ifdef DEBUG + /* + * This error message is largely superflous since the + * code handles the error condition sucessfully, and + * spewing it out from inside libc can actually hose + * certain programs. + */ + fprintf(stderr, "Cycle in netgroup %s\n", lp->l_groupname); +#endif + return (1); + } else + lp->l_parsed = 1; + pos = lp->l_line; + /* Watch for null pointer dereferences, dammit! */ + while (pos != NULL && *pos != '\0') { + if (*pos == '(') { + grp = (struct netgrp *)malloc(sizeof (struct netgrp)); + bzero((char *)grp, sizeof (struct netgrp)); + grp->ng_next = grouphead.gr; + grouphead.gr = grp; + pos++; + gpos = strsep(&pos, ")"); +#ifdef DEBUG + fields = 0; +#endif + for (strpos = 0; strpos < 3; strpos++) { + if ((spos = strsep(&gpos, ","))) { +#ifdef DEBUG + fields++; +#endif + while (*spos == ' ' || *spos == '\t') + spos++; + if ((epos = strpbrk(spos, " \t"))) { + *epos = '\0'; + len = epos - spos; + } else + len = strlen(spos); + if (len > 0) { + grp->ng_str[strpos] = (char *) + malloc(len + 1); + bcopy(spos, grp->ng_str[strpos], + len + 1); + } + } else { + /* + * All other systems I've tested + * return NULL for empty netgroup + * fields. It's up to user programs + * to handle the NULLs appropriately. + */ + grp->ng_str[strpos] = NULL; + } + } +#ifdef DEBUG + /* + * Note: on other platforms, malformed netgroup + * entries are not normally flagged. While we + * can catch bad entries and report them, we should + * stay silent by default for compatibility's sake. + */ + if (fields < 3) + fprintf(stderr, "Bad entry (%s%s%s%s%s) in netgroup \"%s\"\n", + grp->ng_str[NG_HOST] == NULL ? "" : grp->ng_str[NG_HOST], + grp->ng_str[NG_USER] == NULL ? "" : ",", + grp->ng_str[NG_USER] == NULL ? "" : grp->ng_str[NG_USER], + grp->ng_str[NG_DOM] == NULL ? "" : ",", + grp->ng_str[NG_DOM] == NULL ? "" : grp->ng_str[NG_DOM], + lp->l_groupname); +#endif + } else { + spos = strsep(&pos, ", \t"); + if (parse_netgrp(spos)) + continue; + } + /* Watch for null pointer dereferences, dammit! */ + if (pos != NULL) + while (*pos == ' ' || *pos == ',' || *pos == '\t') + pos++; + } + return (0); +} + +/* + * Read the netgroup file and save lines until the line for the netgroup + * is found. Return 1 if eof is encountered. + */ +static struct linelist * +read_for_group(group) + char *group; +{ + register char *pos, *spos, *linep, *olinep; + register int len, olen; + int cont; + struct linelist *lp; + char line[LINSIZ + 1]; + char *key = NULL, *data = NULL; + + data = lookup (gtable, group); + sprintf(line, "%s %s", group, data); + pos = (char *)&line; +#ifdef CANT_HAPPEN + if (*pos == '#') + continue; +#endif + while (*pos == ' ' || *pos == '\t') + pos++; + spos = pos; + while (*pos != ' ' && *pos != '\t' && *pos != '\n' && + *pos != '\0') + pos++; + len = pos - spos; + while (*pos == ' ' || *pos == '\t') + pos++; + if (*pos != '\n' && *pos != '\0') { + lp = (struct linelist *)malloc(sizeof (*lp)); + lp->l_parsed = 0; + lp->l_groupname = (char *)malloc(len + 1); + bcopy(spos, lp->l_groupname, len); + *(lp->l_groupname + len) = '\0'; + len = strlen(pos); + olen = 0; + /* + * Loop around handling line continuations. + */ + do { + if (*(pos + len - 1) == '\n') + len--; + if (*(pos + len - 1) == '\\') { + len--; + cont = 1; + } else + cont = 0; + if (len > 0) { + linep = (char *)malloc(olen + len + 1); + if (olen > 0) { + bcopy(olinep, linep, olen); + free(olinep); + } + bcopy(pos, linep + olen, len); + olen += len; + *(linep + olen) = '\0'; + olinep = linep; + } +#ifdef CANT_HAPPEN + if (cont) { + if (fgets(line, LINSIZ, netf)) { + pos = line; + len = strlen(pos); + } else + cont = 0; + } +#endif + } while (cont); + lp->l_line = linep; + lp->l_next = linehead; + linehead = lp; +#ifdef CANT_HAPPEN + /* + * If this is the one we wanted, we are done. + */ + if (!strcmp(lp->l_groupname, group)) +#endif + return (lp); + } + return ((struct linelist *)0); +} diff --git a/libexec/revnetgroup/revnetgroup.8 b/libexec/revnetgroup/revnetgroup.8 new file mode 100644 index 0000000..0a1330e --- /dev/null +++ b/libexec/revnetgroup/revnetgroup.8 @@ -0,0 +1,137 @@ +.\" Copyright (c) 1995 +.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Bill Paul. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id$ +.\" +.Dd October 24, 1995 +.Dt REVNETGROUP 8 +.Os +.Sh NAME +.Nm revnetgroup +.Nd "generate reverse netgroup data" +.Sh SYNOPSIS +.Nm revnetgroup +.Fl u +.Fl h +.Op Fl f Ar netgroup_file +.Sh DESCRIPTION +.Nm revnetgroup +processes the contents of a file in +.Xr netgroup 5 +format into what is called +.Pa reverse netgroup +form. That is, where the original file shows +netgroup memberships in terms of which members reside in a particular +group, the reverse netgroup format specifies what groups are associated +with a particular member. This information is used to generate the +.Nm netgroup.byuser +and +.Nm netgroup.byhosts +NIS maps. These reverse netgroup maps are used to help speed up +netgroup lookups, particularly for the +.Fn innetgr +library function. +.Pp +For example, the standard +.Nm /etc/netgroup +file may list a netgroup and a list of its members. Here, the +netgroup is considered the +.Pa key +and the member names are the +.Pa data . +By contrast, the reverse +.Nm netgroup.byusers +database lists each unique +member as the key and the netgroups to which the members belong become +the data. Seperate databases are created to hold information pertaining +to users and hosts; this allows netgroup username lookups +and netgroup hostname lookups to be performed using independent keyspaces. +.Pp +By constructing these reverse netgroup databases (and the corresponding +NIS maps) in advance, the +.Xr getnetgrent 3 +library functions are spared from having to work out the dependencies +themselves on the fly. This is important on networks with large numbers +of users and hosts, since it can take a considerable amount of time +to process very large netgroup databases. +.Pp +The +.Nm revnetgroup +command prints its results on the standard output. It is usually called +only by +.Nm /var/yp/Makefile +when rebuilding the NIS netgroup maps. +.Pp +.Sh OPTIONS +The +.Nm revnetgroup +command supports the following options: +.Bl -tag -width flag +.It Fl u +Generate netgroup.byuser output; only username information in the +original netgroup file is processed. +.It Fl h +Generate netgroup.byhost output; only hostname information in the +original netgroup file is processed. (Note at least one of the +.Fl u +or +.Fl h +flags must be specified.) +.It Op Fl f Ar netgroup_file +The +.Nm revnetgroup +command uses +.Nm /etc/netgroup +as its default input file. The +.Fl f +flag allows the user to specify an alternate input file. Specifying ``-'' +as the input file causes +.Nm revnetgroup +to read from the standard input. +.El +.Sh FILES +.Bl -tag -width Pa -compact +.It Pa /var/yp/Makefile +The Makefile that calls +.Nm yp_mkdb +and +.Nm revnetgroup +to build the NIS databases. +.It Pa /etc/netgroup +The default netgroup database file. This file is most often found +only on the NIS master server. +.El +.Sh SEE ALSO +.Xr yp 4 , +.Xr netgroup 5 , +.Xr yp_mkdb 8 , +.Xr getnetgrent 3 +.Sh AUTHOR +Bill Paul <wpaul@ctr.columbia.edu> diff --git a/libexec/revnetgroup/revnetgroup.c b/libexec/revnetgroup/revnetgroup.c new file mode 100644 index 0000000..4a3ab5a --- /dev/null +++ b/libexec/revnetgroup/revnetgroup.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * reverse netgroup map generator program + * + * Written by Bill Paul <wpaul@ctr.columbia.edu> + * Center for Telecommunications Research + * Columbia University, New York City + * + * $Id$ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "hash.h" + +#define LINSIZ 1024 + +/* Default location of netgroup file. */ +char *netgroup = "/etc/netgroup"; + +/* Stored hash table version of 'forward' netgroup database. */ +struct group_entry *gtable[TABLESIZE]; + +/* + * Stored hash table of 'reverse' netgroup member database + * which we will construct. + */ +struct member_entry *mtable[TABLESIZE]; + +extern void store __P(( struct group_entry ** , char *, char * )); +extern void mstore __P(( struct member_entry ** , char *, char *, char * )); +extern char *lookup __P(( struct group_entry **, char * )); + +void usage(prog) +char *prog; +{ + fprintf (stderr,"usage: %s -u|-h [-f netgroup file]\n",prog); + exit(1); +} + +extern char *optarg; + +main(argc, argv) +int argc; +char *argv[0]; +{ + FILE *fp; + char readbuf[LINSIZ]; + struct group_entry *gcur; + struct member_entry *mcur; + char *host, *user, *domain; + char ch; + char *key = NULL, *data = NULL; + int hosts, i; + + if (argc < 2) + usage(argv[0]); + + while ((ch = getopt(argc, argv, "uhf:")) != EOF) { + switch(ch) { + case 'u': + hosts = 0; + break; + case 'h': + hosts = 1; + break; + case 'f': + netgroup = optarg; + break; + default: + usage(argv[0]); + break; + } + } + + if (strcmp(netgroup, "-")) { + if ((fp = fopen(netgroup, "r")) == NULL) { + perror(netgroup); + exit(1); + } + } else { + fp = stdin; + } + + /* Stuff all the netgroup names and members into a hash table. */ + while (fgets(readbuf, LINSIZ, fp)) { + if (readbuf[0] == '#') + continue; + if ((data = (char *)(strpbrk(readbuf, " \t") + 1)) < (char *)2) + continue; + key = (char *)&readbuf; + *(data - 1) = '\0'; + store(gtable, key, data); + } + + fclose(fp); + + /* + * Find all members of each netgroup and keep track of which + * group they belong to. + */ + for (i = 0; i < TABLESIZE; i++) { + gcur = gtable[i]; + while(gcur) { + __setnetgrent(gcur->key); + while(__getnetgrent(&host, &user, &domain) != NULL) { + if (hosts ? host && strcmp(host,"-") : user && strcmp(user, "-")) + mstore(mtable, hosts ? host : user, gcur->key, domain); + } + gcur = gcur->next; + } + } + + /* Release resources used by the netgroup parser code. */ + __endnetgrent(); + + /* Spew out the results. */ + for (i = 0; i < TABLESIZE; i++) { + mcur = mtable[i]; + while(mcur) { + struct grouplist *tmp; + printf ("%s.%s\t", mcur->key, mcur->domain); + tmp = mcur->groups; + while(tmp) { + printf ("%s", tmp->groupname); + tmp = tmp->next; + if (tmp) + printf(","); + } + mcur = mcur->next; + printf ("\n"); + } + } + + /* Let the OS free all our resources. */ + exit(0); +} diff --git a/libexec/rexecd/Makefile b/libexec/rexecd/Makefile new file mode 100644 index 0000000..006a10e --- /dev/null +++ b/libexec/rexecd/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= rexecd +MAN8= rexecd.8 +CFLAGS+= -DSKEY + +DPADD= ${LIBSKEY} ${LIBMD} ${LIBCRYPT} +LDADD= -lskey -lmd -lcrypt + +.include <bsd.prog.mk> diff --git a/libexec/rexecd/rexecd.8 b/libexec/rexecd/rexecd.8 new file mode 100644 index 0000000..34059b8 --- /dev/null +++ b/libexec/rexecd/rexecd.8 @@ -0,0 +1,152 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)rexecd.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd September 23, 1994 +.Dt REXECD 8 +.Os BSD 4.2 +.Sh NAME +.Nm rexecd +.Nd remote execution server +.Sh SYNOPSIS +.Nm rexecd +.Sh DESCRIPTION +.Nm Rexecd +is the server for the +.Xr rexec 3 +routine. The server provides remote execution facilities +with authentication based on user names and +passwords. +.Pp +.Nm Rexecd +listens for service requests at the port indicated in +the ``exec'' service specification; see +.Xr services 5 . +When a service request is received the following protocol +is initiated: +.Bl -enum +.It +The server reads characters from the socket up +to a NUL +.Pq Ql \e0 +byte. The resultant string is +interpreted as an +.Tn ASCII +number, base 10. +.It +If the number received in step 1 is non-zero, +it is interpreted as the port number of a secondary +stream to be used for the +.Em stderr . +A second connection is then created to the specified +port on the client's machine. +.It +A NUL terminated user name of at most 16 characters +is retrieved on the initial socket. +.It +A NUL terminated, unencrypted password of at most +16 characters is retrieved on the initial socket. +.It +A NUL terminated command to be passed to a +shell is retrieved on the initial socket. The length of +the command is limited by the upper bound on the size of +the system's argument list. +.It +.Nm Rexecd +then validates the user as is done at login time +and, if the authentication was successful, changes +to the user's home directory, and establishes the user +and group protections of the user. +If any of these steps fail the connection is +aborted with a diagnostic message returned. +.It +A NUL byte is returned on the initial socket +and the command line is passed to the normal login +shell of the user. The +shell inherits the network connections established +by +.Nm rexecd . +.El +.Sh CAVEATS +.Nm Rexecd +will no longer allow root logins, access for users listed in /etc/ftpusers, +or access for users with no passwords, which were all serious security holes. +The entire concept of rexec/rexecd is a major security hole and an example +of how not to do things. +.Nm Rexecd +is disabled by default in /etc/inetd.conf. +.Sh DIAGNOSTICS +Except for the last one listed below, +all diagnostic messages are returned on the initial socket, +after which any network connections are closed. +An error is indicated by a leading byte with a value of +1 (0 is returned in step 7 above upon successful completion +of all the steps prior to the command execution). +.Pp +.Bl -tag -width Ds +.It Sy username too long +The name is +longer than 16 characters. +.It Sy password too long +The password is longer than 16 characters. +.It Sy command too long +The command line passed exceeds the size of the argument +list (as configured into the system). +.It Sy Login incorrect. +No password file entry for the user name existed. +.It Sy Password incorrect. +The wrong password was supplied. +.It Sy \&No remote directory. +The +.Xr chdir +command to the home directory failed. +.It Sy Try again. +A +.Xr fork +by the server failed. +.It Sy <shellname>: ... +The user's login shell could not be started. +This message is returned +on the connection associated with the +.Em stderr , +and is not preceded by a flag byte. +.El +.Sh SEE ALSO +.Xr rexec 3 +.Sh BUGS +A facility to allow all data and password exchanges to be encrypted should be +present. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/libexec/rexecd/rexecd.c b/libexec/rexecd/rexecd.c new file mode 100644 index 0000000..9c6d029 --- /dev/null +++ b/libexec/rexecd/rexecd.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rexecd.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <netdb.h> +#include <paths.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <syslog.h> +#include <netdb.h> + +/*VARARGS1*/ +int error(); + +char username[20] = "USER="; +char homedir[64] = "HOME="; +char shell[64] = "SHELL="; +char path[sizeof(_PATH_DEFPATH) + sizeof("PATH=")] = "PATH="; +char *envinit[] = + {homedir, shell, path, username, 0}; +char **environ; +char *remote; + +struct sockaddr_in asin = { AF_INET }; + +/* + * remote execute server: + * username\0 + * password\0 + * command\0 + * data + */ +/*ARGSUSED*/ +main(argc, argv) + int argc; + char **argv; +{ + struct sockaddr_in from; + int fromlen; + struct hostent *hp; + + openlog(argv[0], LOG_PID, LOG_AUTH); + fromlen = sizeof (from); + if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { + (void)fprintf(stderr, + "rexecd: getpeername: %s\n", strerror(errno)); + exit(1); + } + + hp = gethostbyaddr((char *) &from.sin_addr, sizeof(from.sin_addr), + from.sin_family); + remote = inet_ntoa(from.sin_addr); + remote = (hp != NULL) ? hp->h_name : inet_ntoa(from.sin_addr); + + doit(0, &from); +} + +doit(f, fromp) + int f; + struct sockaddr_in *fromp; +{ + FILE *fp; + char cmdbuf[NCARGS+1], *cp, *namep; +#ifdef SKEY + char *skey_crypt(); + char user[16], pass[100]; +#else /* SKEY */ + char user[16], pass[16]; +#endif /* SKEY */ + struct passwd *pwd; + int s; + u_short port; + int pv[2], pid, ready, readfrom, cc; + char buf[BUFSIZ], sig; + int one = 1; + + (void) signal(SIGINT, SIG_DFL); + (void) signal(SIGQUIT, SIG_DFL); + (void) signal(SIGTERM, SIG_DFL); +#ifdef DEBUG + { int t = open(_PATH_TTY, 2); + if (t >= 0) { + ioctl(t, TIOCNOTTY, (char *)0); + (void) close(t); + } + } +#endif + dup2(f, 0); + dup2(f, 1); + dup2(f, 2); + (void) alarm(60); + port = 0; + for (;;) { + char c; + if (read(f, &c, 1) != 1) + exit(1); + if (c == 0) + break; + port = port * 10 + c - '0'; + } + (void) alarm(0); + if (port != 0) { + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + exit(1); + if (bind(s, (struct sockaddr *)&asin, sizeof (asin)) < 0) + exit(1); + (void) alarm(60); + fromp->sin_port = htons(port); + if (connect(s, (struct sockaddr *)fromp, sizeof (*fromp)) < 0) + exit(1); + (void) alarm(0); + } + getstr(user, sizeof(user), "username"); + getstr(pass, sizeof(pass), "password"); + getstr(cmdbuf, sizeof(cmdbuf), "command"); + setpwent(); + pwd = getpwnam(user); + if (pwd == NULL) { + error("Login incorrect.\n"); + exit(1); + } + endpwent(); + if (*pwd->pw_passwd != '\0') { +#ifdef SKEY + namep = skey_crypt(pass, pwd->pw_passwd, pwd, + skeyaccess(user, NULL, remote)); +#else /* SKEY */ + namep = crypt(pass, pwd->pw_passwd); +#endif /* SKEY */ + if (strcmp(namep, pwd->pw_passwd)) { + syslog(LOG_ERR, "LOGIN FAILURE from %s, %s", + remote, user); + error("Login incorrect.\n"); + exit(1); + } + } + + if (pwd->pw_uid == 0 || *pwd->pw_passwd == '\0' || + (pwd->pw_expire && time(NULL) >= pwd->pw_expire)) { + syslog(LOG_ERR, "%s LOGIN REFUSED from %s", user, remote); + error("Login incorrect.\n"); + exit(1); + } + + if ((fp = fopen(_PATH_FTPUSERS, "r")) != NULL) { + while (fgets(buf, sizeof(buf), fp) != NULL) { + if ((cp = index(buf, '\n')) != NULL) + *cp = '\0'; + if (strcmp(buf, pwd->pw_name) == 0) { + syslog(LOG_ERR, "%s LOGIN REFUSED from %s", + user, remote); + error("Login incorrect.\n"); + exit(1); + } + } + } + (void) fclose(fp); + + syslog(LOG_INFO, "login from %s as %s", remote, user); + + if (chdir(pwd->pw_dir) < 0) { + error("No remote directory.\n"); + exit(1); + } + (void) write(2, "\0", 1); + if (port) { + (void) pipe(pv); + pid = fork(); + if (pid == -1) { + error("Try again.\n"); + exit(1); + } + if (pid) { + (void) close(0); (void) close(1); (void) close(2); + (void) close(f); (void) close(pv[1]); + readfrom = (1<<s) | (1<<pv[0]); + ioctl(pv[1], FIONBIO, (char *)&one); + /* should set s nbio! */ + do { + ready = readfrom; + (void) select(16, (fd_set *)&ready, + (fd_set *)NULL, (fd_set *)NULL, + (struct timeval *)NULL); + if (ready & (1<<s)) { + if (read(s, &sig, 1) <= 0) + readfrom &= ~(1<<s); + else + killpg(pid, sig); + } + if (ready & (1<<pv[0])) { + cc = read(pv[0], buf, sizeof (buf)); + if (cc <= 0) { + shutdown(s, 1+1); + readfrom &= ~(1<<pv[0]); + } else + (void) write(s, buf, cc); + } + } while (readfrom); + exit(0); + } + setpgrp(0, getpid()); + (void) close(s); (void)close(pv[0]); + dup2(pv[1], 2); + } + if (*pwd->pw_shell == '\0') + pwd->pw_shell = _PATH_BSHELL; + if (f > 2) + (void) close(f); + if (setlogin(pwd->pw_name) < 0) + syslog(LOG_ERR, "setlogin() failed: %m"); + (void) setgid((gid_t)pwd->pw_gid); + initgroups(pwd->pw_name, pwd->pw_gid); + (void) setuid((uid_t)pwd->pw_uid); + (void)strcat(path, _PATH_DEFPATH); + environ = envinit; + strncat(homedir, pwd->pw_dir, sizeof(homedir)-6); + strncat(shell, pwd->pw_shell, sizeof(shell)-7); + strncat(username, pwd->pw_name, sizeof(username)-6); + cp = strrchr(pwd->pw_shell, '/'); + if (cp) + cp++; + else + cp = pwd->pw_shell; + execl(pwd->pw_shell, cp, "-c", cmdbuf, 0); + perror(pwd->pw_shell); + exit(1); +} + +/*VARARGS1*/ +error(fmt, a1, a2, a3) + char *fmt; + int a1, a2, a3; +{ + char buf[BUFSIZ]; + + buf[0] = 1; + (void) sprintf(buf+1, fmt, a1, a2, a3); + (void) write(2, buf, strlen(buf)); +} + +getstr(buf, cnt, err) + char *buf; + int cnt; + char *err; +{ + char c; + + do { + if (read(0, &c, 1) != 1) + exit(1); + *buf++ = c; + if (--cnt == 0) { + error("%s too long\n", err); + exit(1); + } + } while (c != 0); +} diff --git a/libexec/rlogind/Makefile b/libexec/rlogind/Makefile new file mode 100644 index 0000000..e7c147d --- /dev/null +++ b/libexec/rlogind/Makefile @@ -0,0 +1,17 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= rlogind +SRCS= rlogind.c +MAN8= rlogind.8 +DPADD= ${LIBUTIL} +LDADD= -lutil + +.if exists(${DESTDIR}/usr/lib/libkrb.a) && (defined(MAKE_KERBEROS) \ + || defined(MAKE_EBONES)) +CFLAGS+=-DKERBEROS -DCRYPT +DPADD= ${LIBKRB} ${LIBDES} +LDADD+= -lkrb -ldes +DISTRIBUTION= krb +.endif + +.include <bsd.prog.mk> diff --git a/libexec/rlogind/pathnames.h b/libexec/rlogind/pathnames.h new file mode 100644 index 0000000..5240f19 --- /dev/null +++ b/libexec/rlogind/pathnames.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + */ + +#include <paths.h> + +#define _PATH_LOGIN "/usr/bin/login" diff --git a/libexec/rlogind/rlogind.8 b/libexec/rlogind/rlogind.8 new file mode 100644 index 0000000..68b89a6 --- /dev/null +++ b/libexec/rlogind/rlogind.8 @@ -0,0 +1,171 @@ +.\" Copyright (c) 1983, 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)rlogind.8 8.1 (Berkeley) 6/4/93 +.\" +.Dd June 4, 1993 +.Dt RLOGIND 8 +.Os BSD 4.2 +.Sh NAME +.Nm rlogind +.Nd remote login server +.Sh SYNOPSIS +.Nm rlogind +.Op Fl Daln +.Sh DESCRIPTION +.Nm Rlogind +is the server for the +.Xr rlogin 1 +program. The server provides a remote login facility +with authentication based on privileged port numbers from trusted hosts. +.Pp +Options supported by +.Nm rlogind : +.Bl -tag -width Ds +.It Fl D +Set TCP_NODELAY socket option. This improves responsiveness at the expense of +some additional network traffic. +.It Fl a +Ask hostname for verification. +.It Fl l +Prevent any authentication based on the user's +.Dq Pa .rhosts +file, unless the user is logging in as the superuser. +.It Fl n +Disable keep-alive messages. +.El +.Pp +.Nm Rlogind +listens for service requests at the port indicated in +the ``login'' service specification; see +.Xr services 5 . +When a service request is received the following protocol +is initiated: +.Bl -enum +.It +The server checks the client's source port. +If the port is not in the range 512-1023, the server +aborts the connection. +.It +The server checks the client's source address +and requests the corresponding host name (see +.Xr gethostbyaddr 3 , +.Xr hosts 5 +and +.Xr named 8 ) . +If the hostname cannot be determined, +the dot-notation representation of the host address is used. +If the hostname is in the same domain as the server (according to +the last two components of the domain name), +or if the +.Fl a +option is given, +the addresses for the hostname are requested, +verifying that the name and address correspond. +Normal authentication is bypassed if the address verification fails. +.El +.Pp +Once the source port and address have been checked, +.Nm rlogind +proceeds with the authentication process described in +.Xr rshd 8 . +It then allocates a pseudo terminal (see +.Xr pty 4 ) , +and manipulates file descriptors so that the slave +half of the pseudo terminal becomes the +.Em stdin , +.Em stdout , +and +.Em stderr +for a login process. +The login process is an instance of the +.Xr login 1 +program, invoked with the +.Fl f +option if authentication has succeeded. +If automatic authentication fails, the user is +prompted to log in as if on a standard terminal line. +.Pp +The parent of the login process manipulates the master side of +the pseudo terminal, operating as an intermediary +between the login process and the client instance of the +.Xr rlogin +program. In normal operation, the packet protocol described +in +.Xr pty 4 +is invoked to provide +.Ql ^S/^Q +type facilities and propagate +interrupt signals to the remote programs. The login process +propagates the client terminal's baud rate and terminal type, +as found in the environment variable, +.Ql Ev TERM ; +see +.Xr environ 7 . +The screen or window size of the terminal is requested from the client, +and window size changes from the client are propagated to the pseudo terminal. +.Pp +Transport-level keepalive messages are enabled unless the +.Fl n +option is present. +The use of keepalive messages allows sessions to be timed out +if the client crashes or becomes unreachable. +.Sh DIAGNOSTICS +All initial diagnostic messages are indicated +by a leading byte with a value of 1, +after which any network connections are closed. +If there are no errors before +.Xr login +is invoked, a null byte is returned as in indication of success. +.Bl -tag -width Ds +.It Sy Try again. +A +.Xr fork +by the server failed. +.El +.Sh SEE ALSO +.Xr login 1 , +.Xr ruserok 3 , +.Xr rshd 8 +.Sh BUGS +The authentication procedure used here assumes the integrity +of each client machine and the connecting medium. This is +insecure, but is useful in an ``open'' environment. +.Pp +A facility to allow all data exchanges to be encrypted should be +present. +.Pp +A more extensible protocol should be used. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/libexec/rlogind/rlogind.c b/libexec/rlogind/rlogind.c new file mode 100644 index 0000000..e88aa2e --- /dev/null +++ b/libexec/rlogind/rlogind.c @@ -0,0 +1,772 @@ +/*- + * Copyright (c) 1983, 1988, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1988, 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rlogind.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * remote login server: + * \0 + * remuser\0 + * locuser\0 + * terminal_type/speed\0 + * data + */ + +#define FD_SETSIZE 16 /* don't need many bits for select */ +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <signal.h> +#include <termios.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include <pwd.h> +#include <syslog.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include "pathnames.h" + +#ifndef TIOCPKT_WINDOW +#define TIOCPKT_WINDOW 0x80 +#endif + +#ifdef KERBEROS +#include <kerberosIV/des.h> +#include <kerberosIV/krb.h> +#define SECURE_MESSAGE "This rlogin session is using DES encryption for all transmissions.\r\n" + +AUTH_DAT *kdata; +KTEXT ticket; +u_char auth_buf[sizeof(AUTH_DAT)]; +u_char tick_buf[sizeof(KTEXT_ST)]; +Key_schedule schedule; +int doencrypt, retval, use_kerberos, vacuous; + +#define ARGSTR "Dalnkvx" +#else +#define ARGSTR "Daln" +#endif /* KERBEROS */ + +char *env[2]; +#define NMAX 30 +char lusername[NMAX+1], rusername[NMAX+1]; +static char term[64] = "TERM="; +#define ENVSIZE (sizeof("TERM=")-1) /* skip null for concatenation */ +int keepalive = 1; +int check_all = 0; +int no_delay; + +struct passwd *pwd; + +void doit __P((int, struct sockaddr_in *)); +int control __P((int, char *, int)); +void protocol __P((int, int)); +void cleanup __P((int)); +void fatal __P((int, char *, int)); +int do_rlogin __P((struct sockaddr_in *)); +void getstr __P((char *, int, char *)); +void setup_term __P((int)); +int do_krb_login __P((struct sockaddr_in *)); +void usage __P((void)); +int local_domain __P((char *)); +char *topdomain __P((char *)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int __check_rhosts_file; + struct sockaddr_in from; + int ch, fromlen, on; + + openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH); + + opterr = 0; + while ((ch = getopt(argc, argv, ARGSTR)) != EOF) + switch (ch) { + case 'D': + no_delay = 1; + break; + case 'a': + check_all = 1; + break; + case 'l': + __check_rhosts_file = 0; + break; + case 'n': + keepalive = 0; + break; +#ifdef KERBEROS + case 'k': + use_kerberos = 1; + break; + case 'v': + vacuous = 1; + break; +#ifdef CRYPT + case 'x': + doencrypt = 1; + break; +#endif +#endif + case '?': + default: + usage(); + break; + } + argc -= optind; + argv += optind; + +#ifdef KERBEROS + if (use_kerberos && vacuous) { + usage(); + fatal(STDERR_FILENO, "only one of -k and -v allowed", 0); + } +#endif + fromlen = sizeof (from); + if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { + syslog(LOG_ERR,"Can't get peer name of remote host: %m"); + fatal(STDERR_FILENO, "Can't get peer name of remote host", 1); + } + on = 1; + if (keepalive && + setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) + syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); + if (no_delay && + setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) + syslog(LOG_WARNING, "setsockopt (TCP_NODELAY): %m"); + on = IPTOS_LOWDELAY; + if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) + syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + + doit(0, &from); +} + +int child; +int netf; +char line[MAXPATHLEN]; +int confirmed; + +struct winsize win = { 0, 0, 0, 0 }; + + +void +doit(f, fromp) + int f; + struct sockaddr_in *fromp; +{ + int master, pid, on = 1; + int authenticated = 0; + register struct hostent *hp; + char hostname[2 * MAXHOSTNAMELEN + 1]; + char c; + + alarm(60); + read(f, &c, 1); + + if (c != 0) + exit(1); +#ifdef KERBEROS + if (vacuous) + fatal(f, "Remote host requires Kerberos authentication", 0); +#endif + + alarm(0); + fromp->sin_port = ntohs((u_short)fromp->sin_port); + hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof(struct in_addr), + fromp->sin_family); + if (hp) + (void)strcpy(hostname, hp->h_name); + else + (void)strcpy(hostname, inet_ntoa(fromp->sin_addr)); + +#ifdef KERBEROS + if (use_kerberos) { + retval = do_krb_login(fromp); + if (retval == 0) + authenticated++; + else if (retval > 0) + fatal(f, krb_err_txt[retval], 0); + write(f, &c, 1); + confirmed = 1; /* we sent the null! */ + } else +#endif + { + if (fromp->sin_family != AF_INET || + fromp->sin_port >= IPPORT_RESERVED || + fromp->sin_port < IPPORT_RESERVED/2) { + syslog(LOG_NOTICE, "Connection from %s on illegal port", + inet_ntoa(fromp->sin_addr)); + fatal(f, "Permission denied", 0); + } +#ifdef IP_OPTIONS + { + u_char optbuf[BUFSIZ/3], *cp; + char lbuf[BUFSIZ], *lp; + int optsize = sizeof(optbuf), ipproto; + struct protoent *ip; + + if ((ip = getprotobyname("ip")) != NULL) + ipproto = ip->p_proto; + else + ipproto = IPPROTO_IP; + if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, + &optsize) == 0 && optsize != 0) { + lp = lbuf; + for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) + sprintf(lp, " %2.2x", *cp); + syslog(LOG_NOTICE, + "Connection received using IP options (ignored):%s", + lbuf); + if (setsockopt(0, ipproto, IP_OPTIONS, + (char *)NULL, optsize) != 0) { + syslog(LOG_ERR, + "setsockopt IP_OPTIONS NULL: %m"); + exit(1); + } + } + } +#endif + if (do_rlogin(fromp) == 0) + authenticated++; + } + if (confirmed == 0) { + write(f, "", 1); + confirmed = 1; /* we sent the null! */ + } +#ifdef KERBEROS +#ifdef CRYPT + if (doencrypt) + (void) des_write(f, SECURE_MESSAGE, sizeof(SECURE_MESSAGE) - 1); +#endif +#endif + netf = f; + + pid = forkpty(&master, line, NULL, &win); + if (pid < 0) { + if (errno == ENOENT) + fatal(f, "Out of ptys", 0); + else + fatal(f, "Forkpty", 1); + } + if (pid == 0) { + if (f > 2) /* f should always be 0, but... */ + (void) close(f); + setup_term(0); + if (*lusername=='-') { + syslog(LOG_ERR, "tried to pass user \"%s\" to login", + lusername); + fatal(STDERR_FILENO, "invalid user", 0); + } + if (authenticated) { +#ifdef KERBEROS + if (use_kerberos && (pwd->pw_uid == 0)) + syslog(LOG_INFO|LOG_AUTH, + "ROOT Kerberos login from %s.%s@%s on %s\n", + kdata->pname, kdata->pinst, kdata->prealm, + hostname); +#endif + + execl(_PATH_LOGIN, "login", "-p", + "-h", hostname, "-f", lusername, (char *)NULL); + } else + execl(_PATH_LOGIN, "login", "-p", + "-h", hostname, lusername, (char *)NULL); + fatal(STDERR_FILENO, _PATH_LOGIN, 1); + /*NOTREACHED*/ + } +#ifdef CRYPT +#ifdef KERBEROS + /* + * If encrypted, don't turn on NBIO or the des read/write + * routines will croak. + */ + + if (!doencrypt) +#endif +#endif + ioctl(f, FIONBIO, &on); + ioctl(master, FIONBIO, &on); + ioctl(master, TIOCPKT, &on); + signal(SIGCHLD, cleanup); + protocol(f, master); + signal(SIGCHLD, SIG_IGN); + cleanup(0); +} + +char magic[2] = { 0377, 0377 }; +char oobdata[] = {TIOCPKT_WINDOW}; + +/* + * Handle a "control" request (signaled by magic being present) + * in the data stream. For now, we are only willing to handle + * window size changes. + */ +int +control(pty, cp, n) + int pty; + char *cp; + int n; +{ + struct winsize w; + + if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's') + return (0); + oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */ + bcopy(cp+4, (char *)&w, sizeof(w)); + w.ws_row = ntohs(w.ws_row); + w.ws_col = ntohs(w.ws_col); + w.ws_xpixel = ntohs(w.ws_xpixel); + w.ws_ypixel = ntohs(w.ws_ypixel); + (void)ioctl(pty, TIOCSWINSZ, &w); + return (4+sizeof (w)); +} + +/* + * rlogin "protocol" machine. + */ +void +protocol(f, p) + register int f, p; +{ + char pibuf[1024+1], fibuf[1024], *pbp, *fbp; + register pcc = 0, fcc = 0; + int cc, nfd, n; + char cntl; + + /* + * Must ignore SIGTTOU, otherwise we'll stop + * when we try and set slave pty's window shape + * (our controlling tty is the master pty). + */ + (void) signal(SIGTTOU, SIG_IGN); + send(f, oobdata, 1, MSG_OOB); /* indicate new rlogin */ + if (f > p) + nfd = f + 1; + else + nfd = p + 1; + if (nfd > FD_SETSIZE) { + syslog(LOG_ERR, "select mask too small, increase FD_SETSIZE"); + fatal(f, "internal error (select mask too small)", 0); + } + for (;;) { + fd_set ibits, obits, ebits, *omask; + + FD_ZERO(&ebits); + FD_ZERO(&ibits); + FD_ZERO(&obits); + omask = (fd_set *)NULL; + if (fcc) { + FD_SET(p, &obits); + omask = &obits; + } else + FD_SET(f, &ibits); + if (pcc >= 0) + if (pcc) { + FD_SET(f, &obits); + omask = &obits; + } else + FD_SET(p, &ibits); + FD_SET(p, &ebits); + if ((n = select(nfd, &ibits, omask, &ebits, 0)) < 0) { + if (errno == EINTR) + continue; + fatal(f, "select", 1); + } + if (n == 0) { + /* shouldn't happen... */ + sleep(5); + continue; + } +#define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP)) + if (FD_ISSET(p, &ebits)) { + cc = read(p, &cntl, 1); + if (cc == 1 && pkcontrol(cntl)) { + cntl |= oobdata[0]; + send(f, &cntl, 1, MSG_OOB); + if (cntl & TIOCPKT_FLUSHWRITE) { + pcc = 0; + FD_CLR(p, &ibits); + } + } + } + if (FD_ISSET(f, &ibits)) { +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) + fcc = des_read(f, fibuf, sizeof(fibuf)); + else +#endif +#endif + fcc = read(f, fibuf, sizeof(fibuf)); + if (fcc < 0 && errno == EWOULDBLOCK) + fcc = 0; + else { + register char *cp; + int left, n; + + if (fcc <= 0) + break; + fbp = fibuf; + + top: + for (cp = fibuf; cp < fibuf+fcc-1; cp++) + if (cp[0] == magic[0] && + cp[1] == magic[1]) { + left = fcc - (cp-fibuf); + n = control(p, cp, left); + if (n) { + left -= n; + if (left > 0) + bcopy(cp+n, cp, left); + fcc -= n; + goto top; /* n^2 */ + } + } + FD_SET(p, &obits); /* try write */ + } + } + + if (FD_ISSET(p, &obits) && fcc > 0) { + cc = write(p, fbp, fcc); + if (cc > 0) { + fcc -= cc; + fbp += cc; + } + } + + if (FD_ISSET(p, &ibits)) { + pcc = read(p, pibuf, sizeof (pibuf)); + pbp = pibuf; + if (pcc < 0 && errno == EWOULDBLOCK) + pcc = 0; + else if (pcc <= 0) + break; + else if (pibuf[0] == 0) { + pbp++, pcc--; +#ifdef CRYPT +#ifdef KERBEROS + if (!doencrypt) +#endif +#endif + FD_SET(f, &obits); /* try write */ + } else { + if (pkcontrol(pibuf[0])) { + pibuf[0] |= oobdata[0]; + send(f, &pibuf[0], 1, MSG_OOB); + } + pcc = 0; + } + } + if ((FD_ISSET(f, &obits)) && pcc > 0) { +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) + cc = des_write(f, pbp, pcc); + else +#endif +#endif + cc = write(f, pbp, pcc); + if (cc < 0 && errno == EWOULDBLOCK) { + /* + * This happens when we try write after read + * from p, but some old kernels balk at large + * writes even when select returns true. + */ + if (!FD_ISSET(p, &ibits)) + sleep(5); + continue; + } + if (cc > 0) { + pcc -= cc; + pbp += cc; + } + } + } +} + +void +cleanup(signo) + int signo; +{ + char *p; + + p = line + sizeof(_PATH_DEV) - 1; + if (logout(p)) + logwtmp(p, "", ""); + (void)chmod(line, 0666); + (void)chown(line, 0, 0); + *p = 'p'; + (void)chmod(line, 0666); + (void)chown(line, 0, 0); + shutdown(netf, 2); + exit(1); +} + +void +fatal(f, msg, syserr) + int f; + char *msg; + int syserr; +{ + int len; + char buf[BUFSIZ], *bp = buf; + + /* + * Prepend binary one to message if we haven't sent + * the magic null as confirmation. + */ + if (!confirmed) + *bp++ = '\01'; /* error indicator */ + if (syserr) + len = sprintf(bp, "rlogind: %s: %s.\r\n", + msg, strerror(errno)); + else + len = sprintf(bp, "rlogind: %s.\r\n", msg); + (void) write(f, buf, bp + len - buf); + exit(1); +} + +int +do_rlogin(dest) + struct sockaddr_in *dest; +{ + getstr(rusername, sizeof(rusername), "remuser too long"); + getstr(lusername, sizeof(lusername), "locuser too long"); + getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long"); + + pwd = getpwnam(lusername); + if (pwd == NULL) + return (-1); + /* XXX why don't we syslog() failure? */ + return (iruserok(dest->sin_addr.s_addr, pwd->pw_uid == 0, + rusername, lusername)); +} + +void +getstr(buf, cnt, errmsg) + char *buf; + int cnt; + char *errmsg; +{ + char c; + + do { + if (read(0, &c, 1) != 1) + exit(1); + if (--cnt < 0) + fatal(STDOUT_FILENO, errmsg, 0); + *buf++ = c; + } while (c != 0); +} + +extern char **environ; + +void +setup_term(fd) + int fd; +{ + register char *cp = index(term+ENVSIZE, '/'); + char *speed; + struct termios tt; + +#ifndef notyet + tcgetattr(fd, &tt); + if (cp) { + *cp++ = '\0'; + speed = cp; + cp = index(speed, '/'); + if (cp) + *cp++ = '\0'; + cfsetspeed(&tt, atoi(speed)); + } + + tt.c_iflag = TTYDEF_IFLAG; + tt.c_oflag = TTYDEF_OFLAG; + tt.c_lflag = TTYDEF_LFLAG; + tcsetattr(fd, TCSAFLUSH, &tt); +#else + if (cp) { + *cp++ = '\0'; + speed = cp; + cp = index(speed, '/'); + if (cp) + *cp++ = '\0'; + tcgetattr(fd, &tt); + cfsetspeed(&tt, atoi(speed)); + tcsetattr(fd, TCSAFLUSH, &tt); + } +#endif + + env[0] = term; + env[1] = 0; + environ = env; +} + +#ifdef KERBEROS +#define VERSION_SIZE 9 + +/* + * Do the remote kerberos login to the named host with the + * given inet address + * + * Return 0 on valid authorization + * Return -1 on valid authentication, no authorization + * Return >0 for error conditions + */ +int +do_krb_login(dest) + struct sockaddr_in *dest; +{ + int rc; + char instance[INST_SZ], version[VERSION_SIZE]; + long authopts = 0L; /* !mutual */ + struct sockaddr_in faddr; + + kdata = (AUTH_DAT *) auth_buf; + ticket = (KTEXT) tick_buf; + + instance[0] = '*'; + instance[1] = '\0'; + +#ifdef CRYPT + if (doencrypt) { + rc = sizeof(faddr); + if (getsockname(0, (struct sockaddr *)&faddr, &rc)) + return (-1); + authopts = KOPT_DO_MUTUAL; + rc = krb_recvauth( + authopts, 0, + ticket, "rcmd", + instance, dest, &faddr, + kdata, "", schedule, version); + des_set_key(&kdata->session, schedule); + + } else +#endif + rc = krb_recvauth( + authopts, 0, + ticket, "rcmd", + instance, dest, (struct sockaddr_in *) 0, + kdata, "", (bit_64 *) 0, version); + + if (rc != KSUCCESS) + return (rc); + + getstr(lusername, sizeof(lusername), "locuser"); + /* get the "cmd" in the rcmd protocol */ + getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type"); + + pwd = getpwnam(lusername); + if (pwd == NULL) + return (-1); + + /* returns nonzero for no access */ + if (kuserok(kdata, lusername) != 0) + return (-1); + + return (0); + +} +#endif /* KERBEROS */ + +void +usage() +{ +#ifdef KERBEROS + syslog(LOG_ERR, "usage: rlogind [-Daln] [-k | -v]"); +#else + syslog(LOG_ERR, "usage: rlogind [-Daln]"); +#endif +} + +/* + * Check whether host h is in our local domain, + * defined as sharing the last two components of the domain part, + * or the entire domain part if the local domain has only one component. + * If either name is unqualified (contains no '.'), + * assume that the host is local, as it will be + * interpreted as such. + */ +int +local_domain(h) + char *h; +{ + char localhost[MAXHOSTNAMELEN]; + char *p1, *p2; + + localhost[0] = 0; + (void) gethostname(localhost, sizeof(localhost)); + p1 = topdomain(localhost); + p2 = topdomain(h); + if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) + return (1); + return (0); +} + +char * +topdomain(h) + char *h; +{ + register char *p; + char *maybe = NULL; + int dots = 0; + + for (p = h + strlen(h); p >= h; p--) { + if (*p == '.') { + if (++dots == 2) + return (p); + maybe = p; + } + } + return (maybe); +} diff --git a/libexec/rpc.rstatd/Makefile b/libexec/rpc.rstatd/Makefile new file mode 100644 index 0000000..312f7cf --- /dev/null +++ b/libexec/rpc.rstatd/Makefile @@ -0,0 +1,10 @@ +# $Id: Makefile,v 1.2 1993/11/10 03:45:40 smace Exp $ + +PROG = rpc.rstatd +SRCS = rstatd.c rstat_proc.c +MAN8 = rpc.rstatd.8 + +DPADD= ${LIBRPCSVC} ${LIBUTIL} ${LIBKVM} +LDADD= -lrpcsvc -lutil -lkvm + +.include <bsd.prog.mk> diff --git a/libexec/rpc.rstatd/rpc.rstatd.8 b/libexec/rpc.rstatd/rpc.rstatd.8 new file mode 100644 index 0000000..0644bbb --- /dev/null +++ b/libexec/rpc.rstatd/rpc.rstatd.8 @@ -0,0 +1,61 @@ +.\" -*- nroff -*- +.\" +.\" Copyright (c) 1985, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: rpc.rstatd.8,v 1.1 1993/09/16 00:27:45 jtc Exp $ +.\" +.Dd June 7, 1993 +.Dt RPC.RSTATD 8 +.Os BSD 4.3 +.Sh NAME +.Nm rpc.rstatd +.Nd kernel statistics server +.Sh SYNOPSIS +.Nm /usr/libexec/rpc.rstatd +.Sh DESCRIPTION +.Nm rpc.rstatd +is a server which returns performance statistics obtained from the kernel. +These statistics are read using the +.Xr rup 1 +command. +The +.Nm rpc.rstatd +daemon is normally invoked by +.Xr inetd 8 . +.Pp +.Nm rpc.rstatd +uses an RPC protocol defined in +.Pa /usr/include/rpcsvc/rstat.x . +.Sh SEE ALSO +.Xr rup 1 , +.Xr inetd 8 + diff --git a/libexec/rpc.rstatd/rstat_proc.c b/libexec/rpc.rstatd/rstat_proc.c new file mode 100644 index 0000000..613ce51 --- /dev/null +++ b/libexec/rpc.rstatd/rstat_proc.c @@ -0,0 +1,459 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +#ifndef lint +/*static char sccsid[] = "from: @(#)rpc.rstatd.c 1.1 86/09/25 Copyr 1984 Sun Micro";*/ +/*static char sccsid[] = "from: @(#)rstat_proc.c 2.2 88/08/01 4.0 RPCSRC";*/ +static char rcsid[] = "$Id: rstat_proc.c,v 1.2 1994/10/15 13:39:54 davidg Exp $"; +#endif + +/* + * rstat service: built with rstat.x and derived from rpc.rstatd.c + * + * Copyright (c) 1984 by Sun Microsystems, Inc. + */ + +#include <fcntl.h> +#include <kvm.h> +#include <limits.h> +#include <nlist.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include <rpc/rpc.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <sys/param.h> +#ifdef BSD +#include <sys/vmmeter.h> +#include <sys/dkstat.h> +#else +#include <sys/dk.h> +#endif +#include <net/if.h> + +#undef FSHIFT /* Use protocol's shift and scale values */ +#undef FSCALE +#undef if_ipackets +#undef if_ierrors +#undef if_opackets +#undef if_oerrors +#undef if_collisions +#include <rpcsvc/rstat.h> + +struct nlist nl[] = { +#define X_CPTIME 0 + { "_cp_time" }, +#define X_CNT 1 + { "_cnt" }, +#define X_IFNET 2 + { "_ifnet" }, +#define X_DKXFER 3 + { "_dk_xfer" }, +#define X_BOOTTIME 4 + { "_boottime" }, +#define X_HZ 5 + { "_hz" }, +#ifdef vax +#define X_AVENRUN 6 + { "_avenrun" }, +#endif + "", +}; +int firstifnet, numintfs; /* chain of ethernet interfaces */ +int stats_service(); + +extern int from_inetd; +int sincelastreq = 0; /* number of alarms since last request */ +extern int closedown; + +union { + struct stats s1; + struct statsswtch s2; + struct statstime s3; +} stats_all; + +void updatestat(); +static stat_is_init = 0; +static kvm_t *kd; +extern int errno; + +#if defined(BSD) +static int cp_time_xlat[RSTAT_CPUSTATES] = { CP_USER, CP_NICE, CP_SYS, + CP_IDLE }; +static long bsd_cp_time[CPUSTATES]; +#endif + + +#ifndef FSCALE +#define FSCALE (1 << 8) +#endif + +#ifndef BSD +/* + * BSD has the kvm facility for getting info from the + * kernel. If you aren't on BSD, this surfices. + */ +int kmem; + +kvm_read(kd, off, addr, size) + void * kd; + unsigned long off, size; + char *addr; +{ + int len; + if (lseek(kmem, (long)off, 0) == -1) + return(-1); + return(read(kmem, addr, size)); +} + +kvm_nlist(kd, nl) + void * kd; + struct nlist *nl; +{ + int n = nlist("/vmunix", nl); + if (nl[0].n_value == 0) + return(n); + + if ((kmem = open("/dev/kmem", 0)) < 0) + return(-1); + return(0); +} +#endif + +stat_init() +{ + stat_is_init = 1; + setup(); + updatestat(); + (void) signal(SIGALRM, updatestat); + alarm(1); +} + +statstime * +rstatproc_stats_3() +{ + if (! stat_is_init) + stat_init(); + sincelastreq = 0; + return(&stats_all.s3); +} + +statsswtch * +rstatproc_stats_2() +{ + if (! stat_is_init) + stat_init(); + sincelastreq = 0; + return(&stats_all.s2); +} + +stats * +rstatproc_stats_1() +{ + if (! stat_is_init) + stat_init(); + sincelastreq = 0; + return(&stats_all.s1); +} + +u_int * +rstatproc_havedisk_3() +{ + static u_int have; + + if (! stat_is_init) + stat_init(); + sincelastreq = 0; + have = havedisk(); + return(&have); +} + +u_int * +rstatproc_havedisk_2() +{ + return(rstatproc_havedisk_3()); +} + +u_int * +rstatproc_havedisk_1() +{ + return(rstatproc_havedisk_3()); +} + +void +updatestat() +{ + int off, i, hz; + struct vmmeter cnt; + struct ifnet ifnet; + double avrun[3]; + struct timeval tm, btm; + +#ifdef DEBUG + fprintf(stderr, "entering updatestat\n"); +#endif + if (sincelastreq >= closedown) { +#ifdef DEBUG + fprintf(stderr, "about to closedown\n"); +#endif + if (from_inetd) + exit(0); + else { + stat_is_init = 0; + return; + } + } + sincelastreq++; + + if (kvm_read(kd, (long)nl[X_HZ].n_value, (char *)&hz, sizeof hz) != sizeof hz) { + syslog(LOG_ERR, "rstat: can't read hz from kmem\n"); + exit(1); + } +#if defined(BSD) + if (kvm_read(kd, (long)nl[X_CPTIME].n_value, (char *)bsd_cp_time, sizeof(bsd_cp_time)) + != sizeof(bsd_cp_time)) { + syslog(LOG_ERR, "rstat: can't read cp_time from kmem\n"); + exit(1); + } + for(i = 0; i < RSTAT_CPUSTATES ; i++) + stats_all.s1.cp_time[i] = bsd_cp_time[cp_time_xlat[i]]; +#else + if (kvm_read(kd, (long)nl[X_CPTIME].n_value, (char *)stats_all.s1.cp_time, sizeof (stats_all.s1.cp_time)) + != sizeof (stats_all.s1.cp_time)) { + syslog(LOG_ERR, "rstat: can't read cp_time from kmem\n"); + exit(1); + } +#endif +#ifdef vax + if (kvm_read(kd, (long)nl[X_AVENRUN].n_value, (char *)avrun, sizeof (avrun)) != sizeof (avrun)) { + syslog(LOG_ERR, "rstat: can't read avenrun from kmem\n"); + exit(1); + } +#endif +#ifdef BSD + (void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0])); +#endif + stats_all.s2.avenrun[0] = avrun[0] * FSCALE; + stats_all.s2.avenrun[1] = avrun[1] * FSCALE; + stats_all.s2.avenrun[2] = avrun[2] * FSCALE; + if (kvm_read(kd, (long)nl[X_BOOTTIME].n_value, (char *)&btm, sizeof (stats_all.s2.boottime)) + != sizeof (stats_all.s2.boottime)) { + syslog(LOG_ERR, "rstat: can't read boottime from kmem\n"); + exit(1); + } + stats_all.s2.boottime.tv_sec = btm.tv_sec; + stats_all.s2.boottime.tv_usec = btm.tv_usec; + + +#ifdef DEBUG + fprintf(stderr, "%d %d %d %d\n", stats_all.s1.cp_time[0], + stats_all.s1.cp_time[1], stats_all.s1.cp_time[2], stats_all.s1.cp_time[3]); +#endif + + if (kvm_read(kd, (long)nl[X_CNT].n_value, (char *)&cnt, sizeof cnt) != sizeof cnt) { + syslog(LOG_ERR, "rstat: can't read cnt from kmem\n"); + exit(1); + } + stats_all.s1.v_pgpgin = cnt.v_vnodepgsin; + stats_all.s1.v_pgpgout = cnt.v_vnodepgsout; + stats_all.s1.v_pswpin = cnt.v_swappgsin; + stats_all.s1.v_pswpout = cnt.v_swappgsout; + stats_all.s1.v_intr = cnt.v_intr; + gettimeofday(&tm, (struct timezone *) 0); + stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) + + hz*(tm.tv_usec - btm.tv_usec)/1000000; + stats_all.s2.v_swtch = cnt.v_swtch; + + if (kvm_read(kd, (long)nl[X_DKXFER].n_value, (char *)stats_all.s1.dk_xfer, sizeof (stats_all.s1.dk_xfer)) + != sizeof (stats_all.s1.dk_xfer)) { + syslog(LOG_ERR, "rstat: can't read dk_xfer from kmem\n"); + exit(1); + } + + stats_all.s1.if_ipackets = 0; + stats_all.s1.if_opackets = 0; + stats_all.s1.if_ierrors = 0; + stats_all.s1.if_oerrors = 0; + stats_all.s1.if_collisions = 0; + for (off = firstifnet, i = 0; off && i < numintfs; i++) { + if (kvm_read(kd, off, (char *)&ifnet, sizeof ifnet) != sizeof ifnet) { + syslog(LOG_ERR, "rstat: can't read ifnet from kmem\n"); + exit(1); + } + stats_all.s1.if_ipackets += ifnet.if_data.ifi_ipackets; + stats_all.s1.if_opackets += ifnet.if_data.ifi_opackets; + stats_all.s1.if_ierrors += ifnet.if_data.ifi_ierrors; + stats_all.s1.if_oerrors += ifnet.if_data.ifi_oerrors; + stats_all.s1.if_collisions += ifnet.if_data.ifi_collisions; + off = (int) ifnet.if_next; + } + gettimeofday((struct timeval *)&stats_all.s3.curtime, + (struct timezone *) 0); + alarm(1); +} + +setup() +{ + struct ifnet ifnet; + int off; + char errbuf[_POSIX2_LINE_MAX]; + + int en; + + if ((kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf)) == NULL) { + syslog(LOG_ERR, "rpc.rstatd, %s", errbuf); + exit(1); + } + + if ((en = kvm_nlist(kd, nl)) != 0) { + syslog(LOG_ERR, "rstatd: Can't get namelist. %d", en); + exit (1); + } + + if (kvm_read(kd, (long)nl[X_IFNET].n_value, &firstifnet, + sizeof(int)) != sizeof(int)) { + syslog(LOG_ERR, "rstat: can't read firstifnet from kmem\n"); + exit(1); + } + + numintfs = 0; + for (off = firstifnet; off;) { + if (kvm_read(kd, off, (char *)&ifnet, sizeof ifnet) != sizeof ifnet) { + syslog(LOG_ERR, "rstat: can't read ifnet from kmem\n"); + exit(1); + } + numintfs++; + off = (int) ifnet.if_next; + } +} + +/* + * returns true if have a disk + */ +havedisk() +{ + int i, cnt; + long xfer[DK_NDRIVE]; + + if (kvm_nlist(kd, nl) != 0) { + syslog(LOG_ERR, "rstatd: Can't get namelist.(d)"); + exit (1); + } + + if (kvm_read(kd, (long)nl[X_DKXFER].n_value, (char *)xfer, sizeof xfer)!= sizeof xfer) { + syslog(LOG_ERR, "rstat: can't read kmem\n"); + exit(1); + } + cnt = 0; + for (i=0; i < DK_NDRIVE; i++) + cnt += xfer[i]; + return (cnt != 0); +} + +void +rstat_service(rqstp, transp) + struct svc_req *rqstp; + SVCXPRT *transp; +{ + union { + int fill; + } argument; + char *result; + bool_t (*xdr_argument)(), (*xdr_result)(); + char *(*local)(); + + switch (rqstp->rq_proc) { + case NULLPROC: + (void)svc_sendreply(transp, xdr_void, (char *)NULL); + goto leave; + + case RSTATPROC_STATS: + xdr_argument = xdr_void; + xdr_result = xdr_statstime; + switch (rqstp->rq_vers) { + case RSTATVERS_ORIG: + local = (char *(*)()) rstatproc_stats_1; + break; + case RSTATVERS_SWTCH: + local = (char *(*)()) rstatproc_stats_2; + break; + case RSTATVERS_TIME: + local = (char *(*)()) rstatproc_stats_3; + break; + default: + svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); + goto leave; + /*NOTREACHED*/ + } + break; + + case RSTATPROC_HAVEDISK: + xdr_argument = xdr_void; + xdr_result = xdr_u_int; + switch (rqstp->rq_vers) { + case RSTATVERS_ORIG: + local = (char *(*)()) rstatproc_havedisk_1; + break; + case RSTATVERS_SWTCH: + local = (char *(*)()) rstatproc_havedisk_2; + break; + case RSTATVERS_TIME: + local = (char *(*)()) rstatproc_havedisk_3; + break; + default: + svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME); + goto leave; + /*NOTREACHED*/ + } + break; + + default: + svcerr_noproc(transp); + goto leave; + } + bzero((char *)&argument, sizeof(argument)); + if (!svc_getargs(transp, xdr_argument, &argument)) { + svcerr_decode(transp); + goto leave; + } + result = (*local)(&argument, rqstp); + if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_argument, &argument)) { + (void)fprintf(stderr, "unable to free arguments\n"); + exit(1); + } +leave: + if (from_inetd) + exit(0); +} diff --git a/libexec/rpc.rstatd/rstatd.c b/libexec/rpc.rstatd/rstatd.c new file mode 100644 index 0000000..972974c --- /dev/null +++ b/libexec/rpc.rstatd/rstatd.c @@ -0,0 +1,118 @@ +/*- + * Copyright (c) 1993, John Brezak + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$Id: rstatd.c,v 1.2 1994/11/18 22:31:05 ats Exp $"; +#endif /* not lint */ + +#include <stdio.h> +#include <rpc/rpc.h> +#include <signal.h> +#include <syslog.h> +#include <rpcsvc/rstat.h> + +extern void rstat_service(); + +int from_inetd = 1; /* started from inetd ? */ +int closedown = 20; /* how long to wait before going dormant */ + +void +cleanup() +{ + (void) pmap_unset(RSTATPROG, RSTATVERS_TIME); + (void) pmap_unset(RSTATPROG, RSTATVERS_SWTCH); + (void) pmap_unset(RSTATPROG, RSTATVERS_ORIG); + exit(0); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + SVCXPRT *transp; + int sock = 0; + int proto = 0; + struct sockaddr_in from; + int fromlen; + + if (argc == 2) + closedown = atoi(argv[1]); + if (closedown <= 0) + closedown = 20; + + /* + * See if inetd started us + */ + fromlen = sizeof(from); + if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { + from_inetd = 0; + sock = RPC_ANYSOCK; + proto = IPPROTO_UDP; + } + + if (!from_inetd) { + daemon(0, 0); + + (void)pmap_unset(RSTATPROG, RSTATVERS_TIME); + (void)pmap_unset(RSTATPROG, RSTATVERS_SWTCH); + (void)pmap_unset(RSTATPROG, RSTATVERS_ORIG); + + (void) signal(SIGINT, cleanup); + (void) signal(SIGTERM, cleanup); + (void) signal(SIGHUP, cleanup); + } + + openlog("rpc.rstatd", LOG_CONS|LOG_PID, LOG_DAEMON); + + transp = svcudp_create(sock); + if (transp == NULL) { + syslog(LOG_ERR, "cannot create udp service."); + exit(1); + } + if (!svc_register(transp, RSTATPROG, RSTATVERS_TIME, rstat_service, proto)) { + syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_TIME, udp)."); + exit(1); + } + if (!svc_register(transp, RSTATPROG, RSTATVERS_SWTCH, rstat_service, proto)) { + syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_SWTCH, udp)."); + exit(1); + } + if (!svc_register(transp, RSTATPROG, RSTATVERS_ORIG, rstat_service, proto)) { + syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_ORIG, udp)."); + exit(1); + } + + svc_run(); + syslog(LOG_ERR, "svc_run returned"); + exit(1); +} diff --git a/libexec/rpc.rusersd/Makefile b/libexec/rpc.rusersd/Makefile new file mode 100644 index 0000000..1c68939 --- /dev/null +++ b/libexec/rpc.rusersd/Makefile @@ -0,0 +1,16 @@ +# $Id: Makefile,v 1.2 1993/11/10 03:46:04 smace Exp $ + +PROG = rpc.rusersd +SRCS = rusersd.c rusers_proc.c +MAN8 = rpc.rusersd.8 + +DPADD= ${LIBRPCSVC} ${LIBUTIL} +LDADD= -lrpcsvc -lutil + +.if exists(/usr/include/X11/extensions/xidle.h) +CFLAGS+= -DXIDLE +LDADD+= -L/usr/X386/lib -lXext -lX11 +.endif + + +.include <bsd.prog.mk> diff --git a/libexec/rpc.rusersd/rpc.rusersd.8 b/libexec/rpc.rusersd/rpc.rusersd.8 new file mode 100644 index 0000000..15947ac --- /dev/null +++ b/libexec/rpc.rusersd/rpc.rusersd.8 @@ -0,0 +1,64 @@ +.\" -*- nroff -*- +.\" +.\" Copyright (c) 1985, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: rpc.rusersd.8,v 1.1 1993/09/16 00:34:43 jtc Exp $ +.\" +.Dd June 7, 1993 +.Dt RPC.RUSERSD 8 +.Os BSD 4.3 +.Sh NAME +.Nm rpc.rusersd +.Nd logged in users server +.Sh SYNOPSIS +.Nm /usr/libexec/rpc.rusersd +.Sh DESCRIPTION +.Nm rpc.rusersd +is a server which returns information about users +currently logged in to the system. +.Pp +The currently logged in users are queried using the +.Xr rusers 1 +command. +The +.Nm rpc.rusersd +daemon is normally invoked by +.Xr inetd 8 . +.Pp +.Nm rpc.rusersd +uses an RPC protocol defined in +.Pa /usr/include/rpcsvc/rnusers.x . +.Sh SEE ALSO +.Xr rusers 1 , +.Xr who 1 , +.Xr w 1 , +.Xr inetd 8 diff --git a/libexec/rpc.rusersd/rusers_proc.c b/libexec/rpc.rusersd/rusers_proc.c new file mode 100644 index 0000000..d472673 --- /dev/null +++ b/libexec/rpc.rusersd/rusers_proc.c @@ -0,0 +1,390 @@ +/*- + * Copyright (c) 1993, John Brezak + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$Id: rusers_proc.c,v 1.3 1994/11/18 23:36:18 ats Exp $"; +#endif /* not lint */ + +#include <signal.h> +#include <sys/types.h> +#include <sys/time.h> +#include <utmp.h> +#include <stdio.h> +#include <syslog.h> +#include <rpc/rpc.h> +#include <sys/socket.h> +#include <sys/param.h> +#include <sys/stat.h> +#ifdef XIDLE +#include <setjmp.h> +#include <X11/Xlib.h> +#include <X11/extensions/xidle.h> +#endif +#define utmp rutmp +#include <rpcsvc/rnusers.h> +#undef utmp + +#define IGNOREUSER "sleeper" + +#ifdef OSF +#define _PATH_UTMP UTMP_FILE +#endif + +#ifndef _PATH_UTMP +#define _PATH_UTMP "/etc/utmp" +#endif + +#ifndef _PATH_DEV +#define _PATH_DEV "/dev" +#endif + +#ifndef UT_LINESIZE +#define UT_LINESIZE sizeof(((struct utmp *)0)->ut_line) +#endif +#ifndef UT_NAMESIZE +#define UT_NAMESIZE sizeof(((struct utmp *)0)->ut_name) +#endif +#ifndef UT_HOSTSIZE +#define UT_HOSTSIZE sizeof(((struct utmp *)0)->ut_host) +#endif + +typedef char ut_line_t[UT_LINESIZE+1]; +typedef char ut_name_t[UT_NAMESIZE+1]; +typedef char ut_host_t[UT_HOSTSIZE+1]; + +utmpidle utmp_idle[MAXUSERS]; +rutmp old_utmp[MAXUSERS]; +ut_line_t line[MAXUSERS]; +ut_name_t name[MAXUSERS]; +ut_host_t host[MAXUSERS]; + +extern int from_inetd; + +FILE *ufp; + +#ifdef XIDLE +Display *dpy; + +static jmp_buf openAbort; + +static void +abortOpen () +{ + longjmp (openAbort, 1); +} + +XqueryIdle(char *display) +{ + int first_event, first_error; + Time IdleTime; + + (void) signal (SIGALRM, abortOpen); + (void) alarm ((unsigned) 10); + if (!setjmp (openAbort)) { + if (!(dpy= XOpenDisplay(display))) { + syslog(LOG_ERR, "Cannot open display %s", display); + return(-1); + } + if (XidleQueryExtension(dpy, &first_event, &first_error)) { + if (!XGetIdleTime(dpy, &IdleTime)) { + syslog(LOG_ERR, "%s: Unable to get idle time.", display); + return(-1); + } + } + else { + syslog(LOG_ERR, "%s: Xidle extension not loaded.", display); + return(-1); + } + XCloseDisplay(dpy); + } + else { + syslog(LOG_ERR, "%s: Server grabbed for over 10 seconds.", display); + return(-1); + } + (void) signal (SIGALRM, SIG_DFL); + (void) alarm ((unsigned) 0); + + IdleTime /= 1000; + return((IdleTime + 30) / 60); +} +#endif + +static u_int +getidle(char *tty, char *display) +{ + struct stat st; + char devname[PATH_MAX]; + time_t now; + u_long idle; + + /* + * If this is an X terminal or console, then try the + * XIdle extension + */ +#ifdef XIDLE + if (display && *display && (idle = XqueryIdle(display)) >= 0) + return(idle); +#endif + idle = 0; + if (*tty == 'X') { + u_long kbd_idle, mouse_idle; +#if !defined(__FreeBSD__) + kbd_idle = getidle("kbd", NULL); +#else + kbd_idle = getidle("vga", NULL); +#endif + mouse_idle = getidle("mouse", NULL); + idle = (kbd_idle < mouse_idle)?kbd_idle:mouse_idle; + } + else { + sprintf(devname, "%s/%s", _PATH_DEV, tty); + if (stat(devname, &st) < 0) { +#ifdef DEBUG + printf("%s: %s\n", devname, strerror(errno)); +#endif + return(-1); + } + time(&now); +#ifdef DEBUG + printf("%s: now=%d atime=%d\n", devname, now, + st.st_atime); +#endif + idle = now - st.st_atime; + idle = (idle + 30) / 60; /* secs->mins */ + } + if (idle < 0) idle = 0; + + return(idle); +} + +static utmpidlearr * +do_names_2(int all) +{ + static utmpidlearr ut; + struct utmp usr; + int nusers = 0; + + bzero((char *)&ut, sizeof(ut)); + ut.utmpidlearr_val = &utmp_idle[0]; + + ufp = fopen(_PATH_UTMP, "r"); + if (!ufp) { + syslog(LOG_ERR, "%m"); + return(&ut); + } + + /* only entries with both name and line fields */ + while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1 && + nusers < MAXUSERS) + if (*usr.ut_name && *usr.ut_line && + strncmp(usr.ut_name, IGNOREUSER, + sizeof(usr.ut_name)) +#ifdef OSF + && usr.ut_type == USER_PROCESS +#endif + ) { + utmp_idle[nusers].ui_utmp.ut_time = + usr.ut_time; + utmp_idle[nusers].ui_idle = + getidle(usr.ut_line, usr.ut_host); + utmp_idle[nusers].ui_utmp.ut_line = line[nusers]; + strncpy(line[nusers], usr.ut_line, UT_LINESIZE); + utmp_idle[nusers].ui_utmp.ut_name = name[nusers]; + strncpy(name[nusers], usr.ut_name, UT_NAMESIZE); + utmp_idle[nusers].ui_utmp.ut_host = host[nusers]; + strncpy(host[nusers], usr.ut_host, UT_HOSTSIZE); + + /* Make sure entries are NUL terminated */ + line[nusers][UT_LINESIZE] = + name[nusers][UT_NAMESIZE] = + host[nusers][UT_HOSTSIZE] = '\0'; + nusers++; + } + + ut.utmpidlearr_len = nusers; + fclose(ufp); + return(&ut); +} + +int * +rusers_num() +{ + static int num_users = 0; + struct utmp usr; + + ufp = fopen(_PATH_UTMP, "r"); + if (!ufp) { + syslog(LOG_ERR, "%m"); + return(0); + } + + /* only entries with both name and line fields */ + while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1) + if (*usr.ut_name && *usr.ut_line && + strncmp(usr.ut_name, IGNOREUSER, + sizeof(usr.ut_name)) +#ifdef OSF + && usr.ut_type == USER_PROCESS +#endif + ) { + num_users++; + } + + fclose(ufp); + return(&num_users); +} + +static utmparr * +do_names_1(int all) +{ + utmpidlearr *utidle; + static utmparr ut; + int i; + + bzero((char *)&ut, sizeof(ut)); + + utidle = do_names_2(all); + if (utidle) { + ut.utmparr_len = utidle->utmpidlearr_len; + ut.utmparr_val = &old_utmp[0]; + for (i = 0; i < ut.utmparr_len; i++) + bcopy(&utmp_idle[i].ui_utmp, &old_utmp[i], + sizeof(old_utmp[0])); + + } + + return(&ut); +} + +utmpidlearr * +rusersproc_names_2() +{ + return(do_names_2(0)); +} + +utmpidlearr * +rusersproc_allnames_2() +{ + return(do_names_2(1)); +} + +utmparr * +rusersproc_names_1() +{ + return(do_names_1(0)); +} + +utmparr * +rusersproc_allnames_1() +{ + return(do_names_1(1)); +} + +void +rusers_service(rqstp, transp) + struct svc_req *rqstp; + SVCXPRT *transp; +{ + union { + int fill; + } argument; + char *result; + bool_t (*xdr_argument)(), (*xdr_result)(); + char *(*local)(); + + switch (rqstp->rq_proc) { + case NULLPROC: + (void)svc_sendreply(transp, xdr_void, (char *)NULL); + goto leave; + + case RUSERSPROC_NUM: + xdr_argument = xdr_void; + xdr_result = xdr_int; + local = (char *(*)()) rusers_num; + break; + + case RUSERSPROC_NAMES: + xdr_argument = xdr_void; + xdr_result = xdr_utmpidlearr; + switch (rqstp->rq_vers) { + case RUSERSVERS_ORIG: + local = (char *(*)()) rusersproc_names_1; + break; + case RUSERSVERS_IDLE: + local = (char *(*)()) rusersproc_names_2; + break; + default: + svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE); + goto leave; + /*NOTREACHED*/ + } + break; + + case RUSERSPROC_ALLNAMES: + xdr_argument = xdr_void; + xdr_result = xdr_utmpidlearr; + switch (rqstp->rq_vers) { + case RUSERSVERS_ORIG: + local = (char *(*)()) rusersproc_allnames_1; + break; + case RUSERSVERS_IDLE: + local = (char *(*)()) rusersproc_allnames_2; + break; + default: + svcerr_progvers(transp, RUSERSVERS_ORIG, RUSERSVERS_IDLE); + goto leave; + /*NOTREACHED*/ + } + break; + + default: + svcerr_noproc(transp); + goto leave; + } + bzero((char *)&argument, sizeof(argument)); + if (!svc_getargs(transp, xdr_argument, &argument)) { + svcerr_decode(transp); + goto leave; + } + result = (*local)(&argument, rqstp); + if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_argument, &argument)) { + (void)fprintf(stderr, "unable to free arguments\n"); + exit(1); + } +leave: + if (from_inetd) + exit(0); +} diff --git a/libexec/rpc.rusersd/rusersd.c b/libexec/rpc.rusersd/rusersd.c new file mode 100644 index 0000000..0182306 --- /dev/null +++ b/libexec/rpc.rusersd/rusersd.c @@ -0,0 +1,109 @@ +/*- + * Copyright (c) 1993, John Brezak + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$Id: rusersd.c,v 1.2 1994/11/18 22:40:11 ats Exp $"; +#endif /* not lint */ + +#include <stdio.h> +#include <rpc/rpc.h> +#include <signal.h> +#include <syslog.h> +#define utmp rutmp +#include <rpcsvc/rnusers.h> +#undef utmp + +extern void rusers_service(); + +int from_inetd = 1; + +void +cleanup() +{ + (void) pmap_unset(RUSERSPROG, RUSERSVERS_IDLE); + (void) pmap_unset(RUSERSPROG, RUSERSVERS_ORIG); + exit(0); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + SVCXPRT *transp; + int sock = 0; + int proto = 0; + struct sockaddr_in from; + int fromlen; + + /* + * See if inetd started us + */ + fromlen = sizeof(from); + if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { + from_inetd = 0; + sock = RPC_ANYSOCK; + proto = IPPROTO_UDP; + } + + if (!from_inetd) { + daemon(0, 0); + + (void) pmap_unset(RUSERSPROG, RUSERSVERS_IDLE); + (void) pmap_unset(RUSERSPROG, RUSERSVERS_ORIG); + + (void) signal(SIGINT, cleanup); + (void) signal(SIGTERM, cleanup); + (void) signal(SIGHUP, cleanup); + } + + openlog("rpc.rusersd", LOG_CONS|LOG_PID, LOG_DAEMON); + + transp = svcudp_create(sock); + if (transp == NULL) { + syslog(LOG_ERR, "cannot create udp service."); + exit(1); + } + if (!svc_register(transp, RUSERSPROG, RUSERSVERS_IDLE, rusers_service, proto)) { + syslog(LOG_ERR, "unable to register (RUSERSPROG, RUSERSVERS_IDLE, %s).", proto?"udp":"(inetd)"); + exit(1); + } + + if (!svc_register(transp, RUSERSPROG, RUSERSVERS_ORIG, rusers_service, proto)) { + syslog(LOG_ERR, "unable to register (RUSERSPROG, RUSERSVERS_ORIG, %s).", proto?"udp":"(inetd)"); + exit(1); + } + + svc_run(); + syslog(LOG_ERR, "svc_run returned"); + exit(1); +} diff --git a/libexec/rpc.rwalld/Makefile b/libexec/rpc.rwalld/Makefile new file mode 100644 index 0000000..5d7b492 --- /dev/null +++ b/libexec/rpc.rwalld/Makefile @@ -0,0 +1,10 @@ +# $Id: Makefile,v 1.2 1993/11/10 03:46:23 smace Exp $ + +PROG = rpc.rwalld +SRCS = rwalld.c +MAN8 = rpc.rwalld.8 + +DPADD= ${LIBRPCSVC} ${LIBUTIL} +LDADD= -lrpcsvc -lutil + +.include <bsd.prog.mk> diff --git a/libexec/rpc.rwalld/rpc.rwalld.8 b/libexec/rpc.rwalld/rpc.rwalld.8 new file mode 100644 index 0000000..eadadf4 --- /dev/null +++ b/libexec/rpc.rwalld/rpc.rwalld.8 @@ -0,0 +1,67 @@ +.\" -*- nroff -*- +.\" +.\" Copyright (c) 1985, 1991 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: rpc.rwalld.8,v 1.1 1993/09/16 00:36:43 jtc Exp $ +.\" +.Dd June 7, 1993 +.Dt RPC.RWALLD 8 +.Os BSD 4.3 +.Sh NAME +.Nm rpc.rwalld +.Nd write messages to users currently logged in server +.Sh SYNOPSIS +.Nm /usr/libexec/rpc.rwalld +.Sh DESCRIPTION +.Nm rpc.rwalld +is a server which will send a message to users +currently logged in to the system. This server +invokes the +.Xr wall 1 +command to actually write the messages to the +system. +.Pp +Messages are sent to this server by the +.Xr rwall 1 +command. +The +.Nm rpc.rwalld +daemon is normally invoked by +.Xr inetd 8 . +.Pp +.Nm rpc.rwalld +uses an RPC protocol defined in +.Pa /usr/include/rpcsvc/rwall.x . +.Sh SEE ALSO +.Xr rwall 1 , +.Xr wall 1 , +.Xr inetd 8 diff --git a/libexec/rpc.rwalld/rwalld.c b/libexec/rpc.rwalld/rwalld.c new file mode 100644 index 0000000..adb6c3f --- /dev/null +++ b/libexec/rpc.rwalld/rwalld.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 1993 Christopher G. Demetriou + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$Id: rwalld.c,v 1.2 1994/11/18 22:50:22 ats Exp $"; +#endif /* not lint */ + +#include <unistd.h> +#include <sys/types.h> +#include <pwd.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/socket.h> +#include <signal.h> +#include <sys/wait.h> +#include <rpc/rpc.h> +#include <rpcsvc/rwall.h> + +#ifdef OSF +#define WALL_CMD "/usr/sbin/wall" +#else +#define WALL_CMD "/usr/bin/wall -n" +#endif + +void wallprog_1(); +void possess(); +void killkids(); + +int nodaemon = 0; +int from_inetd = 1; + +main(argc, argv) + int argc; + char *argv[]; +{ + SVCXPRT *transp; + int s, salen; + struct sockaddr_in sa; + int sock = 0; + int proto = 0; + + if (argc == 2 && !strcmp(argv[1], "-n")) + nodaemon = 1; + if (argc != 1 && !nodaemon) { + printf("usage: %s [-n]\n", argv[0]); + exit(1); + } + + if (geteuid() == 0) { + struct passwd *pep = getpwnam("nobody"); + if (pep) + setuid(pep->pw_uid); + else + setuid(getuid()); + } + + /* + * See if inetd started us + */ + salen = sizeof(sa); + if (getsockname(0, (struct sockaddr *)&sa, &salen) < 0) { + from_inetd = 0; + sock = RPC_ANYSOCK; + proto = IPPROTO_UDP; + } + + if (!from_inetd) { + if (!nodaemon) + possess(); + + (void)pmap_unset(WALLPROG, WALLVERS); + if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + perror("socket"); + exit(1); + } + bzero((char *)&sa, sizeof sa); + if (bind(s, (struct sockaddr *)&sa, sizeof sa) < 0) { + perror("bind"); + exit(1); + } + + salen = sizeof sa; + if (getsockname(s, (struct sockaddr *)&sa, &salen)) { + perror("getsockname"); + exit(1); + } + + pmap_set(WALLPROG, WALLVERS, IPPROTO_UDP, ntohs(sa.sin_port)); + if (dup2(s, 0) < 0) { + perror("dup2"); + exit(1); + } + (void)pmap_unset(WALLPROG, WALLVERS); + } + + (void)signal(SIGCHLD, killkids); + + transp = svcudp_create(sock); + if (transp == NULL) { + (void)fprintf(stderr, "cannot create udp service.\n"); + exit(1); + } + if (!svc_register(transp, WALLPROG, WALLVERS, wallprog_1, proto)) { + (void)fprintf(stderr, "unable to register (WALLPROG, WALLVERS, udp).\n"); + exit(1); + } + svc_run(); + (void)fprintf(stderr, "svc_run returned\n"); + exit(1); + +} + +void possess() +{ + daemon(0, 0); +} + +void killkids() +{ + while(wait4(-1, NULL, WNOHANG, NULL) > 0) + ; +} + +void *wallproc_wall_1(s) + char **s; +{ + /* fork, popen wall with special option, and send the message */ + if (fork() == 0) { + FILE *pfp; + + pfp = popen(WALL_CMD, "w"); + if (pfp != NULL) { + fprintf(pfp, "\007\007%s", *s); + pclose(pfp); + exit(0); + } + } +} + +void +wallprog_1(rqstp, transp) + struct svc_req *rqstp; + SVCXPRT *transp; +{ + union { + char *wallproc_wall_1_arg; + } argument; + char *result; + bool_t (*xdr_argument)(), (*xdr_result)(); + char *(*local)(); + + switch (rqstp->rq_proc) { + case NULLPROC: + (void)svc_sendreply(transp, xdr_void, (char *)NULL); + goto leave; + + case WALLPROC_WALL: + xdr_argument = xdr_wrapstring; + xdr_result = xdr_void; + local = (char *(*)()) wallproc_wall_1; + break; + + default: + svcerr_noproc(transp); + goto leave; + } + bzero((char *)&argument, sizeof(argument)); + if (!svc_getargs(transp, xdr_argument, &argument)) { + svcerr_decode(transp); + goto leave; + } + result = (*local)(&argument, rqstp); + if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_argument, &argument)) { + (void)fprintf(stderr, "unable to free arguments\n"); + exit(1); + } +leave: + if (from_inetd) + exit(0); +} diff --git a/libexec/rshd/Makefile b/libexec/rshd/Makefile new file mode 100644 index 0000000..984a6b7 --- /dev/null +++ b/libexec/rshd/Makefile @@ -0,0 +1,16 @@ +# From: @(#)Makefile 8.1 (Berkeley) 6/4/93 +# $Id: Makefile,v 1.3 1994/09/29 13:06:37 csgr Exp $ + +PROG= rshd +SRCS= rshd.c +MAN8= rshd.8 + +.if exists(${DESTDIR}/usr/lib/libkrb.a) && (defined(MAKE_KERBEROS) \ + || defined(MAKE_EBONES)) +CFLAGS+=-DKERBEROS -DCRYPT +DPADD= ${LIBKRB} ${LIBDES} +LDADD= -lkrb -ldes +DISTRIBUTION= krb +.endif + +.include <bsd.prog.mk> diff --git a/libexec/rshd/rshd.8 b/libexec/rshd/rshd.8 new file mode 100644 index 0000000..82e1991 --- /dev/null +++ b/libexec/rshd/rshd.8 @@ -0,0 +1,209 @@ +.\" Copyright (c) 1983, 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)rshd.8 8.1 (Berkeley) 6/4/93 +.\" +.Dd June 4, 1993 +.Dt RSHD 8 +.Os BSD 4.2 +.Sh NAME +.Nm rshd +.Nd remote shell server +.Sh SYNOPSIS +.Nm rshd +.Op Fl alnL +.Sh DESCRIPTION +The +.Nm rshd +server +is the server for the +.Xr rcmd 3 +routine and, consequently, for the +.Xr rsh 1 +program. The server provides remote execution facilities +with authentication based on privileged port numbers from trusted hosts. +.Pp +The +.Nm rshd +server +listens for service requests at the port indicated in +the ``cmd'' service specification; see +.Xr services 5 . +When a service request is received the following protocol +is initiated: +.Bl -enum +.It +The server checks the client's source port. +If the port is not in the range 512-1023, the server +aborts the connection. +.It +The server reads characters from the socket up +to a null (`\e0') byte. The resultant string is +interpreted as an +.Tn ASCII +number, base 10. +.It +If the number received in step 2 is non-zero, +it is interpreted as the port number of a secondary +stream to be used for the +.Em stderr . +A second connection is then created to the specified +port on the client's machine. The source port of this +second connection is also in the range 512-1023. +.It +The server checks the client's source address +and requests the corresponding host name (see +.Xr gethostbyaddr 3 , +.Xr hosts 5 +and +.Xr named 8 ) . +If the hostname cannot be determined, +the dot-notation representation of the host address is used. +If the hostname is in the same domain as the server (according to +the last two components of the domain name), +or if the +.Fl a +option is given, +the addresses for the hostname are requested, +verifying that the name and address correspond. +If address verification fails, the connection is aborted +with the message, ``Host address mismatch.'' +.It +A null terminated user name of at most 16 characters +is retrieved on the initial socket. This user name +is interpreted as the user identity on the +.Em client Ns 's +machine. +.It +A null terminated user name of at most 16 characters +is retrieved on the initial socket. This user name +is interpreted as a user identity to use on the +.Sy server Ns 's +machine. +.It +A null terminated command to be passed to a +shell is retrieved on the initial socket. The length of +the command is limited by the upper bound on the size of +the system's argument list. +.It +.Nm Rshd +then validates the user using +.Xr ruserok 3 , +which uses the file +.Pa /etc/hosts.equiv +and the +.Pa .rhosts +file found in the user's home directory. The +.Fl l +option prevents +.Xr ruserok 3 +from doing any validation based on the user's ``.rhosts'' file, +unless the user is the superuser. +.It +If the file +.Pa /etc/nologin +exists and the user is not the superuser, +the connection is closed. +.It +A null byte is returned on the initial socket +and the command line is passed to the normal login +shell of the user. The +shell inherits the network connections established +by +.Nm rshd . +.El +.Pp +Transport-level keepalive messages are enabled unless the +.Fl n +option is present. +The use of keepalive messages allows sessions to be timed out +if the client crashes or becomes unreachable. +.Pp +The +.Fl L +option causes all successful accesses to be logged to +.Xr syslogd 8 +as +.Li auth.info +messages. +.Sh DIAGNOSTICS +Except for the last one listed below, +all diagnostic messages +are returned on the initial socket, +after which any network connections are closed. +An error is indicated by a leading byte with a value of +1 (0 is returned in step 10 above upon successful completion +of all the steps prior to the execution of the login shell). +.Bl -tag -width indent +.It Sy Locuser too long. +The name of the user on the client's machine is +longer than 16 characters. +.It Sy Ruser too long. +The name of the user on the remote machine is +longer than 16 characters. +.It Sy Command too long . +The command line passed exceeds the size of the argument +list (as configured into the system). +.It Sy Login incorrect. +No password file entry for the user name existed. +.It Sy Remote directory. +The +.Xr chdir +command to the home directory failed. +.It Sy Permission denied. +The authentication procedure described above failed. +.It Sy Can't make pipe. +The pipe needed for the +.Em stderr , +wasn't created. +.It Sy Can't fork; try again. +A +.Xr fork +by the server failed. +.It Sy <shellname>: ... +The user's login shell could not be started. This message is returned +on the connection associated with the +.Em stderr , +and is not preceded by a flag byte. +.El +.Sh SEE ALSO +.Xr rsh 1 , +.Xr rcmd 3 , +.Xr ruserok 3 +.Sh BUGS +The authentication procedure used here assumes the integrity +of each client machine and the connecting medium. This is +insecure, but is useful in an ``open'' environment. +.Pp +A facility to allow all data exchanges to be encrypted should be +present. +.Pp +A more extensible protocol (such as Telnet) should be used. diff --git a/libexec/rshd/rshd.c b/libexec/rshd/rshd.c new file mode 100644 index 0000000..e9e6d83 --- /dev/null +++ b/libexec/rshd/rshd.c @@ -0,0 +1,785 @@ +/*- + * Copyright (c) 1988, 1989, 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rshd.c 8.2 (Berkeley) 4/6/94"; +#endif /* not lint */ + +/* + * remote shell server: + * [port]\0 + * remuser\0 + * locuser\0 + * command\0 + * data + */ +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +int keepalive = 1; +int check_all; +int log_success; /* If TRUE, log all successful accesses */ +int sent_null; + +void doit __P((struct sockaddr_in *)); +void error __P((const char *, ...)); +void getstr __P((char *, int, char *)); +int local_domain __P((char *)); +char *topdomain __P((char *)); +void usage __P((void)); + +#ifdef KERBEROS +#include <kerberosIV/des.h> +#include <kerberosIV/krb.h> +#define VERSION_SIZE 9 +#define SECURE_MESSAGE "This rsh session is using DES encryption for all transmissions.\r\n" +#define OPTIONS "alnkvxL" +char authbuf[sizeof(AUTH_DAT)]; +char tickbuf[sizeof(KTEXT_ST)]; +int doencrypt, use_kerberos, vacuous; +Key_schedule schedule; +#else +#define OPTIONS "alnL" +#endif + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int __check_rhosts_file; + struct linger linger; + int ch, on = 1, fromlen; + struct sockaddr_in from; + + openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON); + + opterr = 0; + while ((ch = getopt(argc, argv, OPTIONS)) != EOF) + switch (ch) { + case 'a': + check_all = 1; + break; + case 'l': + __check_rhosts_file = 0; + break; + case 'n': + keepalive = 0; + break; +#ifdef KERBEROS + case 'k': + use_kerberos = 1; + break; + + case 'v': + vacuous = 1; + break; + +#ifdef CRYPT + case 'x': + doencrypt = 1; + break; +#endif +#endif + case 'L': + log_success = 1; + break; + case '?': + default: + usage(); + break; + } + + argc -= optind; + argv += optind; + +#ifdef KERBEROS + if (use_kerberos && vacuous) { + syslog(LOG_ERR, "only one of -k and -v allowed"); + exit(2); + } +#ifdef CRYPT + if (doencrypt && !use_kerberos) { + syslog(LOG_ERR, "-k is required for -x"); + exit(2); + } +#endif +#endif + + fromlen = sizeof (from); + if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { + syslog(LOG_ERR, "getpeername: %m"); + _exit(1); + } + if (keepalive && + setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, + sizeof(on)) < 0) + syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); + linger.l_onoff = 1; + linger.l_linger = 60; /* XXX */ + if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger, + sizeof (linger)) < 0) + syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m"); + doit(&from); + /* NOTREACHED */ +} + +char username[20] = "USER="; +char homedir[64] = "HOME="; +char shell[64] = "SHELL="; +char path[100] = "PATH="; +char *envinit[] = + {homedir, shell, path, username, 0}; +char **environ; + +void +doit(fromp) + struct sockaddr_in *fromp; +{ + extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */ + struct hostent *hp; + struct passwd *pwd; + u_short port; + fd_set ready, readfrom; + int cc, nfd, pv[2], pid, s; + int one = 1; + char *hostname, *errorstr, *errorhost; + char *cp, sig, buf[BUFSIZ]; + char cmdbuf[NCARGS+1], locuser[16], remuser[16]; + char remotehost[2 * MAXHOSTNAMELEN + 1]; + +#ifdef KERBEROS + AUTH_DAT *kdata = (AUTH_DAT *) NULL; + KTEXT ticket = (KTEXT) NULL; + char instance[INST_SZ], version[VERSION_SIZE]; + struct sockaddr_in fromaddr; + int rc; + long authopts; + int pv1[2], pv2[2]; + fd_set wready, writeto; + + fromaddr = *fromp; +#endif + + (void) signal(SIGINT, SIG_DFL); + (void) signal(SIGQUIT, SIG_DFL); + (void) signal(SIGTERM, SIG_DFL); +#ifdef DEBUG + { int t = open(_PATH_TTY, 2); + if (t >= 0) { + ioctl(t, TIOCNOTTY, (char *)0); + (void) close(t); + } + } +#endif + fromp->sin_port = ntohs((u_short)fromp->sin_port); + if (fromp->sin_family != AF_INET) { + syslog(LOG_ERR, "malformed \"from\" address (af %d)\n", + fromp->sin_family); + exit(1); + } +#ifdef IP_OPTIONS + { + u_char optbuf[BUFSIZ/3], *cp; + char lbuf[BUFSIZ], *lp; + int optsize = sizeof(optbuf), ipproto; + struct protoent *ip; + + if ((ip = getprotobyname("ip")) != NULL) + ipproto = ip->p_proto; + else + ipproto = IPPROTO_IP; + if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) && + optsize != 0) { + lp = lbuf; + for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) + sprintf(lp, " %2.2x", *cp); + syslog(LOG_NOTICE, + "Connection received from %s using IP options (ignored):%s", + inet_ntoa(fromp->sin_addr), lbuf); + if (setsockopt(0, ipproto, IP_OPTIONS, + (char *)NULL, optsize) != 0) { + syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m"); + exit(1); + } + } + } +#endif + +#ifdef KERBEROS + if (!use_kerberos) +#endif + if (fromp->sin_port >= IPPORT_RESERVED || + fromp->sin_port < IPPORT_RESERVED/2) { + syslog(LOG_NOTICE|LOG_AUTH, + "Connection from %s on illegal port %u", + inet_ntoa(fromp->sin_addr), + fromp->sin_port); + exit(1); + } + + (void) alarm(60); + port = 0; + for (;;) { + char c; + if ((cc = read(STDIN_FILENO, &c, 1)) != 1) { + if (cc < 0) + syslog(LOG_NOTICE, "read: %m"); + shutdown(0, 1+1); + exit(1); + } + if (c== 0) + break; + port = port * 10 + c - '0'; + } + + (void) alarm(0); + if (port != 0) { + int lport = IPPORT_RESERVED - 1; + s = rresvport(&lport); + if (s < 0) { + syslog(LOG_ERR, "can't get stderr port: %m"); + exit(1); + } +#ifdef KERBEROS + if (!use_kerberos) +#endif + if (port >= IPPORT_RESERVED) { + syslog(LOG_ERR, "2nd port not reserved\n"); + exit(1); + } + fromp->sin_port = htons(port); + if (connect(s, (struct sockaddr *)fromp, sizeof (*fromp)) < 0) { + syslog(LOG_INFO, "connect second port %d: %m", port); + exit(1); + } + } + +#ifdef KERBEROS + if (vacuous) { + error("rshd: remote host requires Kerberos authentication\n"); + exit(1); + } +#endif + +#ifdef notdef + /* from inetd, socket is already on 0, 1, 2 */ + dup2(f, 0); + dup2(f, 1); + dup2(f, 2); +#endif + errorstr = NULL; + hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (struct in_addr), + fromp->sin_family); + if (hp) { + /* + * If name returned by gethostbyaddr is in our domain, + * attempt to verify that we haven't been fooled by someone + * in a remote net; look up the name and check that this + * address corresponds to the name. + */ + hostname = hp->h_name; +#ifdef KERBEROS + if (!use_kerberos) +#endif + if (check_all || local_domain(hp->h_name)) { + strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1); + remotehost[sizeof(remotehost) - 1] = 0; + errorhost = remotehost; + hp = gethostbyname(remotehost); + if (hp == NULL) { + syslog(LOG_INFO, + "Couldn't look up address for %s", + remotehost); + errorstr = + "Couldn't look up address for your host (%s)\n"; + hostname = inet_ntoa(fromp->sin_addr); + } else for (; ; hp->h_addr_list++) { + if (hp->h_addr_list[0] == NULL) { + syslog(LOG_NOTICE, + "Host addr %s not listed for host %s", + inet_ntoa(fromp->sin_addr), + hp->h_name); + errorstr = + "Host address mismatch for %s\n"; + hostname = inet_ntoa(fromp->sin_addr); + break; + } + if (!bcmp(hp->h_addr_list[0], + (caddr_t)&fromp->sin_addr, + sizeof(fromp->sin_addr))) { + hostname = hp->h_name; + break; + } + } + } + } else + errorhost = hostname = inet_ntoa(fromp->sin_addr); + +#ifdef KERBEROS + if (use_kerberos) { + kdata = (AUTH_DAT *) authbuf; + ticket = (KTEXT) tickbuf; + authopts = 0L; + strcpy(instance, "*"); + version[VERSION_SIZE - 1] = '\0'; +#ifdef CRYPT + if (doencrypt) { + struct sockaddr_in local_addr; + rc = sizeof(local_addr); + if (getsockname(0, (struct sockaddr *)&local_addr, + &rc) < 0) { + syslog(LOG_ERR, "getsockname: %m"); + error("rlogind: getsockname: %m"); + exit(1); + } + authopts = KOPT_DO_MUTUAL; + rc = krb_recvauth(authopts, 0, ticket, + "rcmd", instance, &fromaddr, + &local_addr, kdata, "", schedule, + version); + des_set_key(&kdata->session, schedule); + } else +#endif + rc = krb_recvauth(authopts, 0, ticket, "rcmd", + instance, &fromaddr, + (struct sockaddr_in *) 0, + kdata, "", (bit_64 *) 0, version); + if (rc != KSUCCESS) { + error("Kerberos authentication failure: %s\n", + krb_err_txt[rc]); + exit(1); + } + } else +#endif + getstr(remuser, sizeof(remuser), "remuser"); + + getstr(locuser, sizeof(locuser), "locuser"); + getstr(cmdbuf, sizeof(cmdbuf), "command"); + setpwent(); + pwd = getpwnam(locuser); + if (pwd == NULL) { + syslog(LOG_INFO|LOG_AUTH, + "%s@%s as %s: unknown login. cmd='%.80s'", + remuser, hostname, locuser, cmdbuf); + if (errorstr == NULL) + errorstr = "Login incorrect.\n"; + goto fail; + } + if (chdir(pwd->pw_dir) < 0) { + (void) chdir("/"); +#ifdef notdef + syslog(LOG_INFO|LOG_AUTH, + "%s@%s as %s: no home directory. cmd='%.80s'", + remuser, hostname, locuser, cmdbuf); + error("No remote directory.\n"); + exit(1); +#endif + } + +#ifdef KERBEROS + if (use_kerberos) { + if (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0') { + if (kuserok(kdata, locuser) != 0) { + syslog(LOG_INFO|LOG_AUTH, + "Kerberos rsh denied to %s.%s@%s", + kdata->pname, kdata->pinst, kdata->prealm); + error("Permission denied.\n"); + exit(1); + } + } + } else +#endif + + if (errorstr || + (pwd->pw_expire && time(NULL) >= pwd->pw_expire) || + (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' && + iruserok(fromp->sin_addr.s_addr, pwd->pw_uid == 0, + remuser, locuser) < 0)) { + if (__rcmd_errstr) + syslog(LOG_INFO|LOG_AUTH, + "%s@%s as %s: permission denied (%s). cmd='%.80s'", + remuser, hostname, locuser, __rcmd_errstr, + cmdbuf); + else + syslog(LOG_INFO|LOG_AUTH, + "%s@%s as %s: permission denied. cmd='%.80s'", + remuser, hostname, locuser, cmdbuf); +fail: + if (errorstr == NULL) + errorstr = "Permission denied.\n"; + error(errorstr, errorhost); + exit(1); + } + + if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) { + error("Logins currently disabled.\n"); + exit(1); + } +#if BSD > 43 + /* before fork, while we're session leader */ + if (setlogin(pwd->pw_name) < 0) + syslog(LOG_ERR, "setlogin() failed: %m"); +#endif + + (void) write(STDERR_FILENO, "\0", 1); + sent_null = 1; + + if (port) { + if (pipe(pv) < 0) { + error("Can't make pipe.\n"); + exit(1); + } +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) { + if (pipe(pv1) < 0) { + error("Can't make 2nd pipe.\n"); + exit(1); + } + if (pipe(pv2) < 0) { + error("Can't make 3rd pipe.\n"); + exit(1); + } + } +#endif +#endif + pid = fork(); + if (pid == -1) { + error("Can't fork; try again.\n"); + exit(1); + } + if (pid) { +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) { + static char msg[] = SECURE_MESSAGE; + (void) close(pv1[1]); + (void) close(pv2[1]); + des_write(s, msg, sizeof(msg) - 1); + + } else +#endif +#endif + { + (void) close(0); + (void) close(1); + } + (void) close(2); + (void) close(pv[1]); + + FD_ZERO(&readfrom); + FD_SET(s, &readfrom); + FD_SET(pv[0], &readfrom); + if (pv[0] > s) + nfd = pv[0]; + else + nfd = s; +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) { + FD_ZERO(&writeto); + FD_SET(pv2[0], &writeto); + FD_SET(pv1[0], &readfrom); + + nfd = MAX(nfd, pv2[0]); + nfd = MAX(nfd, pv1[0]); + } else +#endif +#endif + ioctl(pv[0], FIONBIO, (char *)&one); + + /* should set s nbio! */ + nfd++; + do { + ready = readfrom; +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) { + wready = writeto; + if (select(nfd, &ready, + &wready, (fd_set *) 0, + (struct timeval *) 0) < 0) + break; + } else +#endif +#endif + if (select(nfd, &ready, (fd_set *)0, + (fd_set *)0, (struct timeval *)0) < 0) + break; + if (FD_ISSET(s, &ready)) { + int ret; +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) + ret = des_read(s, &sig, 1); + else +#endif +#endif + ret = read(s, &sig, 1); + if (ret <= 0) + FD_CLR(s, &readfrom); + else + killpg(pid, sig); + } + if (FD_ISSET(pv[0], &ready)) { + errno = 0; + cc = read(pv[0], buf, sizeof(buf)); + if (cc <= 0) { + shutdown(s, 1+1); + FD_CLR(pv[0], &readfrom); + } else { +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) + (void) + des_write(s, buf, cc); + else +#endif +#endif + (void) + write(s, buf, cc); + } + } +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt && FD_ISSET(pv1[0], &ready)) { + errno = 0; + cc = read(pv1[0], buf, sizeof(buf)); + if (cc <= 0) { + shutdown(pv1[0], 1+1); + FD_CLR(pv1[0], &readfrom); + } else + (void) des_write(STDOUT_FILENO, + buf, cc); + } + + if (doencrypt && FD_ISSET(pv2[0], &wready)) { + errno = 0; + cc = des_read(STDIN_FILENO, + buf, sizeof(buf)); + if (cc <= 0) { + shutdown(pv2[0], 1+1); + FD_CLR(pv2[0], &writeto); + } else + (void) write(pv2[0], buf, cc); + } +#endif +#endif + + } while (FD_ISSET(s, &readfrom) || +#ifdef CRYPT +#ifdef KERBEROS + (doencrypt && FD_ISSET(pv1[0], &readfrom)) || +#endif +#endif + FD_ISSET(pv[0], &readfrom)); + exit(0); + } + setpgrp(0, getpid()); + (void) close(s); + (void) close(pv[0]); +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) { + close(pv1[0]); close(pv2[0]); + dup2(pv1[1], 1); + dup2(pv2[1], 0); + close(pv1[1]); + close(pv2[1]); + } +#endif +#endif + dup2(pv[1], 2); + close(pv[1]); + } + if (*pwd->pw_shell == '\0') + pwd->pw_shell = _PATH_BSHELL; + (void) setgid((gid_t)pwd->pw_gid); + initgroups(pwd->pw_name, pwd->pw_gid); + (void) setuid((uid_t)pwd->pw_uid); + environ = envinit; + strncat(homedir, pwd->pw_dir, sizeof(homedir)-6); + strcat(path, _PATH_DEFPATH); + strncat(shell, pwd->pw_shell, sizeof(shell)-7); + strncat(username, pwd->pw_name, sizeof(username)-6); + cp = strrchr(pwd->pw_shell, '/'); + if (cp) + cp++; + else + cp = pwd->pw_shell; + endpwent(); + if (log_success || pwd->pw_uid == 0) { +#ifdef KERBEROS + if (use_kerberos) + syslog(LOG_INFO|LOG_AUTH, + "Kerberos shell from %s.%s@%s on %s as %s, cmd='%.80s'", + kdata->pname, kdata->pinst, kdata->prealm, + hostname, locuser, cmdbuf); + else +#endif + syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'", + remuser, hostname, locuser, cmdbuf); + } + execl(pwd->pw_shell, cp, "-c", cmdbuf, 0); + perror(pwd->pw_shell); + exit(1); +} + +/* + * Report error to client. Note: can't be used until second socket has + * connected to client, or older clients will hang waiting for that + * connection first. + */ +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +error(const char *fmt, ...) +#else +error(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; + int len; + char *bp, buf[BUFSIZ]; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + bp = buf; + if (sent_null == 0) { + *bp++ = 1; + len = 1; + } else + len = 0; + (void)vsnprintf(bp, sizeof(buf) - 1, fmt, ap); + (void)write(STDERR_FILENO, buf, len + strlen(bp)); +} + +void +getstr(buf, cnt, err) + char *buf, *err; + int cnt; +{ + char c; + + do { + if (read(STDIN_FILENO, &c, 1) != 1) + exit(1); + *buf++ = c; + if (--cnt == 0) { + error("%s too long\n", err); + exit(1); + } + } while (c != 0); +} + +/* + * Check whether host h is in our local domain, + * defined as sharing the last two components of the domain part, + * or the entire domain part if the local domain has only one component. + * If either name is unqualified (contains no '.'), + * assume that the host is local, as it will be + * interpreted as such. + */ +int +local_domain(h) + char *h; +{ + char localhost[MAXHOSTNAMELEN]; + char *p1, *p2; + + localhost[0] = 0; + (void) gethostname(localhost, sizeof(localhost)); + p1 = topdomain(localhost); + p2 = topdomain(h); + if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) + return (1); + return (0); +} + +char * +topdomain(h) + char *h; +{ + char *p, *maybe = NULL; + int dots = 0; + + for (p = h + strlen(h); p >= h; p--) { + if (*p == '.') { + if (++dots == 2) + return (p); + maybe = p; + } + } + return (maybe); +} + +void +usage() +{ + + syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS); + exit(2); +} diff --git a/libexec/rtld-aout/Makefile b/libexec/rtld-aout/Makefile new file mode 100644 index 0000000..74e3c31 --- /dev/null +++ b/libexec/rtld-aout/Makefile @@ -0,0 +1,22 @@ +# $Id: Makefile,v 1.14 1995/03/04 17:46:23 nate Exp $ + +PROG= ld.so +SRCS= mdprologue.S rtld.c malloc.c shlib.c etc.c md.c +MAN1= rtld.1 +LDDIR?= $(.CURDIR)/.. +PICFLAG=-fpic +CFLAGS+=-I$(LDDIR) -I$(.CURDIR) -I$(LDDIR)/$(MACHINE) $(PICFLAG) -DRTLD +LDFLAGS+=-Bshareable -Bsymbolic -assert nosymbolic +ASFLAGS+=-k +DPADD+= ${LIBC:S/c.a/c_pic.a/} ${LIBC:S/c.a/gcc_pic.a/} +LDADD+= -lc_pic -lgcc_pic +BINDIR= /usr/libexec +INSTALLFLAGS+= -fschg +MLINKS= rtld.1 ld.so.1 + +.PATH: $(LDDIR) $(LDDIR)/$(MACHINE) + +$(PROG): ${OBJS} ${DPADD} + $(LD) -o $(PROG) $(LDFLAGS) $(OBJS) $(LDADD) + +.include <bsd.prog.mk> diff --git a/libexec/rtld-aout/i386/md-static-funcs.c b/libexec/rtld-aout/i386/md-static-funcs.c new file mode 100644 index 0000000..64491e3 --- /dev/null +++ b/libexec/rtld-aout/i386/md-static-funcs.c @@ -0,0 +1,16 @@ +/* + * $Id: md-static-funcs.c,v 1.2 1994/02/13 20:42:06 jkh Exp $ + * + * Called by ld.so when onanating. + * This *must* be a static function, so it is not called through a jmpslot. + */ + +static inline void +md_relocate_simple(r, relocation, addr) +struct relocation_info *r; +long relocation; +char *addr; +{ + *(long *)addr += relocation; +} + diff --git a/libexec/rtld-aout/i386/md.c b/libexec/rtld-aout/i386/md.c new file mode 100644 index 0000000..07271fc --- /dev/null +++ b/libexec/rtld-aout/i386/md.c @@ -0,0 +1,360 @@ +/* + * Copyright (c) 1993 Paul Kranenburg + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Paul Kranenburg. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: md.c,v 1.11 1994/12/23 22:31:12 nate Exp $ + */ + +#include <sys/param.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <err.h> +#include <fcntl.h> +#include <a.out.h> +#include <stab.h> +#include <string.h> + +#include "ld.h" + +/* + * Get relocation addend corresponding to relocation record RP + * from address ADDR + */ +long +md_get_addend(rp, addr) +struct relocation_info *rp; +unsigned char *addr; +{ + switch (RELOC_TARGET_SIZE(rp)) { + case 0: + return get_byte(addr); + case 1: + return get_short(addr); + case 2: + return get_long(addr); + default: + errx(1, "Unsupported relocation size: %x", + RELOC_TARGET_SIZE(rp)); + } +} + +/* + * Put RELOCATION at ADDR according to relocation record RP. + */ +void +md_relocate(rp, relocation, addr, relocatable_output) +struct relocation_info *rp; +long relocation; +unsigned char *addr; +int relocatable_output; +{ + switch (RELOC_TARGET_SIZE(rp)) { + case 0: + put_byte(addr, relocation); + break; + case 1: + put_short(addr, relocation); + break; + case 2: + put_long(addr, relocation); + break; + default: + errx(1, "Unsupported relocation size: %x", + RELOC_TARGET_SIZE(rp)); + } +} + +/* + * Machine dependent part of claim_rrs_reloc(). + * Set RRS relocation type. + */ +int +md_make_reloc(rp, r, type) +struct relocation_info *rp, *r; +int type; +{ + /* Relocation size */ + r->r_length = rp->r_length; + + if (RELOC_PCREL_P(rp)) + r->r_pcrel = 1; + + if (type & RELTYPE_RELATIVE) + r->r_relative = 1; + + if (type & RELTYPE_COPY) + r->r_copy = 1; + + return 0; +} + +/* + * Set up a transfer from jmpslot at OFFSET (relative to the PLT table) + * to the binder slot (which is at offset 0 of the PLT). + */ +void +md_make_jmpslot(sp, offset, index) +jmpslot_t *sp; +long offset; +long index; +{ + /* + * i386 PC-relative "fixed point" is located right after the + * instruction it pertains to. + */ + u_long fudge = - (sizeof(sp->opcode) + sizeof(sp->addr) + offset); + + sp->opcode = CALL; +#if 0 + sp->addr = fudge; +#else + sp->addr[0] = fudge & 0xffff; + sp->addr[1] = fudge >> 16; +#endif + sp->reloc_index = index; +} + +/* + * Set up a "direct" transfer (ie. not through the run-time binder) from + * jmpslot at OFFSET to ADDR. Used by `ld' when the SYMBOLIC flag is on, + * and by `ld.so' after resolving the symbol. + * On the i386, we use the JMP instruction which is PC relative, so no + * further RRS relocations will be necessary for such a jmpslot. + */ +void +md_fix_jmpslot(sp, offset, addr) +jmpslot_t *sp; +long offset; +u_long addr; +{ + u_long fudge = addr - (sizeof(sp->opcode) + sizeof(sp->addr) + offset); + + sp->opcode = JUMP; +#if 0 + sp->addr = fudge; +#else + sp->addr[0] = fudge & 0xffff; + sp->addr[1] = fudge >> 16; +#endif + sp->reloc_index = 0; +} + +/* + * Update the relocation record for a RRS jmpslot. + */ +void +md_make_jmpreloc(rp, r, type) +struct relocation_info *rp, *r; +int type; +{ + jmpslot_t *sp; + + /* + * Fix relocation address to point to the correct + * location within this jmpslot. + */ + r->r_address += sizeof(sp->opcode); + + /* Relocation size */ + r->r_length = 2; + + /* Set relocation type */ + r->r_jmptable = 1; + if (type & RELTYPE_RELATIVE) + r->r_relative = 1; + +} + +/* + * Set relocation type for a RRS GOT relocation. + */ +void +md_make_gotreloc(rp, r, type) +struct relocation_info *rp, *r; +int type; +{ + r->r_baserel = 1; + if (type & RELTYPE_RELATIVE) + r->r_relative = 1; + + /* Relocation size */ + r->r_length = 2; +} + +/* + * Set relocation type for a RRS copy operation. + */ +void +md_make_cpyreloc(rp, r) +struct relocation_info *rp, *r; +{ + /* Relocation size */ + r->r_length = 2; + + r->r_copy = 1; +} + +void +md_set_breakpoint(where, savep) +long where; +long *savep; +{ + *savep = *(long *)where; + *(char *)where = TRAP; +} + +#ifndef RTLD + +#ifdef __FreeBSD__ +int netzmagic; +#endif + +/* + * Initialize (output) exec header such that useful values are + * obtained from subsequent N_*() macro evaluations. + */ +void +md_init_header(hp, magic, flags) +struct exec *hp; +int magic, flags; +{ +#ifdef NetBSD + if (oldmagic || magic == QMAGIC) + hp->a_midmag = magic; + else + N_SETMAGIC((*hp), magic, MID_I386, flags); +#endif +#ifdef __FreeBSD__ + if (oldmagic) + hp->a_midmag = magic; + else if (netzmagic) + N_SETMAGIC_NET((*hp), magic, MID_I386, flags); + else + N_SETMAGIC((*hp), magic, MID_I386, flags); +#endif + + /* TEXT_START depends on the value of outheader.a_entry. */ + if (!(link_mode & SHAREABLE)) + hp->a_entry = PAGSIZ; +} +#endif /* RTLD */ + + +#ifdef NEED_SWAP +/* + * Byte swap routines for cross-linking. + */ + +void +md_swapin_exec_hdr(h) +struct exec *h; +{ + int skip = 0; + + if (!N_BADMAG(*h)) + skip = 1; + + swap_longs((long *)h + skip, sizeof(*h)/sizeof(long) - skip); +} + +void +md_swapout_exec_hdr(h) +struct exec *h; +{ + /* NetBSD: Always leave magic alone */ + int skip = 1; +#if 0 + if (N_GETMAGIC(*h) == OMAGIC) + skip = 0; +#endif + + swap_longs((long *)h + skip, sizeof(*h)/sizeof(long) - skip); +} + + +void +md_swapin_reloc(r, n) +struct relocation_info *r; +int n; +{ + int bits; + + for (; n; n--, r++) { + r->r_address = md_swap_long(r->r_address); + bits = ((int *)r)[1]; + r->r_symbolnum = md_swap_long(bits) & 0x00ffffff; + r->r_pcrel = (bits & 1); + r->r_length = (bits >> 1) & 3; + r->r_extern = (bits >> 3) & 1; + r->r_baserel = (bits >> 4) & 1; + r->r_jmptable = (bits >> 5) & 1; + r->r_relative = (bits >> 6) & 1; +#ifdef N_SIZE + r->r_copy = (bits >> 7) & 1; +#endif + } +} + +void +md_swapout_reloc(r, n) +struct relocation_info *r; +int n; +{ + int bits; + + for (; n; n--, r++) { + r->r_address = md_swap_long(r->r_address); + bits = md_swap_long(r->r_symbolnum) & 0xffffff00; + bits |= (r->r_pcrel & 1); + bits |= (r->r_length & 3) << 1; + bits |= (r->r_extern & 1) << 3; + bits |= (r->r_baserel & 1) << 4; + bits |= (r->r_jmptable & 1) << 5; + bits |= (r->r_relative & 1) << 6; +#ifdef N_SIZE + bits |= (r->r_copy & 1) << 7; +#endif + ((int *)r)[1] = bits; + } +} + +void +md_swapout_jmpslot(j, n) +jmpslot_t *j; +int n; +{ + for (; n; n--, j++) { + j->opcode = md_swap_short(j->opcode); + j->addr[0] = md_swap_short(j->addr[0]); + j->addr[1] = md_swap_short(j->addr[1]); + j->reloc_index = md_swap_short(j->reloc_index); + } +} + +#endif /* NEED_SWAP */ diff --git a/libexec/rtld-aout/i386/md.h b/libexec/rtld-aout/i386/md.h new file mode 100644 index 0000000..da5283d --- /dev/null +++ b/libexec/rtld-aout/i386/md.h @@ -0,0 +1,226 @@ +/* + * Copyright (c) 1993 Paul Kranenburg + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Paul Kranenburg. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: md.h,v 1.11 1994/12/23 22:31:14 nate Exp $ + */ + + +#if defined(CROSS_LINKER) && defined(XHOST) && XHOST==sparc +#define NEED_SWAP +#endif + +#define MAX_ALIGNMENT (sizeof (long)) + +#ifdef NetBSD +#define PAGSIZ __LDPGSZ +#else +#define PAGSIZ 4096 +#endif + +#if defined(NetBSD) || defined(CROSS_LINKER) + +#define N_SET_FLAG(ex,f) (oldmagic || N_GETMAGIC(ex)==QMAGIC ? (0) : \ + N_SETMAGIC(ex, \ + N_GETMAGIC(ex), \ + MID_MACHINE, \ + N_GETFLAG(ex)|(f))) + +#define N_IS_DYNAMIC(ex) ((N_GETFLAG(ex) & EX_DYNAMIC)) + +#define N_BADMID(ex) \ + (N_GETMID(ex) != 0 && N_GETMID(ex) != MID_MACHINE) + +#endif + +/* + * FreeBSD does it differently + */ +#ifdef __FreeBSD__ +#define N_SET_FLAG(ex,f) (oldmagic ? (0) : \ + (netzmagic == 0 ? \ + N_SETMAGIC(ex, \ + N_GETMAGIC(ex), \ + MID_MACHINE, \ + N_GETFLAG(ex)|(f)) : \ + N_SETMAGIC_NET(ex, \ + N_GETMAGIC_NET(ex), \ + MID_MACHINE, \ + N_GETFLAG_NET(ex)|(f)) )) + +#define N_IS_DYNAMIC(ex) ((N_GETMAGIC_NET(ex) == ZMAGIC) ? \ + ((N_GETFLAG_NET(ex) & EX_DYNAMIC)) : \ + ((N_GETFLAG(ex) & EX_DYNAMIC) )) +#define N_BADMID(ex) 0 +#endif + +/* + * Should be handled by a.out.h ? + */ +#define N_ADJUST(ex) (((ex).a_entry < PAGSIZ) ? -PAGSIZ : 0) +#define TEXT_START(ex) (N_TXTADDR(ex) + N_ADJUST(ex)) +#define DATA_START(ex) (N_DATADDR(ex) + N_ADJUST(ex)) + +#define RELOC_STATICS_THROUGH_GOT_P(r) (0) +#define JMPSLOT_NEEDS_RELOC (0) + +#define md_got_reloc(r) (0) + +#define md_get_rt_segment_addend(r,a) md_get_addend(r,a) + +/* Width of a Global Offset Table entry */ +#define GOT_ENTRY_SIZE 4 +typedef long got_t; + +typedef struct jmpslot { + u_short opcode; + u_short addr[2]; + u_short reloc_index; +#define JMPSLOT_RELOC_MASK 0xffff +} jmpslot_t; + +#define NOP 0x90 +#define CALL 0xe890 /* NOP + CALL opcode */ +#define JUMP 0xe990 /* NOP + JMP opcode */ +#define TRAP 0xcc /* INT 3 */ + +/* + * Byte swap defs for cross linking + */ + +#if !defined(NEED_SWAP) + +#define md_swapin_exec_hdr(h) +#define md_swapout_exec_hdr(h) +#define md_swapin_symbols(s,n) +#define md_swapout_symbols(s,n) +#define md_swapin_zsymbols(s,n) +#define md_swapout_zsymbols(s,n) +#define md_swapin_reloc(r,n) +#define md_swapout_reloc(r,n) +#define md_swapin__dynamic(l) +#define md_swapout__dynamic(l) +#define md_swapin_section_dispatch_table(l) +#define md_swapout_section_dispatch_table(l) +#define md_swapin_so_debug(d) +#define md_swapout_so_debug(d) +#define md_swapin_rrs_hash(f,n) +#define md_swapout_rrs_hash(f,n) +#define md_swapin_sod(l,n) +#define md_swapout_sod(l,n) +#define md_swapout_jmpslot(j,n) +#define md_swapout_got(g,n) +#define md_swapin_ranlib_hdr(h,n) +#define md_swapout_ranlib_hdr(h,n) + +#endif /* NEED_SWAP */ + +#ifdef CROSS_LINKER + +#define get_byte(p) ( ((unsigned char *)(p))[0] ) + +#define get_short(p) ( ( ((unsigned char *)(p))[0] << 8) | \ + ( ((unsigned char *)(p))[1] ) \ + ) + +#define get_long(p) ( ( ((unsigned char *)(p))[0] << 24) | \ + ( ((unsigned char *)(p))[1] << 16) | \ + ( ((unsigned char *)(p))[2] << 8 ) | \ + ( ((unsigned char *)(p))[3] ) \ + ) + +#define put_byte(p, v) { ((unsigned char *)(p))[0] = ((unsigned long)(v)); } + +#define put_short(p, v) { ((unsigned char *)(p))[0] = \ + ((((unsigned long)(v)) >> 8) & 0xff); \ + ((unsigned char *)(p))[1] = \ + ((((unsigned long)(v)) ) & 0xff); } + +#define put_long(p, v) { ((unsigned char *)(p))[0] = \ + ((((unsigned long)(v)) >> 24) & 0xff); \ + ((unsigned char *)(p))[1] = \ + ((((unsigned long)(v)) >> 16) & 0xff); \ + ((unsigned char *)(p))[2] = \ + ((((unsigned long)(v)) >> 8) & 0xff); \ + ((unsigned char *)(p))[3] = \ + ((((unsigned long)(v)) ) & 0xff); } + +#ifdef NEED_SWAP + +/* Define IO byte swapping routines */ + +void md_swapin_exec_hdr __P((struct exec *)); +void md_swapout_exec_hdr __P((struct exec *)); +void md_swapin_reloc __P((struct relocation_info *, int)); +void md_swapout_reloc __P((struct relocation_info *, int)); +void md_swapout_jmpslot __P((jmpslot_t *, int)); + +#define md_swapin_symbols(s,n) swap_symbols(s,n) +#define md_swapout_symbols(s,n) swap_symbols(s,n) +#define md_swapin_zsymbols(s,n) swap_zsymbols(s,n) +#define md_swapout_zsymbols(s,n) swap_zsymbols(s,n) +#define md_swapin__dynamic(l) swap__dynamic(l) +#define md_swapout__dynamic(l) swap__dynamic(l) +#define md_swapin_section_dispatch_table(l) swap_section_dispatch_table(l) +#define md_swapout_section_dispatch_table(l) swap_section_dispatch_table(l) +#define md_swapin_so_debug(d) swap_so_debug(d) +#define md_swapout_so_debug(d) swap_so_debug(d) +#define md_swapin_rrs_hash(f,n) swap_rrs_hash(f,n) +#define md_swapout_rrs_hash(f,n) swap_rrs_hash(f,n) +#define md_swapin_sod(l,n) swapin_sod(l,n) +#define md_swapout_sod(l,n) swapout_sod(l,n) +#define md_swapout_got(g,n) swap_longs((long*)(g),n) +#define md_swapin_ranlib_hdr(h,n) swap_ranlib_hdr(h,n) +#define md_swapout_ranlib_hdr(h,n) swap_ranlib_hdr(h,n) + +#define md_swap_short(x) ( (((x) >> 8) & 0xff) | (((x) & 0xff) << 8) ) + +#define md_swap_long(x) ( (((x) >> 24) & 0xff ) | (((x) >> 8 ) & 0xff00 ) | \ + (((x) << 8 ) & 0xff0000) | (((x) << 24) & 0xff000000)) + +#else /* We need not swap, but must pay attention to alignment: */ + +#define md_swap_short(x) (x) +#define md_swap_long(x) (x) + +#endif /* NEED_SWAP */ + +#else /* Not a cross linker: use native */ + +#define md_swap_short(x) (x) +#define md_swap_long(x) (x) + +#define get_byte(where) (*(char *)(where)) +#define get_short(where) (*(short *)(where)) +#define get_long(where) (*(long *)(where)) + +#define put_byte(where,what) (*(char *)(where) = (what)) +#define put_short(where,what) (*(short *)(where) = (what)) +#define put_long(where,what) (*(long *)(where) = (what)) + +#endif /* CROSS_LINKER */ diff --git a/libexec/rtld-aout/i386/mdprologue.S b/libexec/rtld-aout/i386/mdprologue.S new file mode 100644 index 0000000..1de0f72 --- /dev/null +++ b/libexec/rtld-aout/i386/mdprologue.S @@ -0,0 +1,93 @@ +/* + * Copyright (c) 1993 Paul Kranenburg + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Paul Kranenburg. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: mdprologue.S,v 1.7 1994/12/04 07:42:44 mycroft Exp $ + */ + +/* + * i386 run-time link editor entry points. + */ + +#include <sys/syscall.h> + + .text + .globl _binder, _binder_entry + +/* + * _rtl(int version, struct crt_ldso *crtp) + */ + +_rtl: # crt0 calls us here + pushl %ebp # Allocate stack frame + movl %esp,%ebp + pushl %ebx + + call 1f # PIC function prologue +1: + popl %ebx + addl $_GLOBAL_OFFSET_TABLE_+[.-1b],%ebx + + movl 12(%ebp),%eax # Extract data from interface structure + movl (%eax),%eax # base address of ld.so (first field) + # setup arguments for rtld() + movl (%ebx),%ecx # 1st entry in GOT is our __DYNAMIC + addl %eax,%ecx # add load address + pushl %ecx # 3rd arg + pushl 12(%ebp) # 2nd arg == &crt. + pushl 8(%ebp) # 1st arg == version + addl _rtld@GOT(%ebx),%eax # relocate address of function + call %eax # _rtld(version, crtp, DYNAMIC) + addl $12,%esp # pop arguments + + popl %ebx + leave # remove stack frame + ret + + # First call to a procedure generally comes through here for + # binding. + +_binder_entry: + pushl %ebp # setup a stack frame + movl %esp,%ebp + pusha # save all regs + + xorl %eax,%eax # clear + movl 4(%ebp),%esi # return address in PLT + movw (%esi),%ax # get hold of relocation number + subl $6,%esi # make it point to the jmpslot + + pushl %eax # pushd arguments + pushl %esi # + call _binder@PLT # _binder(rpc, index) + addl $8,%esp # pop arguments + movl %eax,4(%ebp) # return value from _binder() == actual + # address of function + popa # restore regs + leave # remove our stack frame + ret diff --git a/libexec/rtld-aout/md-prologue.c b/libexec/rtld-aout/md-prologue.c new file mode 100644 index 0000000..dae455e --- /dev/null +++ b/libexec/rtld-aout/md-prologue.c @@ -0,0 +1,39 @@ +/* + * rtld entry pseudo code - turn into assembler and tweak it + */ + +#include <sys/types.h> +#include <sys/types.h> +#include <a.out.h> +#include "link.h" +#include "md.h" + +extern long _GOT_[]; +extern void (*rtld)(); +extern void (*binder())(); + +void +rtld_entry(version, crtp) +int version; +struct crt *crtp; +{ + register struct link_dynamic *dp; + register void (*f)(); + + /* __DYNAMIC is first entry in GOT */ + dp = (struct link_dynamic *) (_GOT_[0]+crtp->crt_ba); + + f = (void (*)())((long)rtld + crtp->crt_ba); + (*f)(version, crtp, dp); +} + +void +binder_entry() +{ + extern int PC; + struct jmpslot *sp; + void (*func)(); + + func = binder(PC, sp->reloc_index & 0x003fffff); + (*func)(); +} diff --git a/libexec/rtld-aout/rtld.1 b/libexec/rtld-aout/rtld.1 new file mode 100644 index 0000000..2b3c75a --- /dev/null +++ b/libexec/rtld-aout/rtld.1 @@ -0,0 +1,144 @@ +.\" $Id$ +.\" +.\" Copyright (c) 1995 Paul Kranenburg +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Paul Kranenburg. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd June 27, 1995 +.Dt RTLD 1 +.Os FreeBSD +.Sh NAME +.Nm ld.so +.Nd run-time link-editor +.Sh DESCRIPTION +.Nm +is a self-contained, position independent program image providing run-time +support for loading and link-editing shared objects into a process' +address space. It uses the data structures +.Po +see +.Xr link 5 +.Pc +contained within dynamically linked programs to determine which shared +libraries are needed and loads them at a convenient virtual address +using the +.Xr mmap 2 +system call. +.Pp +After all shared libraries have been succesfully loaded, +.Nm +proceeds to resolve external references from both the main program and +all objects loaded. A mechanism is provided for initialisation routines +to be called, on a per-object basis, giving a shared object an opportunity +to perfrom any extra set-up, before execution of the program proper begins. +This is useful for C++ libraries that contain static constrictors. +.Pp +.Nm +is itself a shared object that is initially loaded by the startup module +.Em crt0 . +Since +.Xr a.out 5 +formats do not provide easy access to the file header from within a running +process, +.Em crt0 +uses the special symbol +.Va _DYNAMIC +to determine whether a program is in fact dynamically linked or not. Whenever +the linker +.Xr ld 1 +has relocated this symbol to a location other then 0, +.Em crt0 +assumes the services of +.Nm +are needed +.Po +see +.Xr link 5 +for details +.Pc \&. +.Em crt0 +passes control to +.Nm +\&'s entry point before the program's +.Fn main +routine is called. Thus, +.Nm +can complete the link-editing process before the dynamic program calls upon +services of any dynamic library. +.Pp +To quickly locate the required shared objects in the filesystem, +.Nm +may use a +.Dq hints +file, prepared by the +.Xr ldconfig 8 +utility, in which the full path specification of the shared objects can be +looked up by hashing on the 3-tuple +.Ao +library-name, major-version-number, minor-version-number +.Ac \&. +.Pp +.Nm +recognises a number of environment variables that can be used to modify +its behaviour as follows: +.Pp +.Bl -tag -width "LD_TRACE_LOADED_OBJECTS" +.It Ev LD_LIBRARY_PATH +A colon separated list of directories, overriding the default search path +for shared libraries. +.It Ev LD_WARN_NON_PURE_CODE +When set, issue a warning whenever a link-editing operation requires +modification of the text segment of some loaded object. This is usually +indicative of an incorrectly built library. +.It Ev LD_SUPPRESS_WARNINGS +When set, no warning messages of any kind are issued. Normally, a warning +is given if satisfactorily versioned library could not be found. +.It Ev LD_TRACE_LOADED_OBJECTS +When set, causes +.Nm +to exit after loading the shared objects and printing a summary which includes +the absolute pathnames of all objects, to standard output. +.It Ev LD_NO_INTERN_SEARCH +When set, +.Nm +does not process any internal search paths that were recorded in the +executable. +.It Ev LD_NOSTD_PATH +When set, do not include a set of built-in standard directory paths for +searching. This might be useful when running on a system with a completely +non-standard filesystem layout. +.El +.Pp +.Sh FILES +/var/run/ld.so.hints +.Pp +.Sh SEE ALSO +.Xr ld 1 +.Xr ldconfig 8 +.Xr link 5 +.Sh HISTORY +The shared library model employed first appeared in SunOS 4.0 diff --git a/libexec/rtld-aout/rtld.1aout b/libexec/rtld-aout/rtld.1aout new file mode 100644 index 0000000..2b3c75a --- /dev/null +++ b/libexec/rtld-aout/rtld.1aout @@ -0,0 +1,144 @@ +.\" $Id$ +.\" +.\" Copyright (c) 1995 Paul Kranenburg +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Paul Kranenburg. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd June 27, 1995 +.Dt RTLD 1 +.Os FreeBSD +.Sh NAME +.Nm ld.so +.Nd run-time link-editor +.Sh DESCRIPTION +.Nm +is a self-contained, position independent program image providing run-time +support for loading and link-editing shared objects into a process' +address space. It uses the data structures +.Po +see +.Xr link 5 +.Pc +contained within dynamically linked programs to determine which shared +libraries are needed and loads them at a convenient virtual address +using the +.Xr mmap 2 +system call. +.Pp +After all shared libraries have been succesfully loaded, +.Nm +proceeds to resolve external references from both the main program and +all objects loaded. A mechanism is provided for initialisation routines +to be called, on a per-object basis, giving a shared object an opportunity +to perfrom any extra set-up, before execution of the program proper begins. +This is useful for C++ libraries that contain static constrictors. +.Pp +.Nm +is itself a shared object that is initially loaded by the startup module +.Em crt0 . +Since +.Xr a.out 5 +formats do not provide easy access to the file header from within a running +process, +.Em crt0 +uses the special symbol +.Va _DYNAMIC +to determine whether a program is in fact dynamically linked or not. Whenever +the linker +.Xr ld 1 +has relocated this symbol to a location other then 0, +.Em crt0 +assumes the services of +.Nm +are needed +.Po +see +.Xr link 5 +for details +.Pc \&. +.Em crt0 +passes control to +.Nm +\&'s entry point before the program's +.Fn main +routine is called. Thus, +.Nm +can complete the link-editing process before the dynamic program calls upon +services of any dynamic library. +.Pp +To quickly locate the required shared objects in the filesystem, +.Nm +may use a +.Dq hints +file, prepared by the +.Xr ldconfig 8 +utility, in which the full path specification of the shared objects can be +looked up by hashing on the 3-tuple +.Ao +library-name, major-version-number, minor-version-number +.Ac \&. +.Pp +.Nm +recognises a number of environment variables that can be used to modify +its behaviour as follows: +.Pp +.Bl -tag -width "LD_TRACE_LOADED_OBJECTS" +.It Ev LD_LIBRARY_PATH +A colon separated list of directories, overriding the default search path +for shared libraries. +.It Ev LD_WARN_NON_PURE_CODE +When set, issue a warning whenever a link-editing operation requires +modification of the text segment of some loaded object. This is usually +indicative of an incorrectly built library. +.It Ev LD_SUPPRESS_WARNINGS +When set, no warning messages of any kind are issued. Normally, a warning +is given if satisfactorily versioned library could not be found. +.It Ev LD_TRACE_LOADED_OBJECTS +When set, causes +.Nm +to exit after loading the shared objects and printing a summary which includes +the absolute pathnames of all objects, to standard output. +.It Ev LD_NO_INTERN_SEARCH +When set, +.Nm +does not process any internal search paths that were recorded in the +executable. +.It Ev LD_NOSTD_PATH +When set, do not include a set of built-in standard directory paths for +searching. This might be useful when running on a system with a completely +non-standard filesystem layout. +.El +.Pp +.Sh FILES +/var/run/ld.so.hints +.Pp +.Sh SEE ALSO +.Xr ld 1 +.Xr ldconfig 8 +.Xr link 5 +.Sh HISTORY +The shared library model employed first appeared in SunOS 4.0 diff --git a/libexec/rtld-aout/rtld.c b/libexec/rtld-aout/rtld.c new file mode 100644 index 0000000..2fd7d4f --- /dev/null +++ b/libexec/rtld-aout/rtld.c @@ -0,0 +1,1691 @@ +/* + * Copyright (c) 1993 Paul Kranenburg + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Paul Kranenburg. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: rtld.c,v 1.31 1995/10/27 22:01:00 jdp Exp $ + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/errno.h> +#include <sys/mman.h> +#ifndef MAP_COPY +#define MAP_COPY MAP_PRIVATE +#endif +#include <err.h> +#include <fcntl.h> +#include <a.out.h> +#include <stab.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include "ld.h" + +#ifndef MAP_ANON +#define MAP_ANON 0 +#define anon_open() do { \ + if ((anon_fd = open("/dev/zero", O_RDWR, 0)) == -1) \ + err("open: %s", "/dev/zero"); \ +} while (0) +#define anon_close() do { \ + (void)close(anon_fd); \ + anon_fd = -1; \ +} while (0) +#else +#define anon_open() +#define anon_close() +#endif + +/* + * Loader private data, hung off <so_map>->som_spd + */ +struct somap_private { + int spd_version; + struct so_map *spd_parent; + int spd_refcount; + int spd_flags; +#define RTLD_MAIN 1 +#define RTLD_RTLD 2 +#define RTLD_DL 4 +#define RTLD_INIT 8 + unsigned long a_text; /* text size, if known */ + unsigned long a_data; /* initialized data size */ + unsigned long a_bss; /* uninitialized data size */ + +#ifdef SUN_COMPAT + long spd_offset; /* Correction for Sun main programs */ +#endif +}; + +#define LM_PRIVATE(smp) ((struct somap_private *)(smp)->som_spd) + +#ifdef SUN_COMPAT +#define LM_OFFSET(smp) (LM_PRIVATE(smp)->spd_offset) +#else +#define LM_OFFSET(smp) (0) +#endif + +/* Base address for section_dispatch_table entries */ +#define LM_LDBASE(smp) (smp->som_addr + LM_OFFSET(smp)) + +/* Start of text segment */ +#define LM_TXTADDR(smp) (smp->som_addr == (caddr_t)0 ? PAGSIZ : 0) + +/* Start of run-time relocation_info */ +#define LM_REL(smp) ((struct relocation_info *) \ + (smp->som_addr + LM_OFFSET(smp) + LD_REL((smp)->som_dynamic))) + +/* Start of symbols */ +#define LM_SYMBOL(smp, i) ((struct nzlist *) \ + (smp->som_addr + LM_OFFSET(smp) + LD_SYMBOL((smp)->som_dynamic) + \ + i * (LD_VERSION_NZLIST_P(smp->som_dynamic->d_version) ? \ + sizeof(struct nzlist) : sizeof(struct nlist)))) + +/* Start of hash table */ +#define LM_HASH(smp) ((struct rrs_hash *) \ + ((smp)->som_addr + LM_OFFSET(smp) + LD_HASH((smp)->som_dynamic))) + +/* Start of strings */ +#define LM_STRINGS(smp) ((char *) \ + ((smp)->som_addr + LM_OFFSET(smp) + LD_STRINGS((smp)->som_dynamic))) + +/* End of text */ +#define LM_ETEXT(smp) ((char *) \ + ((smp)->som_addr + LM_TXTADDR(smp) + LD_TEXTSZ((smp)->som_dynamic))) + +/* Needed shared objects */ +#define LM_NEED(smp) ((struct sod *) \ + ((smp)->som_addr + LM_TXTADDR(smp) + LD_NEED((smp)->som_dynamic))) + +/* PLT is in data segment, so don't use LM_OFFSET here */ +#define LM_PLT(smp) ((jmpslot_t *) \ + ((smp)->som_addr + LD_PLT((smp)->som_dynamic))) + +/* Parent of link map */ +#define LM_PARENT(smp) (LM_PRIVATE(smp)->spd_parent) + +char **environ; +char *__progname; +int errno; + +static uid_t uid, euid; +static gid_t gid, egid; +static int careful; +static char __main_progname[] = "main"; +static char *main_progname = __main_progname; +static char us[] = "/usr/libexec/ld.so"; +static int anon_fd = -1; +static char *ld_library_path; + +struct so_map *link_map_head, *main_map; +struct so_map **link_map_tail = &link_map_head; +struct rt_symbol *rt_symbol_head; + +static void *__dlopen __P((char *, int)); +static int __dlclose __P((void *)); +static void *__dlsym __P((void *, char *)); +static char *__dlerror __P((void)); +static void __dlexit __P((void)); + +static struct ld_entry ld_entry = { + __dlopen, __dlclose, __dlsym, __dlerror, __dlexit +}; + + void xprintf __P((char *, ...)); +static void load_objects __P(( struct crt_ldso *, + struct _dynamic *)); +static struct so_map *map_object __P((struct sod *, struct so_map *)); +static int unmap_object __P((struct so_map *)); +static struct so_map *load_object __P((struct sod *, struct so_map *, + int, int)); +static int unload_object __P((struct so_map *)); +static struct so_map *alloc_link_map __P(( char *, struct sod *, + struct so_map *, caddr_t, + struct _dynamic *)); +static inline int check_text_reloc __P(( struct relocation_info *, + struct so_map *, + caddr_t)); +static int reloc_map __P((struct so_map *)); +static void reloc_copy __P((struct so_map *)); +static void init_map __P((struct so_map *, char *, int)); +static void call_map __P((struct so_map *, char *)); +static char *rtfindlib __P((char *, int, int, int *)); +void binder_entry __P((void)); +long binder __P((jmpslot_t *)); +static struct nzlist *lookup __P((char *, struct so_map **, int)); +static inline struct rt_symbol *lookup_rts __P((char *)); +static struct rt_symbol *enter_rts __P((char *, long, int, caddr_t, + long, struct so_map *)); +static void generror __P((char *, ...)); +static void maphints __P((void)); +static void unmaphints __P((void)); + +static int dl_cascade __P((struct so_map *)); + +static inline int +strcmp (register const char *s1, register const char *s2) +{ + while (*s1 == *s2++) + if (*s1++ == 0) + return (0); + return (*(unsigned char *)s1 - *(unsigned char *)--s2); +} + +#include "md-static-funcs.c" + +/* + * Called from assembler stub that has set up crtp (passed from crt0) + * and dp (our __DYNAMIC). + */ +int +rtld(version, crtp, dp) +int version; +struct crt_ldso *crtp; +struct _dynamic *dp; +{ + struct relocation_info *reloc; + struct relocation_info *reloc_limit; /* End+1 of relocation */ + struct so_debug *ddp; + struct so_map *smp; + + /* Check version */ + if (version != CRT_VERSION_BSD_2 && + version != CRT_VERSION_BSD_3 && + version != CRT_VERSION_SUN) + return -1; + + /* Fixup __DYNAMIC structure */ + (long)dp->d_un.d_sdt += crtp->crt_ba; + + /* Relocate ourselves */ + reloc = (struct relocation_info *) (LD_REL(dp) + crtp->crt_ba); + reloc_limit = + (struct relocation_info *) ((char *) reloc + LD_RELSZ(dp)); + while(reloc < reloc_limit) { + /* + * Objects linked with "-Bsymbolic" (in particular, ld.so + * itself) can end up having unused relocation entries at + * the end. These can be detected by the fact that they + * have an address of 0. + */ + if(reloc->r_address == 0) /* We're done */ + break; + md_relocate_simple(reloc, crtp->crt_ba, + reloc->r_address + crtp->crt_ba); + ++reloc; + } + + __progname = "ld.so"; + if (version >= CRT_VERSION_BSD_3) + main_progname = crtp->crt_prog; + + /* Setup out (private) environ variable */ + environ = crtp->crt_ep; + + /* Get user and group identifiers */ + uid = getuid(); euid = geteuid(); + gid = getgid(); egid = getegid(); + + careful = (uid != euid) || (gid != egid); + + if (careful) { + unsetenv("LD_LIBRARY_PATH"); + unsetenv("LD_PRELOAD"); + } else + ld_library_path = getenv("LD_LIBRARY_PATH"); + + /* Setup directory search */ + add_search_path(ld_library_path); + std_search_path(); + + anon_open(); + /* Load required objects into the process address space */ + load_objects(crtp, dp); + + /* Fill in some fields in main's __DYNAMIC structure */ + crtp->crt_dp->d_entry = &ld_entry; + crtp->crt_dp->d_un.d_sdt->sdt_loaded = link_map_head->som_next; + + /* Relocate all loaded objects according to their RRS segments */ + for (smp = link_map_head; smp; smp = smp->som_next) { + if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) + continue; + if (reloc_map(smp) < 0) + return -1; + } + + /* Copy any relocated initialized data. */ + for (smp = link_map_head; smp; smp = smp->som_next) { + if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) + continue; + reloc_copy(smp); + } + + /* Call any object initialization routines. */ + for (smp = link_map_head; smp; smp = smp->som_next) { + if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) + continue; + init_map(smp, ".init", 0); + } + + ddp = crtp->crt_dp->d_debug; + ddp->dd_cc = rt_symbol_head; + if (ddp->dd_in_debugger) { + caddr_t addr = (caddr_t)((long)crtp->crt_bp & (~(PAGSIZ - 1))); + + /* Set breakpoint for the benefit of debuggers */ + if (mprotect(addr, PAGSIZ, + PROT_READ|PROT_WRITE|PROT_EXEC) == -1) { + err(1, "Cannot set breakpoint (%s)", main_progname); + } + md_set_breakpoint((long)crtp->crt_bp, (long *)&ddp->dd_bpt_shadow); + if (mprotect(addr, PAGSIZ, PROT_READ|PROT_EXEC) == -1) { + err(1, "Cannot re-protect breakpoint (%s)", + main_progname); + } + + ddp->dd_bpt_addr = crtp->crt_bp; + if (link_map_head) + ddp->dd_sym_loaded = 1; + } + + /* Close the hints file */ + unmaphints(); + + /* Close our file descriptor */ + (void)close(crtp->crt_ldfd); + anon_close(); + + return LDSO_VERSION_HAS_DLEXIT; +} + + +static void +load_objects(crtp, dp) +struct crt_ldso *crtp; +struct _dynamic *dp; +{ + struct so_map *smp; + int tracing = (int)getenv("LD_TRACE_LOADED_OBJECTS"); + + /* Handle LD_PRELOAD's here */ + + /* Make an entry for the main program */ + smp = alloc_link_map(main_progname, (struct sod *)0, (struct so_map *)0, + (caddr_t)0, crtp->crt_dp); + LM_PRIVATE(smp)->spd_refcount++; + LM_PRIVATE(smp)->spd_flags |= RTLD_MAIN; + + /* Make an entry for ourselves */ + smp = alloc_link_map(us, (struct sod *)0, (struct so_map *)0, + (caddr_t)crtp->crt_ba, dp); + LM_PRIVATE(smp)->spd_refcount++; + LM_PRIVATE(smp)->spd_flags |= RTLD_RTLD; + + for (smp = link_map_head; smp; smp = smp->som_next) { + struct sod *sodp; + long next = 0; + + if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) + continue; + + if (smp->som_dynamic) + next = LD_NEED(smp->som_dynamic); + + while (next) { + struct so_map *newmap; + + sodp = (struct sod *)(LM_LDBASE(smp) + next); + if ((newmap = map_object(sodp, smp)) == NULL) { + if (!tracing) { + errx(1, "%s: %s", main_progname, + __dlerror()); + } + newmap = alloc_link_map(NULL, sodp, smp, 0, 0); + } + LM_PRIVATE(newmap)->spd_refcount++; + next = sodp->sod_next; + } + } + + if (! tracing) + return; + + for (smp = link_map_head; smp; smp = smp->som_next) { + struct sod *sodp; + char *name, *path; + + if ((sodp = smp->som_sod) == NULL) + continue; + name = sodp->sod_name + LM_LDBASE(LM_PARENT(smp)); + + if ((path = smp->som_path) == NULL) + path = "not found"; + + if (sodp->sod_library) + printf("\t-l%s.%d => %s (%p)\n", name, + sodp->sod_major, path, smp->som_addr); + else + printf("\t%s => %s (%p)\n", name, path, smp->som_addr); + } + + exit(0); +} + +/* + * Allocate a new link map for shared object NAME loaded at ADDR as a + * result of the presence of link object LOP in the link map PARENT. + */ + static struct so_map * +alloc_link_map(path, sodp, parent, addr, dp) + char *path; + struct sod *sodp; + struct so_map *parent; + caddr_t addr; + struct _dynamic *dp; +{ + struct so_map *smp; + struct somap_private *smpp; + size_t smp_size; + + /* + * Allocate so_map and private area with a single malloc. Round + * up the size of so_map so the private area is aligned. + */ + smp_size = ((((sizeof(struct so_map)) + sizeof (void *) - 1) / + sizeof (void *)) * sizeof (void *)); + + smp = (struct so_map *)xmalloc(smp_size + + sizeof (struct somap_private)); + smpp = (struct somap_private *) (((caddr_t) smp) + smp_size); + smp->som_next = NULL; + *link_map_tail = smp; + link_map_tail = &smp->som_next; + + smp->som_addr = addr; + if (path == NULL) + smp->som_path = NULL; + else + smp->som_path = strdup(path); + smp->som_sod = sodp; + smp->som_dynamic = dp; + smp->som_spd = (caddr_t)smpp; + +/*XXX*/ if (addr == 0) main_map = smp; + + smpp->spd_refcount = 0; + smpp->spd_flags = 0; + smpp->spd_parent = parent; + smpp->a_text = 0; + smpp->a_data = 0; + smpp->a_bss = 0; +#ifdef SUN_COMPAT + smpp->spd_offset = + (addr==0 && dp && dp->d_version==LD_VERSION_SUN) ? PAGSIZ : 0; +#endif + return smp; +} + + static struct so_map * +find_object(sodp, smp) + struct sod *sodp; + struct so_map *smp; +{ + char *path, *name = (char *)(sodp->sod_name + LM_LDBASE(smp)); + int usehints = 0; + struct so_map *p; + + if (sodp->sod_library) { + usehints = 1; +again: + path = rtfindlib(name, sodp->sod_major, + sodp->sod_minor, &usehints); + if (path == NULL) { + generror ("Can't find shared library \"%s\"", + name); + return NULL; + } + } else { + if (careful && *name != '/') { + generror ("Shared library path must start with \"/\" for \"%s\"", + name); + return NULL; + } + path = name; + } + + /* Check if already loaded */ + for (p = link_map_head; p; p = p->som_next) + if (p->som_path && strcmp(p->som_path, path) == 0) + break; + + return p; +} + +/* + * Map object identified by link object sodp which was found in link + * map smp. Returns a pointer to the link map for the requested object. + * + * On failure, it sets an error message that can be retrieved by __dlerror, + * and returns NULL. + */ + static struct so_map * +map_object(sodp, smp) + struct sod *sodp; + struct so_map *smp; +{ + struct _dynamic *dp; + char *path, *name = (char *)(sodp->sod_name + LM_LDBASE(smp)); + int fd; + caddr_t addr; + struct exec hdr; + int usehints = 0; + struct so_map *p; + struct somap_private *smpp; + + if (sodp->sod_library) { + usehints = 1; +again: + path = rtfindlib(name, sodp->sod_major, + sodp->sod_minor, &usehints); + if (path == NULL) { + generror ("Can't find shared library" + " \"lib%s.so.%d.%d\"", + name, sodp->sod_major, sodp->sod_minor); + return NULL; + } + } else { + if (careful && *name != '/') { + generror ("Shared library path must start with \"/\" for \"%s\"", + name); + return NULL; + } + path = name; + } + + /* Check if already loaded */ + for (p = link_map_head; p; p = p->som_next) + if (p->som_path && strcmp(p->som_path, path) == 0) + break; + + if (p != NULL) + return p; + + if ((fd = open(path, O_RDONLY, 0)) == -1) { + if (usehints) { + usehints = 0; + goto again; + } + generror ("open failed for \"%s\" : %s", + path, strerror (errno)); + return NULL; + } + + if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { + generror ("header read failed for \"%s\"", path); + (void)close(fd); + return NULL; + } + + if (N_BADMAG(hdr)) { + generror ("bad magic number in \"%s\"", path); + (void)close(fd); + return NULL; + } + + if ((addr = mmap(0, hdr.a_text + hdr.a_data + hdr.a_bss, + PROT_READ|PROT_EXEC, + MAP_COPY, fd, 0)) == (caddr_t)-1) { + generror ("mmap failed for \"%s\" : %s", + path, strerror (errno)); + (void)close(fd); + return NULL; + } + + if (mprotect(addr + hdr.a_text, hdr.a_data, + PROT_READ|PROT_WRITE|PROT_EXEC) != 0) { + generror ("mprotect failed for \"%s\" : %s", + path, strerror (errno)); + (void)close(fd); + return NULL; + } + + if (mmap(addr + hdr.a_text + hdr.a_data, hdr.a_bss, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_ANON|MAP_COPY|MAP_FIXED, + anon_fd, 0) == (caddr_t)-1) { + generror ("mmap failed for \"%s\" : %s", + path, strerror (errno)); + (void)close(fd); + return NULL; + } + + (void)close(fd); + + /* Assume _DYNAMIC is the first data item */ + dp = (struct _dynamic *)(addr+hdr.a_text); + + /* Fixup __DYNAMIC structure */ + (long)dp->d_un.d_sdt += (long)addr; + + p = alloc_link_map(path, sodp, smp, addr, dp); + + /* save segment sizes for unmap. */ + smpp = LM_PRIVATE(p); + smpp->a_text = hdr.a_text; + smpp->a_data = hdr.a_data; + smpp->a_bss = hdr.a_bss; + return p; +} + +/* + * Unmap an object that is nolonger in use. + */ + static int +unmap_object(smp) + struct so_map *smp; +{ + struct so_map *prev, *p; + struct somap_private *smpp; + + /* Find the object in the list and unlink it */ + + for (prev = NULL, p = link_map_head; + p != smp; + prev = p, p = p->som_next) continue; + + if (prev == NULL) { + link_map_head = smp->som_next; + if (smp->som_next == NULL) + link_map_tail = &link_map_head; + } else { + prev->som_next = smp->som_next; + if (smp->som_next == NULL) + link_map_tail = &prev->som_next; + } + + /* Unmap the sections we have mapped */ + + smpp = LM_PRIVATE(smp); + + if (munmap(smp->som_addr, smpp->a_text + smpp->a_data) < 0) { + generror ("munmap failed: %s", strerror (errno)); + return -1; + } + if (smpp->a_bss > 0) { + if (munmap(smp->som_addr + smpp->a_text + smpp->a_data, + smpp->a_bss) < 0) { + generror ("munmap failed: %s", strerror (errno)); + return -1; + } + } + if (smp->som_path) free(smp->som_path); + free(smp); + return 0; +} + +static inline int +check_text_reloc(r, smp, addr) +struct relocation_info *r; +struct so_map *smp; +caddr_t addr; +{ + char *sym; + + if (addr >= LM_ETEXT(smp)) + return 0; + + if (RELOC_EXTERN_P(r)) + sym = LM_STRINGS(smp) + + LM_SYMBOL(smp, RELOC_SYMBOL(r))->nz_strx; + else + sym = ""; + + if (getenv("LD_SUPPRESS_WARNINGS") == NULL && + getenv("LD_WARN_NON_PURE_CODE") != NULL) + warnx("warning: non pure code in %s at %x (%s)", + smp->som_path, r->r_address, sym); + + if (smp->som_write == 0 && + mprotect(smp->som_addr + LM_TXTADDR(smp), + LD_TEXTSZ(smp->som_dynamic), + PROT_READ|PROT_WRITE|PROT_EXEC) == -1) { + generror ("mprotect failed for \"%s\" : %s", + smp->som_path, strerror (errno)); + return -1; + } + + smp->som_write = 1; + return 0; +} + +static int +reloc_map(smp) + struct so_map *smp; +{ + /* + * Caching structure for reducing the number of calls to + * lookup() during relocation. + * + * While relocating a given shared object, the dynamic linker + * maintains a caching vector that is directly indexed by + * the symbol number in the relocation entry. The first time + * a given symbol is looked up, the caching vector is + * filled in with a pointer to the symbol table entry, and + * a pointer to the so_map of the shared object in which the + * symbol was defined. On subsequent uses of the same symbol, + * that information is retrieved directly from the caching + * vector, without calling lookup() again. + * + * A symbol that is referenced in a relocation entry is + * typically referenced in many relocation entries, so this + * caching reduces the number of calls to lookup() + * dramatically. The overall improvement in the speed of + * dynamic linking is also dramatic -- as much as a factor + * of three for programs that use many shared libaries. + */ + struct cacheent { + struct nzlist *np; /* Pointer to symbol entry */ + struct so_map *src_map; /* Shared object that defined symbol */ + }; + + struct _dynamic *dp = smp->som_dynamic; + struct relocation_info *r = LM_REL(smp); + struct relocation_info *rend = r + LD_RELSZ(dp)/sizeof(*r); + long symbolbase = (long)LM_SYMBOL(smp, 0); + char *stringbase = LM_STRINGS(smp); + int symsize = LD_VERSION_NZLIST_P(dp->d_version) ? + sizeof(struct nzlist) : + sizeof(struct nlist); + long numsyms = LD_STABSZ(dp) / symsize; + size_t cachebytes = numsyms * sizeof(struct cacheent); + struct cacheent *symcache = + (struct cacheent *) alloca(cachebytes); + + if(symcache == NULL) { + generror("Cannot allocate symbol caching vector for %s", + smp->som_path); + return -1; + } + bzero(symcache, cachebytes); + + if (LD_PLTSZ(dp)) + md_fix_jmpslot(LM_PLT(smp), + (long)LM_PLT(smp), (long)binder_entry); + + for (; r < rend; r++) { + char *sym; + caddr_t addr; + + /* + * Objects linked with "-Bsymbolic" can end up having unused + * relocation entries at the end. These can be detected by + * the fact that they have an address of 0. + */ + if(r->r_address == 0) /* Finished relocating this object */ + break; + + addr = smp->som_addr + r->r_address; + if (check_text_reloc(r, smp, addr) < 0) + return -1; + + if (RELOC_EXTERN_P(r)) { + struct so_map *src_map = NULL; + struct nzlist *p, *np; + long relocation; + + if (RELOC_LAZY_P(r)) + continue; + + p = (struct nzlist *) + (symbolbase + symsize * RELOC_SYMBOL(r)); + + if (p->nz_type == (N_SETV + N_EXT)) + src_map = smp; + + sym = stringbase + p->nz_strx; + + /* + * Look up the symbol, checking the caching + * vector first. + */ + np = symcache[RELOC_SYMBOL(r)].np; + if(np != NULL) /* Symbol already cached */ + src_map = symcache[RELOC_SYMBOL(r)].src_map; + else { /* Symbol not cached yet */ + np = lookup(sym, &src_map, 0/*XXX-jumpslots!*/); + /* + * Record the needed information about + * the symbol in the caching vector, + * so that we won't have to call + * lookup the next time we encounter + * the symbol. + */ + symcache[RELOC_SYMBOL(r)].np = np; + symcache[RELOC_SYMBOL(r)].src_map = src_map; + } + + if (np == NULL) { + generror ("Undefined symbol \"%s\" in %s:%s", + sym, main_progname, smp->som_path); + return -1; + } + + /* + * Found symbol definition. + * If it's in a link map, adjust value + * according to the load address of that map. + * Otherwise it's a run-time allocated common + * whose value is already up-to-date. + */ + relocation = md_get_addend(r, addr); + relocation += np->nz_value; + if (src_map) + relocation += (long)src_map->som_addr; + + if (RELOC_PCREL_P(r)) + relocation -= (long)smp->som_addr; + + if (RELOC_COPY_P(r) && src_map) { + (void)enter_rts(sym, + (long)addr, + N_DATA + N_EXT, + src_map->som_addr + np->nz_value, + np->nz_size, src_map); + continue; + } + md_relocate(r, relocation, addr, 0); + + } else { + md_relocate(r, +#ifdef SUN_COMPAT + md_get_rt_segment_addend(r, addr) +#else + md_get_addend(r, addr) +#endif + + (long)smp->som_addr, addr, 0); + } + + } + + if (smp->som_write) { + if (mprotect(smp->som_addr + LM_TXTADDR(smp), + LD_TEXTSZ(smp->som_dynamic), + PROT_READ|PROT_EXEC) == -1) { + generror ("mprotect failed for \"%s\" : %s", + smp->som_path, strerror (errno)); + return -1; + } + smp->som_write = 0; + } + return 0; +} + +static void +reloc_copy(smp) + struct so_map *smp; +{ + struct rt_symbol *rtsp; + + for (rtsp = rt_symbol_head; rtsp; rtsp = rtsp->rt_next) + if ((rtsp->rt_smp == NULL || rtsp->rt_smp == smp) && + rtsp->rt_sp->nz_type == N_DATA + N_EXT) { + bcopy(rtsp->rt_srcaddr, (caddr_t)rtsp->rt_sp->nz_value, + rtsp->rt_sp->nz_size); + } +} + +static void +init_map(smp, sym, dependants) + struct so_map *smp; + char *sym; + int dependants; +{ + struct so_map *src_map = smp; + struct nzlist *np; + + if (LM_PRIVATE(smp)->spd_flags & RTLD_INIT) + return; + + LM_PRIVATE(smp)->spd_flags |= RTLD_INIT; + + if (dependants) { + struct sod *sodp; + struct so_map *smp2; + long next; + + next = LD_NEED(smp->som_dynamic); + + while (next) { + sodp = (struct sod *)(LM_LDBASE(smp) + next); + smp2 = find_object(sodp, smp); + if (smp2) + init_map(smp2, sym, dependants); + next = sodp->sod_next; + } + } + + np = lookup(sym, &src_map, 1); + if (np) + (*(void (*)())(src_map->som_addr + np->nz_value))(); +} + +static void +call_map(smp, sym) + struct so_map *smp; + char *sym; +{ + struct so_map *src_map = smp; + struct nzlist *np; + + np = lookup(sym, &src_map, 1); + if (np) + (*(void (*)())(src_map->som_addr + np->nz_value))(); +} + +/* + * Load an object with all its dependant objects, recording the type of the + * object and optionally calling its init function. + */ +static struct so_map * +load_object(sodp, parent, type, init) + struct sod *sodp; + struct so_map *parent; + int type; + int init; +{ + struct so_map* smp; + + /* + * Find or map the object. + */ + smp = map_object(sodp, parent); + if (smp == NULL) return NULL; + + /* + * The first time the object is mapped, load it's dependant objects and + * relocate it. + */ + if (LM_PRIVATE(smp)->spd_refcount++ == 0) { + struct sod *sodp; + struct so_map *smp2; + long next; + + next = LD_NEED(smp->som_dynamic); + + /* + * Load dependant objects but defer initialisation until later. + * When all the dependants (and sub dependants, etc.) have been + * loaded and relocated, it is safe to call the init functions, + * using a recursive call to init_map. This ensures that if init + * code in the dependants calls code in the parent, it will work + * as expected. + */ + while (next) { + sodp = (struct sod *)(LM_LDBASE(smp) + next); + /* + * Dependant objects (of both dlopen and main) don't get a + * specific type. + */ + if ((smp2 = load_object(sodp, smp, 0, 0)) == NULL) { +#ifdef DEBUG +xprintf("ld.so: map_object failed on cascaded %s %s (%d.%d): %s\n", + smp->sod_library ? "library" : "file", smp->sod_name, + smp->sod_major, smp->sod_minor, strerror(errno)); +#endif + unload_object(smp); + return NULL; + } + next = sodp->sod_next; + } + + LM_PRIVATE(smp)->spd_flags |= type; + if (reloc_map(smp) < 0) { + unload_object(smp); + return NULL; + } + reloc_copy(smp); + if (init) { + init_map(smp, ".init", 1); + } + } + + return smp; +} + +/* + * Unload an object, recursively unloading dependant objects. + */ +static int +unload_object(smp) + struct so_map *smp; +{ + struct so_map *smp2; + struct sod *sodp; + long next; + + if (--LM_PRIVATE(smp)->spd_refcount != 0) + return -1; + + /* + * Call destructors for the object (before unloading its dependants + * since destructors may use them. Only call destructors if constructors + * have been called. + */ + if (LM_PRIVATE(smp)->spd_flags & RTLD_INIT) + call_map(smp, ".fini"); + + /* + * Unmap any dependant objects first. + */ + next = LD_NEED(smp->som_dynamic); + while (next) { + sodp = (struct sod *)(LM_LDBASE(smp) + next); + smp2 = find_object(sodp, smp); + if (smp2) + unload_object(smp2); + next = sodp->sod_next; + } + + /* + * Remove from address space. + */ + if (unmap_object(smp) < 0) + return -1; + + return 0; +} + +/* + * Run-time common symbol table. + */ + +#define RTC_TABSIZE 57 +static struct rt_symbol *rt_symtab[RTC_TABSIZE]; + +/* + * Compute hash value for run-time symbol table + */ + static inline int +hash_string(key) + char *key; +{ + register char *cp; + register int k; + + cp = key; + k = 0; + while (*cp) + k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff; + + return k; +} + +/* + * Lookup KEY in the run-time common symbol table. + */ + + static inline struct rt_symbol * +lookup_rts(key) + char *key; +{ + register int hashval; + register struct rt_symbol *rtsp; + + /* Determine which bucket. */ + + hashval = hash_string(key) % RTC_TABSIZE; + + /* Search the bucket. */ + + for (rtsp = rt_symtab[hashval]; rtsp; rtsp = rtsp->rt_link) + if (strcmp(key, rtsp->rt_sp->nz_name) == 0) + return rtsp; + + return NULL; +} + + static struct rt_symbol * +enter_rts(name, value, type, srcaddr, size, smp) + char *name; + long value; + int type; + caddr_t srcaddr; + long size; + struct so_map *smp; +{ + register int hashval; + register struct rt_symbol *rtsp, **rpp; + + /* Determine which bucket */ + hashval = hash_string(name) % RTC_TABSIZE; + + /* Find end of bucket */ + for (rpp = &rt_symtab[hashval]; *rpp; rpp = &(*rpp)->rt_link) + continue; + + /* Allocate new common symbol */ + rtsp = (struct rt_symbol *)malloc(sizeof(struct rt_symbol)); + rtsp->rt_sp = (struct nzlist *)malloc(sizeof(struct nzlist)); + rtsp->rt_sp->nz_name = strdup(name); + rtsp->rt_sp->nz_value = value; + rtsp->rt_sp->nz_type = type; + rtsp->rt_sp->nz_size = size; + rtsp->rt_srcaddr = srcaddr; + rtsp->rt_smp = smp; + rtsp->rt_link = NULL; + + /* Link onto linear list as well */ + rtsp->rt_next = rt_symbol_head; + rt_symbol_head = rtsp; + + *rpp = rtsp; + + return rtsp; +} + + +/* + * Lookup NAME in the link maps. The link map producing a definition + * is returned in SRC_MAP. If SRC_MAP is not NULL on entry the search is + * confined to that map. If STRONG is set, the symbol returned must + * have a proper type (used by binder()). + */ + static struct nzlist * +lookup(name, src_map, strong) + char *name; + struct so_map **src_map; /* IN/OUT */ + int strong; +{ + long common_size = 0; + struct so_map *smp; + struct rt_symbol *rtsp; + + if ((rtsp = lookup_rts(name)) != NULL) + return rtsp->rt_sp; + + /* + * Search all maps for a definition of NAME + */ + for (smp = link_map_head; smp; smp = smp->som_next) { + int buckets; + long hashval; + struct rrs_hash *hp; + char *cp; + struct nzlist *np; + + /* Some local caching */ + long symbolbase; + struct rrs_hash *hashbase; + char *stringbase; + int symsize; + + if (*src_map && smp != *src_map) + continue; + + if ((buckets = LD_BUCKETS(smp->som_dynamic)) == 0) + continue; + + if (LM_PRIVATE(smp)->spd_flags & RTLD_RTLD) + continue; + +restart: + /* + * Compute bucket in which the symbol might be found. + */ + for (hashval = 0, cp = name; *cp; cp++) + hashval = (hashval << 1) + *cp; + + hashval = (hashval & 0x7fffffff) % buckets; + + hashbase = LM_HASH(smp); + hp = hashbase + hashval; + if (hp->rh_symbolnum == -1) + /* Nothing in this bucket */ + continue; + + symbolbase = (long)LM_SYMBOL(smp, 0); + stringbase = LM_STRINGS(smp); + symsize = LD_VERSION_NZLIST_P(smp->som_dynamic->d_version)? + sizeof(struct nzlist) : + sizeof(struct nlist); + while (hp) { + np = (struct nzlist *) + (symbolbase + hp->rh_symbolnum * symsize); + cp = stringbase + np->nz_strx; + if (strcmp(cp, name) == 0) + break; + if (hp->rh_next == 0) + hp = NULL; + else + hp = hashbase + hp->rh_next; + } + if (hp == NULL) + /* Nothing in this bucket */ + continue; + + /* + * We have a symbol with the name we're looking for. + */ + if (np->nz_type == N_INDR+N_EXT) { + /* + * Next symbol gives the aliased name. Restart + * search with new name and confine to this map. + */ + name = stringbase + (++np)->nz_strx; + *src_map = smp; + goto restart; + } + + if (np->nz_value == 0) + /* It's not a definition */ + continue; + + if (np->nz_type == N_UNDF+N_EXT && np->nz_value != 0) { + if (np->nz_other == AUX_FUNC) { + /* It's a weak function definition */ + if (strong) + continue; + } else { + /* It's a common, note value and continue search */ + if (common_size < np->nz_value) + common_size = np->nz_value; + continue; + } + } + + *src_map = smp; + return np; + } + + if (common_size == 0) + /* Not found */ + return NULL; + + /* + * It's a common, enter into run-time common symbol table. + */ + rtsp = enter_rts(name, (long)calloc(1, common_size), + N_UNDF + N_EXT, 0, common_size, NULL); + +#if DEBUG + xprintf("Allocating common: %s size %d at %#x\n", name, common_size, + rtsp->rt_sp->nz_value); +#endif + + return rtsp->rt_sp; +} + +/* + * This routine is called from the jumptable to resolve + * procedure calls to shared objects. + */ + long +binder(jsp) + jmpslot_t *jsp; +{ + struct so_map *smp, *src_map = NULL; + long addr; + char *sym; + struct nzlist *np; + int index; + + /* + * Find the PLT map that contains JSP. + */ + for (smp = link_map_head; smp; smp = smp->som_next) { + if (LM_PLT(smp) < jsp && + jsp < LM_PLT(smp) + LD_PLTSZ(smp->som_dynamic)/sizeof(*jsp)) + break; + } + + if (smp == NULL) + errx(1, "Call to binder from unknown location: %#x\n", jsp); + + index = jsp->reloc_index & JMPSLOT_RELOC_MASK; + + /* Get the local symbol this jmpslot refers to */ + sym = LM_STRINGS(smp) + + LM_SYMBOL(smp,RELOC_SYMBOL(&LM_REL(smp)[index]))->nz_strx; + + np = lookup(sym, &src_map, 1); + if (np == NULL) + errx(1, "Undefined symbol \"%s\" called from %s:%s at %#x", + sym, main_progname, smp->som_path, jsp); + + /* Fixup jmpslot so future calls transfer directly to target */ + addr = np->nz_value; + if (src_map) + addr += (long)src_map->som_addr; + + md_fix_jmpslot(jsp, (long)jsp, addr); + +#if DEBUG + xprintf(" BINDER: %s located at = %#x in %s\n", sym, addr, + src_map->som_path); +#endif + return addr; +} + + +static int hfd; +static long hsize; +static struct hints_header *hheader; +static struct hints_bucket *hbuckets; +static char *hstrtab; + +#define HINTS_VALID (hheader != NULL && hheader != (struct hints_header *)-1) + + static void +maphints __P((void)) +{ + caddr_t addr; + + if ((hfd = open(_PATH_LD_HINTS, O_RDONLY, 0)) == -1) { + hheader = (struct hints_header *)-1; + return; + } + + hsize = PAGSIZ; + addr = mmap(0, hsize, PROT_READ, MAP_COPY, hfd, 0); + + if (addr == (caddr_t)-1) { + close(hfd); + hheader = (struct hints_header *)-1; + return; + } + + hheader = (struct hints_header *)addr; + if (HH_BADMAG(*hheader)) { + munmap(addr, hsize); + close(hfd); + hheader = (struct hints_header *)-1; + return; + } + + if (hheader->hh_version != LD_HINTS_VERSION_1) { + munmap(addr, hsize); + close(hfd); + hheader = (struct hints_header *)-1; + return; + } + + if (hheader->hh_ehints > hsize) { + if (mmap(addr+hsize, hheader->hh_ehints - hsize, + PROT_READ, MAP_COPY|MAP_FIXED, + hfd, hsize) != (caddr_t)(addr+hsize)) { + + munmap((caddr_t)hheader, hsize); + close(hfd); + hheader = (struct hints_header *)-1; + return; + } + } + + hbuckets = (struct hints_bucket *)(addr + hheader->hh_hashtab); + hstrtab = (char *)(addr + hheader->hh_strtab); +} + + static void +unmaphints() +{ + + if (HINTS_VALID) { + munmap((caddr_t)hheader, hsize); + close(hfd); + hheader = NULL; + } +} + + int +hinthash(cp, vmajor) + char *cp; + int vmajor; +{ + int k = 0; + + while (*cp) + k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff; + + k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff; + + return k; +} + +#undef major +#undef minor + + static char * +findhint(name, major, minor, preferred_path) + char *name; + int major, minor; + char *preferred_path; +{ + struct hints_bucket *bp; + + bp = hbuckets + (hinthash(name, major) % hheader->hh_nbucket); + + while (1) { + /* Sanity check */ + if (bp->hi_namex >= hheader->hh_strtab_sz) { + warnx("Bad name index: %#x\n", bp->hi_namex); + break; + } + if (bp->hi_pathx >= hheader->hh_strtab_sz) { + warnx("Bad path index: %#x\n", bp->hi_pathx); + break; + } + + if (strcmp(name, hstrtab + bp->hi_namex) == 0) { + /* It's `name', check version numbers */ + if (bp->hi_major == major && + (bp->hi_ndewey < 2 || bp->hi_minor >= minor)) { + if (preferred_path == NULL || + strcmp(preferred_path, + hstrtab + bp->hi_pathx) == 0) { + return hstrtab + bp->hi_pathx; + } + } + } + + if (bp->hi_next == -1) + break; + + /* Move on to next in bucket */ + bp = &hbuckets[bp->hi_next]; + } + + /* No hints available for name */ + return NULL; +} + + static char * +rtfindlib(name, major, minor, usehints) + char *name; + int major, minor; + int *usehints; +{ + char *cp, *ld_path = ld_library_path; + int realminor; + + if (hheader == NULL) + maphints(); + + if (!HINTS_VALID || !(*usehints)) + goto lose; + + if (ld_path != NULL) { + /* Prefer paths from LD_LIBRARY_PATH */ + while ((cp = strsep(&ld_path, ":")) != NULL) { + + cp = findhint(name, major, minor, cp); + if (ld_path) + *(ld_path-1) = ':'; + if (cp) + return cp; + } + /* Not found in hints, try directory search */ + realminor = -1; + cp = (char *)findshlib(name, &major, &realminor, 0); + if (cp && realminor >= minor) + return cp; + } + + /* No LD_LIBRARY_PATH or lib not found in there; check default */ + cp = findhint(name, major, minor, NULL); + if (cp) + return cp; + +lose: + /* No hints available for name */ + *usehints = 0; + realminor = -1; + cp = (char *)findshlib(name, &major, &realminor, 0); + if (cp) { + if (realminor < minor && getenv("LD_SUPPRESS_WARNINGS") == NULL) + warnx("warning: lib%s.so.%d.%d: " + "minor version < %d expected, using it anyway", + name, major, realminor, minor); + return cp; + } + generror ("Can't find shared library \"%s\"", + name); + return NULL; +} + +static struct somap_private dlmap_private = { + 0, + (struct so_map *)0, + 0, +#ifdef SUN_COMPAT + 0, +#endif +}; + +static struct so_map dlmap = { + (caddr_t)0, + "internal", + (struct so_map *)0, + (struct sod *)0, + (caddr_t)0, + (u_int)0, + (struct _dynamic *)0, + (caddr_t)&dlmap_private +}; + +/* + * Buffer for error messages and a pointer that is set to point to the buffer + * when a error occurs. It acts as a last error flag, being set to NULL + * after an error is returned. + */ +#define DLERROR_BUF_SIZE 512 +static char dlerror_buf [DLERROR_BUF_SIZE]; +static char *dlerror_msg = NULL; + + + static void * +__dlopen(name, mode) + char *name; + int mode; +{ + struct sod *sodp; + struct so_map *smp; + + /* + * A NULL argument returns the current set of mapped objects. + */ + if (name == NULL) { + LM_PRIVATE(link_map_head)->spd_refcount++; + return link_map_head; + } + if ((sodp = (struct sod *)malloc(sizeof(struct sod))) == NULL) { + generror ("malloc failed: %s", strerror (errno)); + return NULL; + } + + sodp->sod_name = (long)strdup(name); + sodp->sod_library = 0; + sodp->sod_major = sodp->sod_minor = 0; + + if ((smp = load_object(sodp, &dlmap, RTLD_DL, 1)) == NULL) { +#ifdef DEBUG + xprintf("%s: %s\n", name, dlerror_buf); +#endif + return NULL; + } + + /* + * If this was newly loaded, call the _init() function in the + * object as per manpage. + */ + if (LM_PRIVATE(smp)->spd_refcount == 1) + call_map(smp, "__init"); + + return smp; +} + + static int +__dlclose(fd) + void *fd; +{ + struct so_map *smp = (struct so_map *)fd; + +#ifdef DEBUG + xprintf("dlclose(%s): refcount = %d\n", smp->som_path, + LM_PRIVATE(smp)->spd_refcount); +#endif + + if (smp == NULL) { + generror("NULL argument to dlclose"); + return -1; + } + + if (LM_PRIVATE(smp)->spd_refcount > 1) { + LM_PRIVATE(smp)->spd_refcount--; + return 0; + } + + /* + * Call the function _fini() in the object as per manpage. + */ + call_map(smp, "__fini"); + + free((void*) smp->som_sod->sod_name); + free(smp->som_sod); + if (unload_object(smp) < 0) + return -1; + + return 0; +} + + static void * +__dlsym(fd, sym) + void *fd; + char *sym; +{ + struct so_map *smp = (struct so_map *)fd, *src_map = NULL; + struct nzlist *np; + long addr; + + /* + * Restrict search to passed map if dlopen()ed. + */ + if (smp && LM_PRIVATE(smp)->spd_flags & RTLD_DL) + src_map = smp; + + np = lookup(sym, &src_map, 1); + if (np == NULL) { + generror ("Symbol \"%s\" not found", sym); + return NULL; + } + /* Fixup jmpslot so future calls transfer directly to target */ + addr = np->nz_value; + if (src_map) + addr += (long)src_map->som_addr; + + return (void *)addr; +} + + static char * +__dlerror __P((void)) +{ + char *err; + + err = dlerror_msg; + dlerror_msg = NULL; /* Next call will return NULL */ + + return err; +} + + static void +__dlexit __P((void)) +{ + struct so_map *smp; + + for (smp = link_map_head; smp; smp = smp->som_next) { + if (LM_PRIVATE(smp)->spd_flags & (RTLD_RTLD|RTLD_MAIN)) + continue; + call_map(smp, ".fini"); + } +} + +/* + * Generate an error message that can be later be retrieved via dlerror. + */ +static void +#if __STDC__ +generror(char *fmt, ...) +#else +generror(fmt, va_alist) +char *fmt; +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vsnprintf (dlerror_buf, DLERROR_BUF_SIZE, fmt, ap); + dlerror_msg = dlerror_buf; + + va_end(ap); +} + +void +#if __STDC__ +xprintf(char *fmt, ...) +#else +xprintf(fmt, va_alist) +char *fmt; +#endif +{ + char buf[256]; + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + + vsprintf(buf, fmt, ap); + (void)write(1, buf, strlen(buf)); + va_end(ap); +} + diff --git a/libexec/rtld-aout/shlib.c b/libexec/rtld-aout/shlib.c new file mode 100644 index 0000000..f256508 --- /dev/null +++ b/libexec/rtld-aout/shlib.c @@ -0,0 +1,279 @@ +/* + * Copyright (c) 1993 Paul Kranenburg + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Paul Kranenburg. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: shlib.c,v 1.12 1995/03/04 17:46:09 nate Exp $ + */ + +#include <sys/param.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/time.h> +#include <err.h> +#include <fcntl.h> +#include <string.h> +#include <ctype.h> +#include <dirent.h> +#include <a.out.h> + +#include "ld.h" + +#ifdef SUNOS4 +char *strsep(); +#endif + +/* + * Standard directories to search for files specified by -l. + */ +#ifndef STANDARD_SEARCH_DIRS +#define STANDARD_SEARCH_DIRS "/usr/lib" +#endif + +/* + * Actual vector of library search directories, + * including `-L'ed and LD_LIBARAY_PATH spec'd ones. + */ +char **search_dirs; +int n_search_dirs; + +char *standard_search_dirs[] = { + STANDARD_SEARCH_DIRS +}; + + +void +add_search_dir(name) + char *name; +{ + n_search_dirs++; + search_dirs = (char **) + xrealloc(search_dirs, n_search_dirs * sizeof(char *)); + search_dirs[n_search_dirs - 1] = strdup(name); +} + +void +add_search_path(path) +char *path; +{ + register char *cp; + + if (path == NULL) + return; + + /* Add search directories from `paths' */ + while ((cp = strsep(&path, ":")) != NULL) { + add_search_dir(cp); + if (path) + *(path-1) = ':'; + } +} + +void +std_search_path() +{ + int i, n; + + /* Append standard search directories */ + n = sizeof standard_search_dirs / sizeof standard_search_dirs[0]; + for (i = 0; i < n; i++) + add_search_dir(standard_search_dirs[i]); +} + +/* + * Return true if CP points to a valid dewey number. + * Decode and leave the result in the array DEWEY. + * Return the number of decoded entries in DEWEY. + */ + +int +getdewey(dewey, cp) +int dewey[]; +char *cp; +{ + int i, n; + + for (n = 0, i = 0; i < MAXDEWEY; i++) { + if (*cp == '\0') + break; + + if (*cp == '.') cp++; + if (!isdigit(*cp)) + return 0; + + dewey[n++] = strtol(cp, &cp, 10); + } + + return n; +} + +/* + * Compare two dewey arrays. + * Return -1 if `d1' represents a smaller value than `d2'. + * Return 1 if `d1' represents a greater value than `d2'. + * Return 0 if equal. + */ +int +cmpndewey(d1, n1, d2, n2) +int d1[], d2[]; +int n1, n2; +{ + register int i; + + for (i = 0; i < n1 && i < n2; i++) { + if (d1[i] < d2[i]) + return -1; + if (d1[i] > d2[i]) + return 1; + } + + if (n1 == n2) + return 0; + + if (i == n1) + return -1; + + if (i == n2) + return 1; + + errx(1, "cmpndewey: cant happen"); + return 0; +} + +/* + * Search directories for a shared library matching the given + * major and minor version numbers. + * + * MAJOR == -1 && MINOR == -1 --> find highest version + * MAJOR != -1 && MINOR == -1 --> find highest minor version + * MAJOR == -1 && MINOR != -1 --> invalid + * MAJOR != -1 && MINOR != -1 --> find highest micro version + */ + +/* Not interested in devices right now... */ +#undef major +#undef minor + +char * +findshlib(name, majorp, minorp, do_dot_a) +char *name; +int *majorp, *minorp; +int do_dot_a; +{ + int dewey[MAXDEWEY]; + int ndewey; + int tmp[MAXDEWEY]; + int i; + int len; + char *lname, *path = NULL; + int major = *majorp, minor = *minorp; + + len = strlen(name); + lname = (char *)alloca(len + sizeof("lib")); + sprintf(lname, "lib%s", name); + len += 3; + + ndewey = 0; + + for (i = 0; i < n_search_dirs; i++) { + DIR *dd = opendir(search_dirs[i]); + struct dirent *dp; + int found_dot_a = 0; + + if (dd == NULL) + continue; + + while ((dp = readdir(dd)) != NULL) { + int n, might_take_it = 0; + + if (do_dot_a && path == NULL && + dp->d_namlen == len + 2 && + strncmp(dp->d_name, lname, len) == 0 && + (dp->d_name+len)[0] == '.' && + (dp->d_name+len)[1] == 'a') { + + path = concat(search_dirs[i], "/", dp->d_name); + found_dot_a = 1; + } + + if (dp->d_namlen < len + 4) + continue; + if (strncmp(dp->d_name, lname, len) != 0) + continue; + if (strncmp(dp->d_name+len, ".so.", 4) != 0) + continue; + + if ((n = getdewey(tmp, dp->d_name+len+4)) == 0) + continue; + + if (major != -1 && found_dot_a) { /* XXX */ + free(path); + path = NULL; + found_dot_a = 0; + } + + if (major == -1 && minor == -1) { + might_take_it = 1; + } else if (major != -1 && minor == -1) { + if (tmp[0] == major) + might_take_it = 1; + } else if (major != -1 && minor != -1) { + if (tmp[0] == major) + if (n == 1 || tmp[1] >= minor) + might_take_it = 1; + } + + if (!might_take_it) + continue; + + if (cmpndewey(tmp, n, dewey, ndewey) <= 0) + continue; + + /* We have a better version */ + if (path) + free(path); + path = concat(search_dirs[i], "/", dp->d_name); + found_dot_a = 0; + bcopy(tmp, dewey, sizeof(dewey)); + ndewey = n; + *majorp = dewey[0]; + *minorp = dewey[1]; + } + closedir(dd); + + if (found_dot_a) + /* + * There's a .a archive here. + */ + return path; + } + + return path; +} diff --git a/libexec/rtld-elf/rtld.1 b/libexec/rtld-elf/rtld.1 new file mode 100644 index 0000000..2b3c75a --- /dev/null +++ b/libexec/rtld-elf/rtld.1 @@ -0,0 +1,144 @@ +.\" $Id$ +.\" +.\" Copyright (c) 1995 Paul Kranenburg +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Paul Kranenburg. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd June 27, 1995 +.Dt RTLD 1 +.Os FreeBSD +.Sh NAME +.Nm ld.so +.Nd run-time link-editor +.Sh DESCRIPTION +.Nm +is a self-contained, position independent program image providing run-time +support for loading and link-editing shared objects into a process' +address space. It uses the data structures +.Po +see +.Xr link 5 +.Pc +contained within dynamically linked programs to determine which shared +libraries are needed and loads them at a convenient virtual address +using the +.Xr mmap 2 +system call. +.Pp +After all shared libraries have been succesfully loaded, +.Nm +proceeds to resolve external references from both the main program and +all objects loaded. A mechanism is provided for initialisation routines +to be called, on a per-object basis, giving a shared object an opportunity +to perfrom any extra set-up, before execution of the program proper begins. +This is useful for C++ libraries that contain static constrictors. +.Pp +.Nm +is itself a shared object that is initially loaded by the startup module +.Em crt0 . +Since +.Xr a.out 5 +formats do not provide easy access to the file header from within a running +process, +.Em crt0 +uses the special symbol +.Va _DYNAMIC +to determine whether a program is in fact dynamically linked or not. Whenever +the linker +.Xr ld 1 +has relocated this symbol to a location other then 0, +.Em crt0 +assumes the services of +.Nm +are needed +.Po +see +.Xr link 5 +for details +.Pc \&. +.Em crt0 +passes control to +.Nm +\&'s entry point before the program's +.Fn main +routine is called. Thus, +.Nm +can complete the link-editing process before the dynamic program calls upon +services of any dynamic library. +.Pp +To quickly locate the required shared objects in the filesystem, +.Nm +may use a +.Dq hints +file, prepared by the +.Xr ldconfig 8 +utility, in which the full path specification of the shared objects can be +looked up by hashing on the 3-tuple +.Ao +library-name, major-version-number, minor-version-number +.Ac \&. +.Pp +.Nm +recognises a number of environment variables that can be used to modify +its behaviour as follows: +.Pp +.Bl -tag -width "LD_TRACE_LOADED_OBJECTS" +.It Ev LD_LIBRARY_PATH +A colon separated list of directories, overriding the default search path +for shared libraries. +.It Ev LD_WARN_NON_PURE_CODE +When set, issue a warning whenever a link-editing operation requires +modification of the text segment of some loaded object. This is usually +indicative of an incorrectly built library. +.It Ev LD_SUPPRESS_WARNINGS +When set, no warning messages of any kind are issued. Normally, a warning +is given if satisfactorily versioned library could not be found. +.It Ev LD_TRACE_LOADED_OBJECTS +When set, causes +.Nm +to exit after loading the shared objects and printing a summary which includes +the absolute pathnames of all objects, to standard output. +.It Ev LD_NO_INTERN_SEARCH +When set, +.Nm +does not process any internal search paths that were recorded in the +executable. +.It Ev LD_NOSTD_PATH +When set, do not include a set of built-in standard directory paths for +searching. This might be useful when running on a system with a completely +non-standard filesystem layout. +.El +.Pp +.Sh FILES +/var/run/ld.so.hints +.Pp +.Sh SEE ALSO +.Xr ld 1 +.Xr ldconfig 8 +.Xr link 5 +.Sh HISTORY +The shared library model employed first appeared in SunOS 4.0 diff --git a/libexec/talkd/Makefile b/libexec/talkd/Makefile new file mode 100644 index 0000000..4b4a8399 --- /dev/null +++ b/libexec/talkd/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= ntalkd +SRCS= talkd.c announce.c process.c table.c print.c ttymsg.c +.PATH: ${.CURDIR}/../../usr.bin/wall +MAN8= talkd.8 + +.include <bsd.prog.mk> diff --git a/libexec/talkd/announce.c b/libexec/talkd/announce.c new file mode 100644 index 0000000..db03bbf --- /dev/null +++ b/libexec/talkd/announce.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)announce.c 8.2 (Berkeley) 1/7/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <protocols/talkd.h> +#include <sgtty.h> +#include <errno.h> +#include <syslog.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <paths.h> + +extern char hostname[]; + +/* + * Announce an invitation to talk. + */ + +/* + * See if the user is accepting messages. If so, announce that + * a talk is requested. + */ +announce(request, remote_machine) + CTL_MSG *request; + char *remote_machine; +{ + char full_tty[32]; + FILE *tf; + struct stat stbuf; + + (void)snprintf(full_tty, sizeof(full_tty), + "%s%s", _PATH_DEV, request->r_tty); + if (stat(full_tty, &stbuf) < 0 || (stbuf.st_mode&020) == 0) + return (PERMISSION_DENIED); + return (print_mesg(request->r_tty, tf, request, remote_machine)); +} + +#define max(a,b) ( (a) > (b) ? (a) : (b) ) +#define N_LINES 5 +#define N_CHARS 120 + +/* + * Build a block of characters containing the message. + * It is sent blank filled and in a single block to + * try to keep the message in one piece if the recipient + * in in vi at the time + */ +print_mesg(tty, tf, request, remote_machine) + char *tty; + FILE *tf; + CTL_MSG *request; + char *remote_machine; +{ + struct timeval clock; + struct timezone zone; + struct tm *localtime(); + struct tm *localclock; + struct iovec iovec; + char line_buf[N_LINES][N_CHARS]; + int sizes[N_LINES]; + char big_buf[N_LINES*N_CHARS]; + char *bptr, *lptr, *ttymsg(); + int i, j, max_size; + + i = 0; + max_size = 0; + gettimeofday(&clock, &zone); + localclock = localtime( &clock.tv_sec ); + (void)sprintf(line_buf[i], " "); + sizes[i] = strlen(line_buf[i]); + max_size = max(max_size, sizes[i]); + i++; + (void)sprintf(line_buf[i], "Message from Talk_Daemon@%s at %d:%02d ...", + hostname, localclock->tm_hour , localclock->tm_min ); + sizes[i] = strlen(line_buf[i]); + max_size = max(max_size, sizes[i]); + i++; + (void)sprintf(line_buf[i], "talk: connection requested by %s@%s", + request->l_name, remote_machine); + sizes[i] = strlen(line_buf[i]); + max_size = max(max_size, sizes[i]); + i++; + (void)sprintf(line_buf[i], "talk: respond with: talk %s@%s", + request->l_name, remote_machine); + sizes[i] = strlen(line_buf[i]); + max_size = max(max_size, sizes[i]); + i++; + (void)sprintf(line_buf[i], " "); + sizes[i] = strlen(line_buf[i]); + max_size = max(max_size, sizes[i]); + i++; + bptr = big_buf; + *bptr++ = '\007'; /* send something to wake them up */ + *bptr++ = '\r'; /* add a \r in case of raw mode */ + *bptr++ = '\n'; + for (i = 0; i < N_LINES; i++) { + /* copy the line into the big buffer */ + lptr = line_buf[i]; + while (*lptr != '\0') + *(bptr++) = *(lptr++); + /* pad out the rest of the lines with blanks */ + for (j = sizes[i]; j < max_size + 2; j++) + *(bptr++) = ' '; + *(bptr++) = '\r'; /* add a \r in case of raw mode */ + *(bptr++) = '\n'; + } + *bptr = '\0'; + iovec.iov_base = big_buf; + iovec.iov_len = bptr - big_buf; + /* + * we choose a timeout of RING_WAIT-5 seconds so that we don't + * stack up processes trying to write messages to a tty + * that is permanently blocked. + */ + if (ttymsg(&iovec, 1, tty, RING_WAIT - 5) != NULL) + return (FAILED); + + return (SUCCESS); +} diff --git a/libexec/talkd/print.c b/libexec/talkd/print.c new file mode 100644 index 0000000..0769d97 --- /dev/null +++ b/libexec/talkd/print.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* debug print routines */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <protocols/talkd.h> +#include <syslog.h> +#include <stdio.h> + +static char *types[] = + { "leave_invite", "look_up", "delete", "announce" }; +#define NTYPES (sizeof (types) / sizeof (types[0])) +static char *answers[] = + { "success", "not_here", "failed", "machine_unknown", "permission_denied", + "unknown_request", "badversion", "badaddr", "badctladdr" }; +#define NANSWERS (sizeof (answers) / sizeof (answers[0])) + +print_request(cp, mp) + char *cp; + register CTL_MSG *mp; +{ + char tbuf[80], *tp; + + if (mp->type > NTYPES) { + (void)sprintf(tbuf, "type %d", mp->type); + tp = tbuf; + } else + tp = types[mp->type]; + syslog(LOG_DEBUG, "%s: %s: id %d, l_user %s, r_user %s, r_tty %s", + cp, tp, mp->id_num, mp->l_name, mp->r_name, mp->r_tty); +} + +print_response(cp, rp) + char *cp; + register CTL_RESPONSE *rp; +{ + char tbuf[80], *tp, abuf[80], *ap; + + if (rp->type > NTYPES) { + (void)sprintf(tbuf, "type %d", rp->type); + tp = tbuf; + } else + tp = types[rp->type]; + if (rp->answer > NANSWERS) { + (void)sprintf(abuf, "answer %d", rp->answer); + ap = abuf; + } else + ap = answers[rp->answer]; + syslog(LOG_DEBUG, "%s: %s: %s, id %d", cp, tp, ap, ntohl(rp->id_num)); +} diff --git a/libexec/talkd/process.c b/libexec/talkd/process.c new file mode 100644 index 0000000..a83492b --- /dev/null +++ b/libexec/talkd/process.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)process.c 8.2 (Berkeley) 11/16/93"; +#endif /* not lint */ + +/* + * process.c handles the requests, which can be of three types: + * ANNOUNCE - announce to a user that a talk is wanted + * LEAVE_INVITE - insert the request into the table + * LOOK_UP - look up to see if a request is waiting in + * in the table for the local user + * DELETE - delete invitation + */ +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <protocols/talkd.h> +#include <netdb.h> +#include <syslog.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <paths.h> + +CTL_MSG *find_request(); +CTL_MSG *find_match(); + +process_request(mp, rp) + register CTL_MSG *mp; + register CTL_RESPONSE *rp; +{ + register CTL_MSG *ptr; + extern int debug; + char *s; + + rp->vers = TALK_VERSION; + rp->type = mp->type; + rp->id_num = htonl(0); + if (mp->vers != TALK_VERSION) { + syslog(LOG_WARNING, "Bad protocol version %d", mp->vers); + rp->answer = BADVERSION; + return; + } + mp->id_num = ntohl(mp->id_num); + mp->addr.sa_family = ntohs(mp->addr.sa_family); + if (mp->addr.sa_family != AF_INET) { + syslog(LOG_WARNING, "Bad address, family %d", + mp->addr.sa_family); + rp->answer = BADADDR; + return; + } + mp->ctl_addr.sa_family = ntohs(mp->ctl_addr.sa_family); + if (mp->ctl_addr.sa_family != AF_INET) { + syslog(LOG_WARNING, "Bad control address, family %d", + mp->ctl_addr.sa_family); + rp->answer = BADCTLADDR; + return; + } + for (s = mp->l_name; *s; s++) + if (!isprint(*s)) { + syslog(LOG_NOTICE, "Illegal user name. Aborting"); + rp->answer = FAILED; + return; + } + mp->pid = ntohl(mp->pid); + if (debug) + print_request("process_request", mp); + switch (mp->type) { + + case ANNOUNCE: + do_announce(mp, rp); + break; + + case LEAVE_INVITE: + ptr = find_request(mp); + if (ptr != (CTL_MSG *)0) { + rp->id_num = htonl(ptr->id_num); + rp->answer = SUCCESS; + } else + insert_table(mp, rp); + break; + + case LOOK_UP: + ptr = find_match(mp); + if (ptr != (CTL_MSG *)0) { + rp->id_num = htonl(ptr->id_num); + rp->addr = ptr->addr; + rp->addr.sa_family = htons(ptr->addr.sa_family); + rp->answer = SUCCESS; + } else + rp->answer = NOT_HERE; + break; + + case DELETE: + rp->answer = delete_invite(mp->id_num); + break; + + default: + rp->answer = UNKNOWN_REQUEST; + break; + } + if (debug) + print_response("process_request", rp); +} + +do_announce(mp, rp) + register CTL_MSG *mp; + CTL_RESPONSE *rp; +{ + struct hostent *hp; + CTL_MSG *ptr; + int result; + + /* see if the user is logged */ + result = find_user(mp->r_name, mp->r_tty); + if (result != SUCCESS) { + rp->answer = result; + return; + } +#define satosin(sa) ((struct sockaddr_in *)(sa)) + hp = gethostbyaddr((char *)&satosin(&mp->ctl_addr)->sin_addr, + sizeof (struct in_addr), AF_INET); + if (hp == (struct hostent *)0) { + rp->answer = MACHINE_UNKNOWN; + return; + } + ptr = find_request(mp); + if (ptr == (CTL_MSG *) 0) { + insert_table(mp, rp); + rp->answer = announce(mp, hp->h_name); + return; + } + if (mp->id_num > ptr->id_num) { + /* + * This is an explicit re-announce, so update the id_num + * field to avoid duplicates and re-announce the talk. + */ + ptr->id_num = new_id(); + rp->id_num = htonl(ptr->id_num); + rp->answer = announce(mp, hp->h_name); + } else { + /* a duplicated request, so ignore it */ + rp->id_num = htonl(ptr->id_num); + rp->answer = SUCCESS; + } +} + +#include <utmp.h> + +/* + * Search utmp for the local user + */ +find_user(name, tty) + char *name, *tty; +{ + struct utmp ubuf; + int status; + FILE *fd; + struct stat statb; + char line[sizeof(ubuf.ut_line) + 1]; + char ftty[sizeof(_PATH_DEV) - 1 + sizeof(line)]; + + if ((fd = fopen(_PATH_UTMP, "r")) == NULL) { + fprintf(stderr, "talkd: can't read %s.\n", _PATH_UTMP); + return (FAILED); + } +#define SCMPN(a, b) strncmp(a, b, sizeof (a)) + status = NOT_HERE; + (void) strcpy(ftty, _PATH_DEV); + while (fread((char *) &ubuf, sizeof ubuf, 1, fd) == 1) + if (SCMPN(ubuf.ut_name, name) == 0) { + strncpy(line, ubuf.ut_line, sizeof(ubuf.ut_line)); + line[sizeof(ubuf.ut_line)] = '\0'; + if (*tty == '\0') { + status = PERMISSION_DENIED; + /* no particular tty was requested */ + (void) strcpy(ftty + sizeof(_PATH_DEV) - 1, + line); + if (stat(ftty, &statb) == 0) { + if (!(statb.st_mode & 020)) + continue; + (void) strcpy(tty, line); + status = SUCCESS; + break; + } + } + if (strcmp(line, tty) == 0) { + status = SUCCESS; + break; + } + } + fclose(fd); + return (status); +} diff --git a/libexec/talkd/table.c b/libexec/talkd/table.c new file mode 100644 index 0000000..ef57206 --- /dev/null +++ b/libexec/talkd/table.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)table.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * Routines to handle insertion, deletion, etc on the table + * of requests kept by the daemon. Nothing fancy here, linear + * search on a double-linked list. A time is kept with each + * entry so that overly old invitations can be eliminated. + * + * Consider this a mis-guided attempt at modularity + */ +#include <sys/param.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <protocols/talkd.h> +#include <syslog.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define MAX_ID 16000 /* << 2^15 so I don't have sign troubles */ + +#define NIL ((TABLE_ENTRY *)0) + +extern int debug; +struct timeval tp; +struct timezone txp; + +typedef struct table_entry TABLE_ENTRY; + +struct table_entry { + CTL_MSG request; + long time; + TABLE_ENTRY *next; + TABLE_ENTRY *last; +}; + +TABLE_ENTRY *table = NIL; +CTL_MSG *find_request(); +CTL_MSG *find_match(); + +/* + * Look in the table for an invitation that matches the current + * request looking for an invitation + */ +CTL_MSG * +find_match(request) + register CTL_MSG *request; +{ + register TABLE_ENTRY *ptr; + time_t current_time; + + gettimeofday(&tp, &txp); + current_time = tp.tv_sec; + if (debug) + print_request("find_match", request); + for (ptr = table; ptr != NIL; ptr = ptr->next) { + if ((ptr->time - current_time) > MAX_LIFE) { + /* the entry is too old */ + if (debug) + print_request("deleting expired entry", + &ptr->request); + delete(ptr); + continue; + } + if (debug) + print_request("", &ptr->request); + if (strcmp(request->l_name, ptr->request.r_name) == 0 && + strcmp(request->r_name, ptr->request.l_name) == 0 && + ptr->request.type == LEAVE_INVITE) + return (&ptr->request); + } + return ((CTL_MSG *)0); +} + +/* + * Look for an identical request, as opposed to a complimentary + * one as find_match does + */ +CTL_MSG * +find_request(request) + register CTL_MSG *request; +{ + register TABLE_ENTRY *ptr; + time_t current_time; + + gettimeofday(&tp, &txp); + current_time = tp.tv_sec; + /* + * See if this is a repeated message, and check for + * out of date entries in the table while we are it. + */ + if (debug) + print_request("find_request", request); + for (ptr = table; ptr != NIL; ptr = ptr->next) { + if ((ptr->time - current_time) > MAX_LIFE) { + /* the entry is too old */ + if (debug) + print_request("deleting expired entry", + &ptr->request); + delete(ptr); + continue; + } + if (debug) + print_request("", &ptr->request); + if (strcmp(request->r_name, ptr->request.r_name) == 0 && + strcmp(request->l_name, ptr->request.l_name) == 0 && + request->type == ptr->request.type && + request->pid == ptr->request.pid) { + /* update the time if we 'touch' it */ + ptr->time = current_time; + return (&ptr->request); + } + } + return ((CTL_MSG *)0); +} + +insert_table(request, response) + CTL_MSG *request; + CTL_RESPONSE *response; +{ + register TABLE_ENTRY *ptr; + time_t current_time; + + gettimeofday(&tp, &txp); + current_time = tp.tv_sec; + request->id_num = new_id(); + response->id_num = htonl(request->id_num); + /* insert a new entry into the top of the list */ + ptr = (TABLE_ENTRY *)malloc(sizeof(TABLE_ENTRY)); + if (ptr == NIL) { + syslog(LOG_ERR, "insert_table: Out of memory"); + _exit(1); + } + ptr->time = current_time; + ptr->request = *request; + ptr->next = table; + if (ptr->next != NIL) + ptr->next->last = ptr; + ptr->last = NIL; + table = ptr; +} + +/* + * Generate a unique non-zero sequence number + */ +new_id() +{ + static int current_id = 0; + + current_id = (current_id + 1) % MAX_ID; + /* 0 is reserved, helps to pick up bugs */ + if (current_id == 0) + current_id = 1; + return (current_id); +} + +/* + * Delete the invitation with id 'id_num' + */ +delete_invite(id_num) + int id_num; +{ + register TABLE_ENTRY *ptr; + + ptr = table; + if (debug) + syslog(LOG_DEBUG, "delete_invite(%d)", id_num); + for (ptr = table; ptr != NIL; ptr = ptr->next) { + if (ptr->request.id_num == id_num) + break; + if (debug) + print_request("", &ptr->request); + } + if (ptr != NIL) { + delete(ptr); + return (SUCCESS); + } + return (NOT_HERE); +} + +/* + * Classic delete from a double-linked list + */ +delete(ptr) + register TABLE_ENTRY *ptr; +{ + + if (debug) + print_request("delete", &ptr->request); + if (table == ptr) + table = ptr->next; + else if (ptr->last != NIL) + ptr->last->next = ptr->next; + if (ptr->next != NIL) + ptr->next->last = ptr->last; + free((char *)ptr); +} diff --git a/libexec/talkd/talkd.8 b/libexec/talkd/talkd.8 new file mode 100644 index 0000000..36dfb28 --- /dev/null +++ b/libexec/talkd/talkd.8 @@ -0,0 +1,75 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)talkd.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt TALKD 8 +.Os BSD 4.3 +.Sh NAME +.Nm talkd +.Nd remote user communication server +.Sh SYNOPSIS +.Nm talkd +.Sh DESCRIPTION +.Nm Talkd +is the server that notifies a user that someone else wants to +initiate a conversation. +It acts as a repository of invitations, responding to requests +by clients wishing to rendezvous to hold a conversation. +In normal operation, a client, the caller, +initiates a rendezvous by sending a +.Tn CTL_MSG +to the server of +type +.Tn LOOK_UP +(see +.Aq Pa protocols/talkd.h ) . +This causes the server to search its invitation +tables to check if an invitation currently exists for the caller +(to speak to the callee specified in the message). +If the lookup fails, +the caller then sends an +.Tn ANNOUNCE +message causing the server to +broadcast an announcement on the callee's login ports requesting contact. +When the callee responds, the local server uses the +recorded invitation to respond with the appropriate rendezvous +address and the caller and callee client programs establish a +stream connection through which the conversation takes place. +.Sh SEE ALSO +.Xr talk 1 , +.Xr write 1 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/libexec/talkd/talkd.c b/libexec/talkd/talkd.c new file mode 100644 index 0000000..9fd5950 --- /dev/null +++ b/libexec/talkd/talkd.c @@ -0,0 +1,128 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)talkd.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * The top level of the daemon, the format is heavily borrowed + * from rwhod.c. Basically: find out who and where you are; + * disconnect all descriptors and ttys, and then endless + * loop on waiting for and processing requests + */ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/param.h> +#include <protocols/talkd.h> +#include <signal.h> +#include <syslog.h> +#include <time.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <paths.h> + +CTL_MSG request; +CTL_RESPONSE response; + +int sockt; +int debug = 0; +void timeout(); +long lastmsgtime; + +char hostname[MAXHOSTNAMELEN]; + +#define TIMEOUT 30 +#define MAXIDLE 120 + +main(argc, argv) + int argc; + char *argv[]; +{ + register CTL_MSG *mp = &request; + int cc; + + if (getuid()) { + fprintf(stderr, "%s: getuid: not super-user\n", argv[0]); + exit(1); + } + openlog("talkd", LOG_PID, LOG_DAEMON); + if (gethostname(hostname, sizeof (hostname) - 1) < 0) { + syslog(LOG_ERR, "gethostname: %m"); + _exit(1); + } + if (chdir(_PATH_DEV) < 0) { + syslog(LOG_ERR, "chdir: %s: %m", _PATH_DEV); + _exit(1); + } + if (argc > 1 && strcmp(argv[1], "-d") == 0) + debug = 1; + signal(SIGALRM, timeout); + alarm(TIMEOUT); + for (;;) { + extern int errno; + + cc = recv(0, (char *)mp, sizeof (*mp), 0); + if (cc != sizeof (*mp)) { + if (cc < 0 && errno != EINTR) + syslog(LOG_WARNING, "recv: %m"); + continue; + } + lastmsgtime = time(0); + process_request(mp, &response); + /* can block here, is this what I want? */ + cc = sendto(sockt, (char *)&response, + sizeof (response), 0, (struct sockaddr *)&mp->ctl_addr, + sizeof (mp->ctl_addr)); + if (cc != sizeof (response)) + syslog(LOG_WARNING, "sendto: %m"); + } +} + +void +timeout() +{ + + if (time(0) - lastmsgtime >= MAXIDLE) + _exit(0); + alarm(TIMEOUT); +} diff --git a/libexec/telnetd/Makefile b/libexec/telnetd/Makefile new file mode 100644 index 0000000..0164c5d --- /dev/null +++ b/libexec/telnetd/Makefile @@ -0,0 +1,18 @@ +# @(#)Makefile 8.2 (Berkeley) 12/15/93 + +PROG= telnetd +CFLAGS+=-DLINEMODE -DUSE_TERMIO -DDIAGNOSTICS +#CFLAGS+=-DKLUDGELINEMODE +CFLAGS+=-DOLD_ENVIRON -DENV_HACK +CFLAGS+=-I${.CURDIR}/../../lib +#CFLAGS+=-DAUTHENTICATION -DENCRYPTION +SRCS= global.c slc.c state.c sys_term.c telnetd.c \ + termstat.c utility.c +#SRCS+= authenc.c +DPADD= ${LIBUTIL} ${LIBTERMCAP} +LDADD= -lutil -ltermcap -ltelnet +#LDADD+= -lkrb -ldes +MAN8= telnetd.8 + + +.include <bsd.prog.mk> diff --git a/libexec/telnetd/authenc.c b/libexec/telnetd/authenc.c new file mode 100644 index 0000000..fcd17fc --- /dev/null +++ b/libexec/telnetd/authenc.c @@ -0,0 +1,91 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)authenc.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#if defined(AUTHENTICATION) || defined(ENCRYPTION) +#include "telnetd.h" +#include <libtelnet/misc.h> + + int +net_write(str, len) + unsigned char *str; + int len; +{ + if (nfrontp + len < netobuf + BUFSIZ) { + bcopy((void *)str, (void *)nfrontp, len); + nfrontp += len; + return(len); + } + return(0); +} + + void +net_encrypt() +{ +#ifdef ENCRYPTION + char *s = (nclearto > nbackp) ? nclearto : nbackp; + if (s < nfrontp && encrypt_output) { + (*encrypt_output)((unsigned char *)s, nfrontp - s); + } + nclearto = nfrontp; +#endif /* ENCRYPTION */ +} + + int +telnet_spin() +{ + ttloop(); + return(0); +} + + char * +telnet_getenv(val) + char *val; +{ + extern char *getenv(); + return(getenv(val)); +} + + char * +telnet_gets(prompt, result, length, echo) + char *prompt; + char *result; + int length; + int echo; +{ + return((char *)0); +} +#endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */ diff --git a/libexec/telnetd/defs.h b/libexec/telnetd/defs.h new file mode 100644 index 0000000..a73d4a6 --- /dev/null +++ b/libexec/telnetd/defs.h @@ -0,0 +1,296 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)defs.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * Telnet server defines + */ +#include <sys/types.h> +#include <sys/param.h> + +#ifndef BSD +# define BSD 43 +#endif + +#if defined(CRAY) && !defined(LINEMODE) +# define SYSV_TERMIO +# define LINEMODE +# define KLUDGELINEMODE +# define DIAGNOSTICS +# if defined(UNICOS50) && !defined(UNICOS5) +# define UNICOS5 +# endif +# if !defined(UNICOS5) +# define BFTPDAEMON +# define HAS_IP_TOS +# endif +#endif /* CRAY */ +#if defined(UNICOS5) && !defined(NO_SETSID) +# define NO_SETSID +#endif + +#if defined(PRINTOPTIONS) && defined(DIAGNOSTICS) +#define TELOPTS +#define TELCMDS +#define SLC_NAMES +#endif + +#if defined(SYSV_TERMIO) && !defined(USE_TERMIO) +# define USE_TERMIO +#endif + +#include <sys/socket.h> +#ifndef CRAY +#include <sys/wait.h> +#endif /* CRAY */ +#include <fcntl.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/time.h> +#ifndef FILIO_H +#include <sys/ioctl.h> +#else +#include <sys/filio.h> +#endif + +#include <netinet/in.h> + +#include <arpa/telnet.h> + +#include <stdio.h> +#ifdef __STDC__ +#include <stdlib.h> +#endif +#include <signal.h> +#include <errno.h> +#include <netdb.h> +#include <syslog.h> +#ifndef LOG_DAEMON +#define LOG_DAEMON 0 +#endif +#ifndef LOG_ODELAY +#define LOG_ODELAY 0 +#endif +#include <ctype.h> +#ifndef NO_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif + +#ifndef USE_TERMIO +#include <sgtty.h> +#else +# ifdef SYSV_TERMIO +# include <termio.h> +# else +# include <termios.h> +# endif +#endif +#if !defined(USE_TERMIO) || defined(NO_CC_T) +typedef unsigned char cc_t; +#endif + +#ifdef __STDC__ +#include <unistd.h> +#endif + +#ifndef _POSIX_VDISABLE +# ifdef VDISABLE +# define _POSIX_VDISABLE VDISABLE +# else +# define _POSIX_VDISABLE ((unsigned char)'\377') +# endif +#endif + + +#ifdef CRAY +# ifdef CRAY1 +# include <sys/pty.h> +# ifndef FD_ZERO +# include <sys/select.h> +# endif /* FD_ZERO */ +# endif /* CRAY1 */ + +#include <memory.h> +#endif /* CRAY */ + +#ifdef __hpux +#include <sys/ptyio.h> +#endif + +#if !defined(TIOCSCTTY) && defined(TCSETCTTY) +# define TIOCSCTTY TCSETCTTY +#endif + +#ifndef FD_SET +#ifndef HAVE_fd_set +typedef struct fd_set { int fds_bits[1]; } fd_set; +#endif + +#define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n))) +#define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n))) +#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n))) +#define FD_ZERO(p) ((p)->fds_bits[0] = 0) +#endif /* FD_SET */ + +/* + * I/O data buffers defines + */ +#define NETSLOP 64 +#ifdef CRAY +#undef BUFSIZ +#define BUFSIZ 2048 +#endif + +#define NIACCUM(c) { *netip++ = c; \ + ncc++; \ + } + +/* clock manipulations */ +#define settimer(x) (clocks.x = ++clocks.system) +#define sequenceIs(x,y) (clocks.x < clocks.y) + +/* + * Linemode support states, in decreasing order of importance + */ +#define REAL_LINEMODE 0x04 +#define KLUDGE_OK 0x03 +#define NO_AUTOKLUDGE 0x02 +#define KLUDGE_LINEMODE 0x01 +#define NO_LINEMODE 0x00 + +/* + * Structures of information for each special character function. + */ +typedef struct { + unsigned char flag; /* the flags for this function */ + cc_t val; /* the value of the special character */ +} slcent, *Slcent; + +typedef struct { + slcent defset; /* the default settings */ + slcent current; /* the current settings */ + cc_t *sptr; /* a pointer to the char in */ + /* system data structures */ +} slcfun, *Slcfun; + +#ifdef DIAGNOSTICS +/* + * Diagnostics capabilities + */ +#define TD_REPORT 0x01 /* Report operations to client */ +#define TD_EXERCISE 0x02 /* Exercise client's implementation */ +#define TD_NETDATA 0x04 /* Display received data stream */ +#define TD_PTYDATA 0x08 /* Display data passed to pty */ +#define TD_OPTIONS 0x10 /* Report just telnet options */ +#endif /* DIAGNOSTICS */ + +/* + * We keep track of each side of the option negotiation. + */ + +#define MY_STATE_WILL 0x01 +#define MY_WANT_STATE_WILL 0x02 +#define MY_STATE_DO 0x04 +#define MY_WANT_STATE_DO 0x08 + +/* + * Macros to check the current state of things + */ + +#define my_state_is_do(opt) (options[opt]&MY_STATE_DO) +#define my_state_is_will(opt) (options[opt]&MY_STATE_WILL) +#define my_want_state_is_do(opt) (options[opt]&MY_WANT_STATE_DO) +#define my_want_state_is_will(opt) (options[opt]&MY_WANT_STATE_WILL) + +#define my_state_is_dont(opt) (!my_state_is_do(opt)) +#define my_state_is_wont(opt) (!my_state_is_will(opt)) +#define my_want_state_is_dont(opt) (!my_want_state_is_do(opt)) +#define my_want_state_is_wont(opt) (!my_want_state_is_will(opt)) + +#define set_my_state_do(opt) (options[opt] |= MY_STATE_DO) +#define set_my_state_will(opt) (options[opt] |= MY_STATE_WILL) +#define set_my_want_state_do(opt) (options[opt] |= MY_WANT_STATE_DO) +#define set_my_want_state_will(opt) (options[opt] |= MY_WANT_STATE_WILL) + +#define set_my_state_dont(opt) (options[opt] &= ~MY_STATE_DO) +#define set_my_state_wont(opt) (options[opt] &= ~MY_STATE_WILL) +#define set_my_want_state_dont(opt) (options[opt] &= ~MY_WANT_STATE_DO) +#define set_my_want_state_wont(opt) (options[opt] &= ~MY_WANT_STATE_WILL) + +/* + * Tricky code here. What we want to know is if the MY_STATE_WILL + * and MY_WANT_STATE_WILL bits have the same value. Since the two + * bits are adjacent, a little arithmatic will show that by adding + * in the lower bit, the upper bit will be set if the two bits were + * different, and clear if they were the same. + */ +#define my_will_wont_is_changing(opt) \ + ((options[opt]+MY_STATE_WILL) & MY_WANT_STATE_WILL) + +#define my_do_dont_is_changing(opt) \ + ((options[opt]+MY_STATE_DO) & MY_WANT_STATE_DO) + +/* + * Make everything symetrical + */ + +#define HIS_STATE_WILL MY_STATE_DO +#define HIS_WANT_STATE_WILL MY_WANT_STATE_DO +#define HIS_STATE_DO MY_STATE_WILL +#define HIS_WANT_STATE_DO MY_WANT_STATE_WILL + +#define his_state_is_do my_state_is_will +#define his_state_is_will my_state_is_do +#define his_want_state_is_do my_want_state_is_will +#define his_want_state_is_will my_want_state_is_do + +#define his_state_is_dont my_state_is_wont +#define his_state_is_wont my_state_is_dont +#define his_want_state_is_dont my_want_state_is_wont +#define his_want_state_is_wont my_want_state_is_dont + +#define set_his_state_do set_my_state_will +#define set_his_state_will set_my_state_do +#define set_his_want_state_do set_my_want_state_will +#define set_his_want_state_will set_my_want_state_do + +#define set_his_state_dont set_my_state_wont +#define set_his_state_wont set_my_state_dont +#define set_his_want_state_dont set_my_want_state_wont +#define set_his_want_state_wont set_my_want_state_dont + +#define his_will_wont_is_changing my_do_dont_is_changing +#define his_do_dont_is_changing my_will_wont_is_changing diff --git a/libexec/telnetd/ext.h b/libexec/telnetd/ext.h new file mode 100644 index 0000000..2460cf6 --- /dev/null +++ b/libexec/telnetd/ext.h @@ -0,0 +1,235 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ext.h 8.2 (Berkeley) 12/15/93 + */ + +/* + * Telnet server variable declarations + */ +extern char options[256]; +extern char do_dont_resp[256]; +extern char will_wont_resp[256]; +extern int linemode; /* linemode on/off */ +#ifdef LINEMODE +extern int uselinemode; /* what linemode to use (on/off) */ +extern int editmode; /* edit modes in use */ +extern int useeditmode; /* edit modes to use */ +extern int alwayslinemode; /* command line option */ +# ifdef KLUDGELINEMODE +extern int lmodetype; /* Client support for linemode */ +# endif /* KLUDGELINEMODE */ +#endif /* LINEMODE */ +extern int flowmode; /* current flow control state */ +extern int restartany; /* restart output on any character state */ +#ifdef DIAGNOSTICS +extern int diagnostic; /* telnet diagnostic capabilities */ +#endif /* DIAGNOSTICS */ +#ifdef BFTPDAEMON +extern int bftpd; /* behave as bftp daemon */ +#endif /* BFTPDAEMON */ +#if defined(SecurID) +extern int require_SecurID; +#endif +#if defined(AUTHENTICATION) +extern int auth_level; +#endif + +extern slcfun slctab[NSLC + 1]; /* slc mapping table */ + +char *terminaltype; + +/* + * I/O data buffers, pointers, and counters. + */ +extern char ptyobuf[BUFSIZ+NETSLOP], *pfrontp, *pbackp; + +extern char netibuf[BUFSIZ], *netip; + +extern char netobuf[BUFSIZ+NETSLOP], *nfrontp, *nbackp; +extern char *neturg; /* one past last bye of urgent data */ + +extern int pcc, ncc; + +#if defined(CRAY2) && defined(UNICOS5) +extern int unpcc; /* characters left unprocessed by CRAY-2 terminal routine */ +extern char *unptyip; /* pointer to remaining characters in buffer */ +#endif + +extern int pty, net; +extern char *line; +extern int SYNCHing; /* we are in TELNET SYNCH mode */ + +#ifndef P +# ifdef __STDC__ +# define P(x) x +# else +# define P(x) () +# endif +#endif + +extern void + _termstat P((void)), + add_slc P((int, int, int)), + check_slc P((void)), + change_slc P((int, int, int)), + cleanup P((int)), + clientstat P((int, int, int)), + copy_termbuf P((char *, int)), + deferslc P((void)), + defer_terminit P((void)), + do_opt_slc P((unsigned char *, int)), + doeof P((void)), + dooption P((int)), + dontoption P((int)), + edithost P((char *, char *)), + fatal P((int, char *)), + fatalperror P((int, char *)), + get_slc_defaults P((void)), + init_env P((void)), + init_termbuf P((void)), + interrupt P((void)), + localstat P((void)), + flowstat P((void)), + netclear P((void)), + netflush P((void)), +#ifdef DIAGNOSTICS + printoption P((char *, int)), + printdata P((char *, char *, int)), + printsub P((int, unsigned char *, int)), +#endif + ptyflush P((void)), + putchr P((int)), + putf P((char *, char *)), + recv_ayt P((void)), + send_do P((int, int)), + send_dont P((int, int)), + send_slc P((void)), + send_status P((void)), + send_will P((int, int)), + send_wont P((int, int)), + sendbrk P((void)), + sendsusp P((void)), + set_termbuf P((void)), + start_login P((char *, int, char *)), + start_slc P((int)), +#if defined(AUTHENTICATION) + start_slave P((char *)), +#else + start_slave P((char *, int, char *)), +#endif + suboption P((void)), + telrcv P((void)), + ttloop P((void)), + tty_binaryin P((int)), + tty_binaryout P((int)); + +extern int + end_slc P((unsigned char **)), + getnpty P((void)), +#ifndef convex + getpty P((int *)), +#endif + login_tty P((int)), + spcset P((int, cc_t *, cc_t **)), + stilloob P((int)), + terminit P((void)), + termstat P((void)), + tty_flowmode P((void)), + tty_restartany P((void)), + tty_isbinaryin P((void)), + tty_isbinaryout P((void)), + tty_iscrnl P((void)), + tty_isecho P((void)), + tty_isediting P((void)), + tty_islitecho P((void)), + tty_isnewmap P((void)), + tty_israw P((void)), + tty_issofttab P((void)), + tty_istrapsig P((void)), + tty_linemode P((void)); + +extern void + tty_rspeed P((int)), + tty_setecho P((int)), + tty_setedit P((int)), + tty_setlinemode P((int)), + tty_setlitecho P((int)), + tty_setsig P((int)), + tty_setsofttab P((int)), + tty_tspeed P((int)), + willoption P((int)), + wontoption P((int)), + writenet P((unsigned char *, int)); + + + +/* + * The following are some clocks used to decide how to interpret + * the relationship between various variables. + */ + +extern struct { + int + system, /* what the current time is */ + echotoggle, /* last time user entered echo character */ + modenegotiated, /* last time operating mode negotiated */ + didnetreceive, /* last time we read data from network */ + ttypesubopt, /* ttype subopt is received */ + tspeedsubopt, /* tspeed subopt is received */ + environsubopt, /* environ subopt is received */ + oenvironsubopt, /* old environ subopt is received */ + xdisplocsubopt, /* xdisploc subopt is received */ + baseline, /* time started to do timed action */ + gotDM; /* when did we last see a data mark */ +} clocks; + + +#if defined(CRAY2) && defined(UNICOS5) +extern int needtermstat; +#endif + +#ifndef DEFAULT_IM +# ifdef CRAY +# define DEFAULT_IM "\r\n\r\nCray UNICOS (%h) (%t)\r\n\r\r\n\r" +# else +# ifdef sun +# define DEFAULT_IM "\r\n\r\nSunOS UNIX (%h) (%t)\r\n\r\r\n\r" +# else +# ifdef ultrix +# define DEFAULT_IM "\r\n\r\nULTRIX (%h) (%t)\r\n\r\r\n\r" +# else +# define DEFAULT_IM "\r\n\r\nFreeBSD (%h) (%t)\r\n\r\r\n\r" +# endif +# endif +# endif +#endif diff --git a/libexec/telnetd/global.c b/libexec/telnetd/global.c new file mode 100644 index 0000000..af21acc --- /dev/null +++ b/libexec/telnetd/global.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)global.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * Allocate global variables. We do this + * by including the header file that defines + * them all as externs, but first we define + * the keyword "extern" to be nothing, so that + * we will actually allocate the space. + */ + +#include "defs.h" +#define extern +#include "ext.h" diff --git a/libexec/telnetd/pathnames.h b/libexec/telnetd/pathnames.h new file mode 100644 index 0000000..4e14a88 --- /dev/null +++ b/libexec/telnetd/pathnames.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + */ + +#if BSD > 43 + +# include <paths.h> + +# ifndef _PATH_LOGIN +# define _PATH_LOGIN "/usr/bin/login" +# endif + +#else + +# define _PATH_TTY "/dev/tty" +# ifndef _PATH_LOGIN +# define _PATH_LOGIN "/bin/login" +# endif + +#endif + +#ifdef BFTPDAEMON +#define BFTPPATH "/usr/ucb/bftp" +#endif /* BFTPDAEMON */ diff --git a/libexec/telnetd/slc.c b/libexec/telnetd/slc.c new file mode 100644 index 0000000..a03bd9d --- /dev/null +++ b/libexec/telnetd/slc.c @@ -0,0 +1,493 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)slc.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include "telnetd.h" + +#ifdef LINEMODE +/* + * local varibles + */ +static unsigned char *def_slcbuf = (unsigned char *)0; +static int def_slclen = 0; +static int slcchange; /* change to slc is requested */ +static unsigned char *slcptr; /* pointer into slc buffer */ +static unsigned char slcbuf[NSLC*6]; /* buffer for slc negotiation */ + +/* + * send_slc + * + * Write out the current special characters to the client. + */ + void +send_slc() +{ + register int i; + + /* + * Send out list of triplets of special characters + * to client. We only send info on the characters + * that are currently supported. + */ + for (i = 1; i <= NSLC; i++) { + if ((slctab[i].defset.flag & SLC_LEVELBITS) == SLC_NOSUPPORT) + continue; + add_slc((unsigned char)i, slctab[i].current.flag, + slctab[i].current.val); + } + +} /* end of send_slc */ + +/* + * default_slc + * + * Set pty special characters to all the defaults. + */ + void +default_slc() +{ + register int i; + + for (i = 1; i <= NSLC; i++) { + slctab[i].current.val = slctab[i].defset.val; + if (slctab[i].current.val == (cc_t)(_POSIX_VDISABLE)) + slctab[i].current.flag = SLC_NOSUPPORT; + else + slctab[i].current.flag = slctab[i].defset.flag; + if (slctab[i].sptr) { + *(slctab[i].sptr) = slctab[i].defset.val; + } + } + slcchange = 1; + +} /* end of default_slc */ +#endif /* LINEMODE */ + +/* + * get_slc_defaults + * + * Initialize the slc mapping table. + */ + void +get_slc_defaults() +{ + register int i; + + init_termbuf(); + + for (i = 1; i <= NSLC; i++) { + slctab[i].defset.flag = + spcset(i, &slctab[i].defset.val, &slctab[i].sptr); + slctab[i].current.flag = SLC_NOSUPPORT; + slctab[i].current.val = 0; + } + +} /* end of get_slc_defaults */ + +#ifdef LINEMODE +/* + * add_slc + * + * Add an slc triplet to the slc buffer. + */ + void +add_slc(func, flag, val) + register char func, flag; + register cc_t val; +{ + + if ((*slcptr++ = (unsigned char)func) == 0xff) + *slcptr++ = 0xff; + + if ((*slcptr++ = (unsigned char)flag) == 0xff) + *slcptr++ = 0xff; + + if ((*slcptr++ = (unsigned char)val) == 0xff) + *slcptr++ = 0xff; + +} /* end of add_slc */ + +/* + * start_slc + * + * Get ready to process incoming slc's and respond to them. + * + * The parameter getit is non-zero if it is necessary to grab a copy + * of the terminal control structures. + */ + void +start_slc(getit) + register int getit; +{ + + slcchange = 0; + if (getit) + init_termbuf(); + (void) sprintf((char *)slcbuf, "%c%c%c%c", + IAC, SB, TELOPT_LINEMODE, LM_SLC); + slcptr = slcbuf + 4; + +} /* end of start_slc */ + +/* + * end_slc + * + * Finish up the slc negotiation. If something to send, then send it. + */ + int +end_slc(bufp) + register unsigned char **bufp; +{ + register int len; + void netflush(); + + /* + * If a change has occured, store the new terminal control + * structures back to the terminal driver. + */ + if (slcchange) { + set_termbuf(); + } + + /* + * If the pty state has not yet been fully processed and there is a + * deferred slc request from the client, then do not send any + * sort of slc negotiation now. We will respond to the client's + * request very soon. + */ + if (def_slcbuf && (terminit() == 0)) { + return(0); + } + + if (slcptr > (slcbuf + 4)) { + if (bufp) { + *bufp = &slcbuf[4]; + return(slcptr - slcbuf - 4); + } else { + (void) sprintf((char *)slcptr, "%c%c", IAC, SE); + slcptr += 2; + len = slcptr - slcbuf; + writenet(slcbuf, len); + netflush(); /* force it out immediately */ + DIAG(TD_OPTIONS, printsub('>', slcbuf+2, len-2);); + } + } + return (0); + +} /* end of end_slc */ + +/* + * process_slc + * + * Figure out what to do about the client's slc + */ + void +process_slc(func, flag, val) + register unsigned char func, flag; + register cc_t val; +{ + register int hislevel, mylevel, ack; + + /* + * Ensure that we know something about this function + */ + if (func > NSLC) { + add_slc(func, SLC_NOSUPPORT, 0); + return; + } + + /* + * Process the special case requests of 0 SLC_DEFAULT 0 + * and 0 SLC_VARIABLE 0. Be a little forgiving here, don't + * worry about whether the value is actually 0 or not. + */ + if (func == 0) { + if ((flag = flag & SLC_LEVELBITS) == SLC_DEFAULT) { + default_slc(); + send_slc(); + } else if (flag == SLC_VARIABLE) { + send_slc(); + } + return; + } + + /* + * Appears to be a function that we know something about. So + * get on with it and see what we know. + */ + + hislevel = flag & SLC_LEVELBITS; + mylevel = slctab[func].current.flag & SLC_LEVELBITS; + ack = flag & SLC_ACK; + /* + * ignore the command if: + * the function value and level are the same as what we already have; + * or the level is the same and the ack bit is set + */ + if (hislevel == mylevel && (val == slctab[func].current.val || ack)) { + return; + } else if (ack) { + /* + * If we get here, we got an ack, but the levels don't match. + * This shouldn't happen. If it does, it is probably because + * we have sent two requests to set a variable without getting + * a response between them, and this is the first response. + * So, ignore it, and wait for the next response. + */ + return; + } else { + change_slc(func, flag, val); + } + +} /* end of process_slc */ + +/* + * change_slc + * + * Process a request to change one of our special characters. + * Compare client's request with what we are capable of supporting. + */ + void +change_slc(func, flag, val) + register char func, flag; + register cc_t val; +{ + register int hislevel, mylevel; + + hislevel = flag & SLC_LEVELBITS; + mylevel = slctab[func].defset.flag & SLC_LEVELBITS; + /* + * If client is setting a function to NOSUPPORT + * or DEFAULT, then we can easily and directly + * accomodate the request. + */ + if (hislevel == SLC_NOSUPPORT) { + slctab[func].current.flag = flag; + slctab[func].current.val = (cc_t)_POSIX_VDISABLE; + flag |= SLC_ACK; + add_slc(func, flag, val); + return; + } + if (hislevel == SLC_DEFAULT) { + /* + * Special case here. If client tells us to use + * the default on a function we don't support, then + * return NOSUPPORT instead of what we may have as a + * default level of DEFAULT. + */ + if (mylevel == SLC_DEFAULT) { + slctab[func].current.flag = SLC_NOSUPPORT; + } else { + slctab[func].current.flag = slctab[func].defset.flag; + } + slctab[func].current.val = slctab[func].defset.val; + add_slc(func, slctab[func].current.flag, + slctab[func].current.val); + return; + } + + /* + * Client wants us to change to a new value or he + * is telling us that he can't change to our value. + * Some of the slc's we support and can change, + * some we do support but can't change, + * and others we don't support at all. + * If we can change it then we have a pointer to + * the place to put the new value, so change it, + * otherwise, continue the negotiation. + */ + if (slctab[func].sptr) { + /* + * We can change this one. + */ + slctab[func].current.val = val; + *(slctab[func].sptr) = val; + slctab[func].current.flag = flag; + flag |= SLC_ACK; + slcchange = 1; + add_slc(func, flag, val); + } else { + /* + * It is not possible for us to support this + * request as he asks. + * + * If our level is DEFAULT, then just ack whatever was + * sent. + * + * If he can't change and we can't change, + * then degenerate to NOSUPPORT. + * + * Otherwise we send our level back to him, (CANTCHANGE + * or NOSUPPORT) and if CANTCHANGE, send + * our value as well. + */ + if (mylevel == SLC_DEFAULT) { + slctab[func].current.flag = flag; + slctab[func].current.val = val; + flag |= SLC_ACK; + } else if (hislevel == SLC_CANTCHANGE && + mylevel == SLC_CANTCHANGE) { + flag &= ~SLC_LEVELBITS; + flag |= SLC_NOSUPPORT; + slctab[func].current.flag = flag; + } else { + flag &= ~SLC_LEVELBITS; + flag |= mylevel; + slctab[func].current.flag = flag; + if (mylevel == SLC_CANTCHANGE) { + slctab[func].current.val = + slctab[func].defset.val; + val = slctab[func].current.val; + } + + } + add_slc(func, flag, val); + } + +} /* end of change_slc */ + +#if defined(USE_TERMIO) && (VEOF == VMIN) +cc_t oldeofc = '\004'; +#endif + +/* + * check_slc + * + * Check the special characters in use and notify the client if any have + * changed. Only those characters that are capable of being changed are + * likely to have changed. If a local change occurs, kick the support level + * and flags up to the defaults. + */ + void +check_slc() +{ + register int i; + + for (i = 1; i <= NSLC; i++) { +#if defined(USE_TERMIO) && (VEOF == VMIN) + /* + * In a perfect world this would be a neat little + * function. But in this world, we should not notify + * client of changes to the VEOF char when + * ICANON is off, because it is not representing + * a special character. + */ + if (i == SLC_EOF) { + if (!tty_isediting()) + continue; + else if (slctab[i].sptr) + oldeofc = *(slctab[i].sptr); + } +#endif /* defined(USE_TERMIO) && defined(SYSV_TERMIO) */ + if (slctab[i].sptr && + (*(slctab[i].sptr) != slctab[i].current.val)) { + slctab[i].current.val = *(slctab[i].sptr); + if (*(slctab[i].sptr) == (cc_t)_POSIX_VDISABLE) + slctab[i].current.flag = SLC_NOSUPPORT; + else + slctab[i].current.flag = slctab[i].defset.flag; + add_slc((unsigned char)i, slctab[i].current.flag, + slctab[i].current.val); + } + } + +} /* check_slc */ + +/* + * do_opt_slc + * + * Process an slc option buffer. Defer processing of incoming slc's + * until after the terminal state has been processed. Save the first slc + * request that comes along, but discard all others. + * + * ptr points to the beginning of the buffer, len is the length. + */ + void +do_opt_slc(ptr, len) + register unsigned char *ptr; + register int len; +{ + register unsigned char func, flag; + cc_t val; + register unsigned char *end = ptr + len; + + if (terminit()) { /* go ahead */ + while (ptr < end) { + func = *ptr++; + if (ptr >= end) break; + flag = *ptr++; + if (ptr >= end) break; + val = (cc_t)*ptr++; + + process_slc(func, flag, val); + + } + } else { + /* + * save this slc buffer if it is the first, otherwise dump + * it. + */ + if (def_slcbuf == (unsigned char *)0) { + def_slclen = len; + def_slcbuf = (unsigned char *)malloc((unsigned)len); + if (def_slcbuf == (unsigned char *)0) + return; /* too bad */ + bcopy(ptr, def_slcbuf, len); + } + } + +} /* end of do_opt_slc */ + +/* + * deferslc + * + * Do slc stuff that was deferred. + */ + void +deferslc() +{ + if (def_slcbuf) { + start_slc(1); + do_opt_slc(def_slcbuf, def_slclen); + (void) end_slc(0); + free(def_slcbuf); + def_slcbuf = (unsigned char *)0; + def_slclen = 0; + } + +} /* end of deferslc */ + +#endif /* LINEMODE */ diff --git a/libexec/telnetd/state.c b/libexec/telnetd/state.c new file mode 100644 index 0000000..db9fbe8 --- /dev/null +++ b/libexec/telnetd/state.c @@ -0,0 +1,1546 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)state.c 8.2 (Berkeley) 12/15/93"; +#endif /* not lint */ + +#include "telnetd.h" +#if defined(AUTHENTICATION) +#include <libtelnet/auth.h> +#endif + +unsigned char doopt[] = { IAC, DO, '%', 'c', 0 }; +unsigned char dont[] = { IAC, DONT, '%', 'c', 0 }; +unsigned char will[] = { IAC, WILL, '%', 'c', 0 }; +unsigned char wont[] = { IAC, WONT, '%', 'c', 0 }; +int not42 = 1; + +/* + * Buffer for sub-options, and macros + * for suboptions buffer manipulations + */ +unsigned char subbuffer[512], *subpointer= subbuffer, *subend= subbuffer; + +#define SB_CLEAR() subpointer = subbuffer +#define SB_TERM() { subend = subpointer; SB_CLEAR(); } +#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ + *subpointer++ = (c); \ + } +#define SB_GET() ((*subpointer++)&0xff) +#define SB_EOF() (subpointer >= subend) +#define SB_LEN() (subend - subpointer) + +#ifdef ENV_HACK +unsigned char *subsave; +#define SB_SAVE() subsave = subpointer; +#define SB_RESTORE() subpointer = subsave; +#endif + + +/* + * State for recv fsm + */ +#define TS_DATA 0 /* base state */ +#define TS_IAC 1 /* look for double IAC's */ +#define TS_CR 2 /* CR-LF ->'s CR */ +#define TS_SB 3 /* throw away begin's... */ +#define TS_SE 4 /* ...end's (suboption negotiation) */ +#define TS_WILL 5 /* will option negotiation */ +#define TS_WONT 6 /* wont " */ +#define TS_DO 7 /* do " */ +#define TS_DONT 8 /* dont " */ + + void +telrcv() +{ + register int c; + static int state = TS_DATA; +#if defined(CRAY2) && defined(UNICOS5) + char *opfrontp = pfrontp; +#endif + + while (ncc > 0) { + if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) + break; + c = *netip++ & 0377, ncc--; + switch (state) { + + case TS_CR: + state = TS_DATA; + /* Strip off \n or \0 after a \r */ + if ((c == 0) || (c == '\n')) { + break; + } + /* FALL THROUGH */ + + case TS_DATA: + if (c == IAC) { + state = TS_IAC; + break; + } + /* + * We now map \r\n ==> \r for pragmatic reasons. + * Many client implementations send \r\n when + * the user hits the CarriageReturn key. + * + * We USED to map \r\n ==> \n, since \r\n says + * that we want to be in column 1 of the next + * printable line, and \n is the standard + * unix way of saying that (\r is only good + * if CRMOD is set, which it normally is). + */ + if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) { + int nc = *netip; +#ifdef LINEMODE + /* + * If we are operating in linemode, + * convert to local end-of-line. + */ + if (linemode && (ncc > 0) && (('\n' == nc) || + ((0 == nc) && tty_iscrnl())) ) { + netip++; ncc--; + c = '\n'; + } else +#endif + { + state = TS_CR; + } + } + *pfrontp++ = c; + break; + + case TS_IAC: +gotiac: switch (c) { + + /* + * Send the process on the pty side an + * interrupt. Do this with a NULL or + * interrupt char; depending on the tty mode. + */ + case IP: + DIAG(TD_OPTIONS, + printoption("td: recv IAC", c)); + interrupt(); + break; + + case BREAK: + DIAG(TD_OPTIONS, + printoption("td: recv IAC", c)); + sendbrk(); + break; + + /* + * Are You There? + */ + case AYT: + DIAG(TD_OPTIONS, + printoption("td: recv IAC", c)); + recv_ayt(); + break; + + /* + * Abort Output + */ + case AO: + { + DIAG(TD_OPTIONS, + printoption("td: recv IAC", c)); + ptyflush(); /* half-hearted */ + init_termbuf(); + + if (slctab[SLC_AO].sptr && + *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) { + *pfrontp++ = + (unsigned char)*slctab[SLC_AO].sptr; + } + + netclear(); /* clear buffer back */ + *nfrontp++ = IAC; + *nfrontp++ = DM; + neturg = nfrontp-1; /* off by one XXX */ + DIAG(TD_OPTIONS, + printoption("td: send IAC", DM)); + break; + } + + /* + * Erase Character and + * Erase Line + */ + case EC: + case EL: + { + cc_t ch; + + DIAG(TD_OPTIONS, + printoption("td: recv IAC", c)); + ptyflush(); /* half-hearted */ + init_termbuf(); + if (c == EC) + ch = *slctab[SLC_EC].sptr; + else + ch = *slctab[SLC_EL].sptr; + if (ch != (cc_t)(_POSIX_VDISABLE)) + *pfrontp++ = (unsigned char)ch; + break; + } + + /* + * Check for urgent data... + */ + case DM: + DIAG(TD_OPTIONS, + printoption("td: recv IAC", c)); + SYNCHing = stilloob(net); + settimer(gotDM); + break; + + + /* + * Begin option subnegotiation... + */ + case SB: + state = TS_SB; + SB_CLEAR(); + continue; + + case WILL: + state = TS_WILL; + continue; + + case WONT: + state = TS_WONT; + continue; + + case DO: + state = TS_DO; + continue; + + case DONT: + state = TS_DONT; + continue; + case EOR: + if (his_state_is_will(TELOPT_EOR)) + doeof(); + break; + + /* + * Handle RFC 10xx Telnet linemode option additions + * to command stream (EOF, SUSP, ABORT). + */ + case xEOF: + doeof(); + break; + + case SUSP: + sendsusp(); + break; + + case ABORT: + sendbrk(); + break; + + case IAC: + *pfrontp++ = c; + break; + } + state = TS_DATA; + break; + + case TS_SB: + if (c == IAC) { + state = TS_SE; + } else { + SB_ACCUM(c); + } + break; + + case TS_SE: + if (c != SE) { + if (c != IAC) { + /* + * bad form of suboption negotiation. + * handle it in such a way as to avoid + * damage to local state. Parse + * suboption buffer found so far, + * then treat remaining stream as + * another command sequence. + */ + + /* for DIAGNOSTICS */ + SB_ACCUM(IAC); + SB_ACCUM(c); + subpointer -= 2; + + SB_TERM(); + suboption(); + state = TS_IAC; + goto gotiac; + } + SB_ACCUM(c); + state = TS_SB; + } else { + /* for DIAGNOSTICS */ + SB_ACCUM(IAC); + SB_ACCUM(SE); + subpointer -= 2; + + SB_TERM(); + suboption(); /* handle sub-option */ + state = TS_DATA; + } + break; + + case TS_WILL: + willoption(c); + state = TS_DATA; + continue; + + case TS_WONT: + wontoption(c); + state = TS_DATA; + continue; + + case TS_DO: + dooption(c); + state = TS_DATA; + continue; + + case TS_DONT: + dontoption(c); + state = TS_DATA; + continue; + + default: + syslog(LOG_ERR, "telnetd: panic state=%d\n", state); + printf("telnetd: panic state=%d\n", state); + exit(1); + } + } +#if defined(CRAY2) && defined(UNICOS5) + if (!linemode) { + char xptyobuf[BUFSIZ+NETSLOP]; + char xbuf2[BUFSIZ]; + register char *cp; + int n = pfrontp - opfrontp, oc; + bcopy(opfrontp, xptyobuf, n); + pfrontp = opfrontp; + pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP, + xbuf2, &oc, BUFSIZ); + for (cp = xbuf2; oc > 0; --oc) + if ((*nfrontp++ = *cp++) == IAC) + *nfrontp++ = IAC; + } +#endif /* defined(CRAY2) && defined(UNICOS5) */ +} /* end of telrcv */ + +/* + * The will/wont/do/dont state machines are based on Dave Borman's + * Telnet option processing state machine. + * + * These correspond to the following states: + * my_state = the last negotiated state + * want_state = what I want the state to go to + * want_resp = how many requests I have sent + * All state defaults are negative, and resp defaults to 0. + * + * When initiating a request to change state to new_state: + * + * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) { + * do nothing; + * } else { + * want_state = new_state; + * send new_state; + * want_resp++; + * } + * + * When receiving new_state: + * + * if (want_resp) { + * want_resp--; + * if (want_resp && (new_state == my_state)) + * want_resp--; + * } + * if ((want_resp == 0) && (new_state != want_state)) { + * if (ok_to_switch_to new_state) + * want_state = new_state; + * else + * want_resp++; + * send want_state; + * } + * my_state = new_state; + * + * Note that new_state is implied in these functions by the function itself. + * will and do imply positive new_state, wont and dont imply negative. + * + * Finally, there is one catch. If we send a negative response to a + * positive request, my_state will be the positive while want_state will + * remain negative. my_state will revert to negative when the negative + * acknowlegment arrives from the peer. Thus, my_state generally tells + * us not only the last negotiated state, but also tells us what the peer + * wants to be doing as well. It is important to understand this difference + * as we may wish to be processing data streams based on our desired state + * (want_state) or based on what the peer thinks the state is (my_state). + * + * This all works fine because if the peer sends a positive request, the data + * that we receive prior to negative acknowlegment will probably be affected + * by the positive state, and we can process it as such (if we can; if we + * can't then it really doesn't matter). If it is that important, then the + * peer probably should be buffering until this option state negotiation + * is complete. + * + */ + void +send_do(option, init) + int option, init; +{ + if (init) { + if ((do_dont_resp[option] == 0 && his_state_is_will(option)) || + his_want_state_is_will(option)) + return; + /* + * Special case for TELOPT_TM: We send a DO, but pretend + * that we sent a DONT, so that we can send more DOs if + * we want to. + */ + if (option == TELOPT_TM) + set_his_want_state_wont(option); + else + set_his_want_state_will(option); + do_dont_resp[option]++; + } + (void) sprintf(nfrontp, (char *)doopt, option); + nfrontp += sizeof (dont) - 2; + + DIAG(TD_OPTIONS, printoption("td: send do", option)); +} + +#ifdef AUTHENTICATION +extern void auth_request(); +#endif +#ifdef LINEMODE +extern void doclientstat(); +#endif + + void +willoption(option) + int option; +{ + int changeok = 0; + void (*func)() = 0; + + /* + * process input from peer. + */ + + DIAG(TD_OPTIONS, printoption("td: recv will", option)); + + if (do_dont_resp[option]) { + do_dont_resp[option]--; + if (do_dont_resp[option] && his_state_is_will(option)) + do_dont_resp[option]--; + } + if (do_dont_resp[option] == 0) { + if (his_want_state_is_wont(option)) { + switch (option) { + + case TELOPT_BINARY: + init_termbuf(); + tty_binaryin(1); + set_termbuf(); + changeok++; + break; + + case TELOPT_ECHO: + /* + * See comments below for more info. + */ + not42 = 0; /* looks like a 4.2 system */ + break; + + case TELOPT_TM: +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + /* + * This telnetd implementation does not really + * support timing marks, it just uses them to + * support the kludge linemode stuff. If we + * receive a will or wont TM in response to our + * do TM request that may have been sent to + * determine kludge linemode support, process + * it, otherwise TM should get a negative + * response back. + */ + /* + * Handle the linemode kludge stuff. + * If we are not currently supporting any + * linemode at all, then we assume that this + * is the client telling us to use kludge + * linemode in response to our query. Set the + * linemode type that is to be supported, note + * that the client wishes to use linemode, and + * eat the will TM as though it never arrived. + */ + if (lmodetype < KLUDGE_LINEMODE) { + lmodetype = KLUDGE_LINEMODE; + clientstat(TELOPT_LINEMODE, WILL, 0); + send_wont(TELOPT_SGA, 1); + } else if (lmodetype == NO_AUTOKLUDGE) { + lmodetype = KLUDGE_OK; + } +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + /* + * We never respond to a WILL TM, and + * we leave the state WONT. + */ + return; + + case TELOPT_LFLOW: + /* + * If we are going to support flow control + * option, then don't worry peer that we can't + * change the flow control characters. + */ + slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; + slctab[SLC_XON].defset.flag |= SLC_DEFAULT; + slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; + slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT; + case TELOPT_TTYPE: + case TELOPT_SGA: + case TELOPT_NAWS: + case TELOPT_TSPEED: + case TELOPT_XDISPLOC: + case TELOPT_NEW_ENVIRON: + case TELOPT_OLD_ENVIRON: + changeok++; + break; + +#ifdef LINEMODE + case TELOPT_LINEMODE: +# ifdef KLUDGELINEMODE + /* + * Note client's desire to use linemode. + */ + lmodetype = REAL_LINEMODE; +# endif /* KLUDGELINEMODE */ + func = doclientstat; + changeok++; + break; +#endif /* LINEMODE */ + +#ifdef AUTHENTICATION + case TELOPT_AUTHENTICATION: + func = auth_request; + changeok++; + break; +#endif + + + default: + break; + } + if (changeok) { + set_his_want_state_will(option); + send_do(option, 0); + } else { + do_dont_resp[option]++; + send_dont(option, 0); + } + } else { + /* + * Option processing that should happen when + * we receive conformation of a change in + * state that we had requested. + */ + switch (option) { + case TELOPT_ECHO: + not42 = 0; /* looks like a 4.2 system */ + /* + * Egads, he responded "WILL ECHO". Turn + * it off right now! + */ + send_dont(option, 1); + /* + * "WILL ECHO". Kludge upon kludge! + * A 4.2 client is now echoing user input at + * the tty. This is probably undesireable and + * it should be stopped. The client will + * respond WONT TM to the DO TM that we send to + * check for kludge linemode. When the WONT TM + * arrives, linemode will be turned off and a + * change propogated to the pty. This change + * will cause us to process the new pty state + * in localstat(), which will notice that + * linemode is off and send a WILL ECHO + * so that we are properly in character mode and + * all is well. + */ + break; +#ifdef LINEMODE + case TELOPT_LINEMODE: +# ifdef KLUDGELINEMODE + /* + * Note client's desire to use linemode. + */ + lmodetype = REAL_LINEMODE; +# endif /* KLUDGELINEMODE */ + func = doclientstat; + break; +#endif /* LINEMODE */ + +#ifdef AUTHENTICATION + case TELOPT_AUTHENTICATION: + func = auth_request; + break; +#endif + + case TELOPT_LFLOW: + func = flowstat; + break; + } + } + } + set_his_state_will(option); + if (func) + (*func)(); +} /* end of willoption */ + + void +send_dont(option, init) + int option, init; +{ + if (init) { + if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) || + his_want_state_is_wont(option)) + return; + set_his_want_state_wont(option); + do_dont_resp[option]++; + } + (void) sprintf(nfrontp, (char *)dont, option); + nfrontp += sizeof (doopt) - 2; + + DIAG(TD_OPTIONS, printoption("td: send dont", option)); +} + + void +wontoption(option) + int option; +{ + /* + * Process client input. + */ + + DIAG(TD_OPTIONS, printoption("td: recv wont", option)); + + if (do_dont_resp[option]) { + do_dont_resp[option]--; + if (do_dont_resp[option] && his_state_is_wont(option)) + do_dont_resp[option]--; + } + if (do_dont_resp[option] == 0) { + if (his_want_state_is_will(option)) { + /* it is always ok to change to negative state */ + switch (option) { + case TELOPT_ECHO: + not42 = 1; /* doesn't seem to be a 4.2 system */ + break; + + case TELOPT_BINARY: + init_termbuf(); + tty_binaryin(0); + set_termbuf(); + break; + +#ifdef LINEMODE + case TELOPT_LINEMODE: +# ifdef KLUDGELINEMODE + /* + * If real linemode is supported, then client is + * asking to turn linemode off. + */ + if (lmodetype != REAL_LINEMODE) + break; + lmodetype = KLUDGE_LINEMODE; +# endif /* KLUDGELINEMODE */ + clientstat(TELOPT_LINEMODE, WONT, 0); + break; +#endif /* LINEMODE */ + + case TELOPT_TM: + /* + * If we get a WONT TM, and had sent a DO TM, + * don't respond with a DONT TM, just leave it + * as is. Short circut the state machine to + * achive this. + */ + set_his_want_state_wont(TELOPT_TM); + return; + + case TELOPT_LFLOW: + /* + * If we are not going to support flow control + * option, then let peer know that we can't + * change the flow control characters. + */ + slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; + slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; + slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; + slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; + break; + +#if defined(AUTHENTICATION) + case TELOPT_AUTHENTICATION: + auth_finished(0, AUTH_REJECT); + break; +#endif + + /* + * For options that we might spin waiting for + * sub-negotiation, if the client turns off the + * option rather than responding to the request, + * we have to treat it here as if we got a response + * to the sub-negotiation, (by updating the timers) + * so that we'll break out of the loop. + */ + case TELOPT_TTYPE: + settimer(ttypesubopt); + break; + + case TELOPT_TSPEED: + settimer(tspeedsubopt); + break; + + case TELOPT_XDISPLOC: + settimer(xdisplocsubopt); + break; + + case TELOPT_OLD_ENVIRON: + settimer(oenvironsubopt); + break; + + case TELOPT_NEW_ENVIRON: + settimer(environsubopt); + break; + + default: + break; + } + set_his_want_state_wont(option); + if (his_state_is_will(option)) + send_dont(option, 0); + } else { + switch (option) { + case TELOPT_TM: +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + if (lmodetype < NO_AUTOKLUDGE) { + lmodetype = NO_LINEMODE; + clientstat(TELOPT_LINEMODE, WONT, 0); + send_will(TELOPT_SGA, 1); + send_will(TELOPT_ECHO, 1); + } +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + break; + +#if defined(AUTHENTICATION) + case TELOPT_AUTHENTICATION: + auth_finished(0, AUTH_REJECT); + break; +#endif + default: + break; + } + } + } + set_his_state_wont(option); + +} /* end of wontoption */ + + void +send_will(option, init) + int option, init; +{ + if (init) { + if ((will_wont_resp[option] == 0 && my_state_is_will(option))|| + my_want_state_is_will(option)) + return; + set_my_want_state_will(option); + will_wont_resp[option]++; + } + (void) sprintf(nfrontp, (char *)will, option); + nfrontp += sizeof (doopt) - 2; + + DIAG(TD_OPTIONS, printoption("td: send will", option)); +} + +#if !defined(LINEMODE) || !defined(KLUDGELINEMODE) +/* + * When we get a DONT SGA, we will try once to turn it + * back on. If the other side responds DONT SGA, we + * leave it at that. This is so that when we talk to + * clients that understand KLUDGELINEMODE but not LINEMODE, + * we'll keep them in char-at-a-time mode. + */ +int turn_on_sga = 0; +#endif + + void +dooption(option) + int option; +{ + int changeok = 0; + + /* + * Process client input. + */ + + DIAG(TD_OPTIONS, printoption("td: recv do", option)); + + if (will_wont_resp[option]) { + will_wont_resp[option]--; + if (will_wont_resp[option] && my_state_is_will(option)) + will_wont_resp[option]--; + } + if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) { + switch (option) { + case TELOPT_ECHO: +#ifdef LINEMODE +# ifdef KLUDGELINEMODE + if (lmodetype == NO_LINEMODE) +# else + if (his_state_is_wont(TELOPT_LINEMODE)) +# endif +#endif + { + init_termbuf(); + tty_setecho(1); + set_termbuf(); + } + changeok++; + break; + + case TELOPT_BINARY: + init_termbuf(); + tty_binaryout(1); + set_termbuf(); + changeok++; + break; + + case TELOPT_SGA: +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + /* + * If kludge linemode is in use, then we must + * process an incoming do SGA for linemode + * purposes. + */ + if (lmodetype == KLUDGE_LINEMODE) { + /* + * Receipt of "do SGA" in kludge + * linemode is the peer asking us to + * turn off linemode. Make note of + * the request. + */ + clientstat(TELOPT_LINEMODE, WONT, 0); + /* + * If linemode did not get turned off + * then don't tell peer that we did. + * Breaking here forces a wont SGA to + * be returned. + */ + if (linemode) + break; + } +#else + turn_on_sga = 0; +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + changeok++; + break; + + case TELOPT_STATUS: + changeok++; + break; + + case TELOPT_TM: + /* + * Special case for TM. We send a WILL, but + * pretend we sent a WONT. + */ + send_will(option, 0); + set_my_want_state_wont(option); + set_my_state_wont(option); + return; + + case TELOPT_LOGOUT: + /* + * When we get a LOGOUT option, respond + * with a WILL LOGOUT, make sure that + * it gets written out to the network, + * and then just go away... + */ + set_my_want_state_will(TELOPT_LOGOUT); + send_will(TELOPT_LOGOUT, 0); + set_my_state_will(TELOPT_LOGOUT); + (void)netflush(); + cleanup(0); + /* NOT REACHED */ + break; + + case TELOPT_LINEMODE: + case TELOPT_TTYPE: + case TELOPT_NAWS: + case TELOPT_TSPEED: + case TELOPT_LFLOW: + case TELOPT_XDISPLOC: +#ifdef TELOPT_ENVIRON + case TELOPT_NEW_ENVIRON: +#endif + case TELOPT_OLD_ENVIRON: + default: + break; + } + if (changeok) { + set_my_want_state_will(option); + send_will(option, 0); + } else { + will_wont_resp[option]++; + send_wont(option, 0); + } + } + set_my_state_will(option); + +} /* end of dooption */ + + void +send_wont(option, init) + int option, init; +{ + if (init) { + if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) || + my_want_state_is_wont(option)) + return; + set_my_want_state_wont(option); + will_wont_resp[option]++; + } + (void) sprintf(nfrontp, (char *)wont, option); + nfrontp += sizeof (wont) - 2; + + DIAG(TD_OPTIONS, printoption("td: send wont", option)); +} + + void +dontoption(option) + int option; +{ + /* + * Process client input. + */ + + + DIAG(TD_OPTIONS, printoption("td: recv dont", option)); + + if (will_wont_resp[option]) { + will_wont_resp[option]--; + if (will_wont_resp[option] && my_state_is_wont(option)) + will_wont_resp[option]--; + } + if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) { + switch (option) { + case TELOPT_BINARY: + init_termbuf(); + tty_binaryout(0); + set_termbuf(); + break; + + case TELOPT_ECHO: /* we should stop echoing */ +#ifdef LINEMODE +# ifdef KLUDGELINEMODE + if ((lmodetype != REAL_LINEMODE) && + (lmodetype != KLUDGE_LINEMODE)) +# else + if (his_state_is_wont(TELOPT_LINEMODE)) +# endif +#endif + { + init_termbuf(); + tty_setecho(0); + set_termbuf(); + } + break; + + case TELOPT_SGA: +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + /* + * If kludge linemode is in use, then we + * must process an incoming do SGA for + * linemode purposes. + */ + if ((lmodetype == KLUDGE_LINEMODE) || + (lmodetype == KLUDGE_OK)) { + /* + * The client is asking us to turn + * linemode on. + */ + lmodetype = KLUDGE_LINEMODE; + clientstat(TELOPT_LINEMODE, WILL, 0); + /* + * If we did not turn line mode on, + * then what do we say? Will SGA? + * This violates design of telnet. + * Gross. Very Gross. + */ + } + break; +#else + set_my_want_state_wont(option); + if (my_state_is_will(option)) + send_wont(option, 0); + set_my_state_wont(option); + if (turn_on_sga ^= 1) + send_will(option, 1); + return; +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + + default: + break; + } + + set_my_want_state_wont(option); + if (my_state_is_will(option)) + send_wont(option, 0); + } + set_my_state_wont(option); + +} /* end of dontoption */ + +#ifdef ENV_HACK +int env_ovar = -1; +int env_ovalue = -1; +#else /* ENV_HACK */ +# define env_ovar OLD_ENV_VAR +# define env_ovalue OLD_ENV_VALUE +#endif /* ENV_HACK */ + +/* + * suboption() + * + * Look at the sub-option buffer, and try to be helpful to the other + * side. + * + * Currently we recognize: + * + * Terminal type is + * Linemode + * Window size + * Terminal speed + */ + void +suboption() +{ + register int subchar; + + DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);}); + + subchar = SB_GET(); + switch (subchar) { + case TELOPT_TSPEED: { + register int xspeed, rspeed; + + if (his_state_is_wont(TELOPT_TSPEED)) /* Ignore if option disabled */ + break; + + settimer(tspeedsubopt); + + if (SB_EOF() || SB_GET() != TELQUAL_IS) + return; + + xspeed = atoi((char *)subpointer); + + while (SB_GET() != ',' && !SB_EOF()); + if (SB_EOF()) + return; + + rspeed = atoi((char *)subpointer); + clientstat(TELOPT_TSPEED, xspeed, rspeed); + + break; + + } /* end of case TELOPT_TSPEED */ + + case TELOPT_TTYPE: { /* Yaaaay! */ + static char terminalname[41]; + + if (his_state_is_wont(TELOPT_TTYPE)) /* Ignore if option disabled */ + break; + settimer(ttypesubopt); + + if (SB_EOF() || SB_GET() != TELQUAL_IS) { + return; /* ??? XXX but, this is the most robust */ + } + + terminaltype = terminalname; + + while ((terminaltype < (terminalname + sizeof terminalname-1)) && + !SB_EOF()) { + register int c; + + c = SB_GET(); + if (isupper(c)) { + c = tolower(c); + } + *terminaltype++ = c; /* accumulate name */ + } + *terminaltype = 0; + terminaltype = terminalname; + break; + } /* end of case TELOPT_TTYPE */ + + case TELOPT_NAWS: { + register int xwinsize, ywinsize; + + if (his_state_is_wont(TELOPT_NAWS)) /* Ignore if option disabled */ + break; + + if (SB_EOF()) + return; + xwinsize = SB_GET() << 8; + if (SB_EOF()) + return; + xwinsize |= SB_GET(); + if (SB_EOF()) + return; + ywinsize = SB_GET() << 8; + if (SB_EOF()) + return; + ywinsize |= SB_GET(); + clientstat(TELOPT_NAWS, xwinsize, ywinsize); + + break; + + } /* end of case TELOPT_NAWS */ + +#ifdef LINEMODE + case TELOPT_LINEMODE: { + register int request; + + if (his_state_is_wont(TELOPT_LINEMODE)) /* Ignore if option disabled */ + break; + /* + * Process linemode suboptions. + */ + if (SB_EOF()) + break; /* garbage was sent */ + request = SB_GET(); /* get will/wont */ + + if (SB_EOF()) + break; /* another garbage check */ + + if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */ + /* + * Process suboption buffer of slc's + */ + start_slc(1); + do_opt_slc(subpointer, subend - subpointer); + (void) end_slc(0); + break; + } else if (request == LM_MODE) { + if (SB_EOF()) + return; + useeditmode = SB_GET(); /* get mode flag */ + clientstat(LM_MODE, 0, 0); + break; + } + + if (SB_EOF()) + break; + switch (SB_GET()) { /* what suboption? */ + case LM_FORWARDMASK: + /* + * According to spec, only server can send request for + * forwardmask, and client can only return a positive response. + * So don't worry about it. + */ + + default: + break; + } + break; + } /* end of case TELOPT_LINEMODE */ +#endif + case TELOPT_STATUS: { + int mode; + + if (SB_EOF()) + break; + mode = SB_GET(); + switch (mode) { + case TELQUAL_SEND: + if (my_state_is_will(TELOPT_STATUS)) + send_status(); + break; + + case TELQUAL_IS: + break; + + default: + break; + } + break; + } /* end of case TELOPT_STATUS */ + + case TELOPT_XDISPLOC: { + if (SB_EOF() || SB_GET() != TELQUAL_IS) + return; + settimer(xdisplocsubopt); + subpointer[SB_LEN()] = '\0'; + (void)setenv("DISPLAY", (char *)subpointer, 1); + break; + } /* end of case TELOPT_XDISPLOC */ + +#ifdef TELOPT_NEW_ENVIRON + case TELOPT_NEW_ENVIRON: +#endif + case TELOPT_OLD_ENVIRON: { + register int c; + register char *cp, *varp, *valp; + + if (SB_EOF()) + return; + c = SB_GET(); + if (c == TELQUAL_IS) { + if (subchar == TELOPT_OLD_ENVIRON) + settimer(oenvironsubopt); + else + settimer(environsubopt); + } else if (c != TELQUAL_INFO) { + return; + } + +#ifdef TELOPT_NEW_ENVIRON + if (subchar == TELOPT_NEW_ENVIRON) { + while (!SB_EOF()) { + c = SB_GET(); + if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR)) + break; + } + } else +#endif + { +#ifdef ENV_HACK + /* + * We only want to do this if we haven't already decided + * whether or not the other side has its VALUE and VAR + * reversed. + */ + if (env_ovar < 0) { + register int last = -1; /* invalid value */ + int empty = 0; + int got_var = 0, got_value = 0, got_uservar = 0; + + /* + * The other side might have its VALUE and VAR values + * reversed. To be interoperable, we need to determine + * which way it is. If the first recognized character + * is a VAR or VALUE, then that will tell us what + * type of client it is. If the fist recognized + * character is a USERVAR, then we continue scanning + * the suboption looking for two consecutive + * VAR or VALUE fields. We should not get two + * consecutive VALUE fields, so finding two + * consecutive VALUE or VAR fields will tell us + * what the client is. + */ + SB_SAVE(); + while (!SB_EOF()) { + c = SB_GET(); + switch(c) { + case OLD_ENV_VAR: + if (last < 0 || last == OLD_ENV_VAR + || (empty && (last == OLD_ENV_VALUE))) + goto env_ovar_ok; + got_var++; + last = OLD_ENV_VAR; + break; + case OLD_ENV_VALUE: + if (last < 0 || last == OLD_ENV_VALUE + || (empty && (last == OLD_ENV_VAR))) + goto env_ovar_wrong; + got_value++; + last = OLD_ENV_VALUE; + break; + case ENV_USERVAR: + /* count strings of USERVAR as one */ + if (last != ENV_USERVAR) + got_uservar++; + if (empty) { + if (last == OLD_ENV_VALUE) + goto env_ovar_ok; + if (last == OLD_ENV_VAR) + goto env_ovar_wrong; + } + last = ENV_USERVAR; + break; + case ENV_ESC: + if (!SB_EOF()) + c = SB_GET(); + /* FALL THROUGH */ + default: + empty = 0; + continue; + } + empty = 1; + } + if (empty) { + if (last == OLD_ENV_VALUE) + goto env_ovar_ok; + if (last == OLD_ENV_VAR) + goto env_ovar_wrong; + } + /* + * Ok, the first thing was a USERVAR, and there + * are not two consecutive VAR or VALUE commands, + * and none of the VAR or VALUE commands are empty. + * If the client has sent us a well-formed option, + * then the number of VALUEs received should always + * be less than or equal to the number of VARs and + * USERVARs received. + * + * If we got exactly as many VALUEs as VARs and + * USERVARs, the client has the same definitions. + * + * If we got exactly as many VARs as VALUEs and + * USERVARS, the client has reversed definitions. + */ + if (got_uservar + got_var == got_value) { + env_ovar_ok: + env_ovar = OLD_ENV_VAR; + env_ovalue = OLD_ENV_VALUE; + } else if (got_uservar + got_value == got_var) { + env_ovar_wrong: + env_ovar = OLD_ENV_VALUE; + env_ovalue = OLD_ENV_VAR; + DIAG(TD_OPTIONS, {sprintf(nfrontp, + "ENVIRON VALUE and VAR are reversed!\r\n"); + nfrontp += strlen(nfrontp);}); + + } + } + SB_RESTORE(); +#endif + + while (!SB_EOF()) { + c = SB_GET(); + if ((c == env_ovar) || (c == ENV_USERVAR)) + break; + } + } + + if (SB_EOF()) + return; + + cp = varp = (char *)subpointer; + valp = 0; + + while (!SB_EOF()) { + c = SB_GET(); + if (subchar == TELOPT_OLD_ENVIRON) { + if (c == env_ovar) + c = NEW_ENV_VAR; + else if (c == env_ovalue) + c = NEW_ENV_VALUE; + } + switch (c) { + + case NEW_ENV_VALUE: + *cp = '\0'; + cp = valp = (char *)subpointer; + break; + + case NEW_ENV_VAR: + case ENV_USERVAR: + *cp = '\0'; + if (valp) + (void)setenv(varp, valp, 1); + else + unsetenv(varp); + cp = varp = (char *)subpointer; + valp = 0; + break; + + case ENV_ESC: + if (SB_EOF()) + break; + c = SB_GET(); + /* FALL THROUGH */ + default: + *cp++ = c; + break; + } + } + *cp = '\0'; + if (valp) + (void)setenv(varp, valp, 1); + else + unsetenv(varp); + break; + } /* end of case TELOPT_NEW_ENVIRON */ +#if defined(AUTHENTICATION) + case TELOPT_AUTHENTICATION: + if (SB_EOF()) + break; + switch(SB_GET()) { + case TELQUAL_SEND: + case TELQUAL_REPLY: + /* + * These are sent by us and cannot be sent by + * the client. + */ + break; + case TELQUAL_IS: + auth_is(subpointer, SB_LEN()); + break; + case TELQUAL_NAME: + auth_name(subpointer, SB_LEN()); + break; + } + break; +#endif + + default: + break; + } /* end of switch */ + +} /* end of suboption */ + + void +doclientstat() +{ + clientstat(TELOPT_LINEMODE, WILL, 0); +} + +#define ADD(c) *ncp++ = c; +#define ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; } + void +send_status() +{ + unsigned char statusbuf[256]; + register unsigned char *ncp; + register unsigned char i; + + ncp = statusbuf; + + netflush(); /* get rid of anything waiting to go out */ + + ADD(IAC); + ADD(SB); + ADD(TELOPT_STATUS); + ADD(TELQUAL_IS); + + /* + * We check the want_state rather than the current state, + * because if we received a DO/WILL for an option that we + * don't support, and the other side didn't send a DONT/WONT + * in response to our WONT/DONT, then the "state" will be + * WILL/DO, and the "want_state" will be WONT/DONT. We + * need to go by the latter. + */ + for (i = 0; i < (unsigned char)NTELOPTS; i++) { + if (my_want_state_is_will(i)) { + ADD(WILL); + ADD_DATA(i); + if (i == IAC) + ADD(IAC); + } + if (his_want_state_is_will(i)) { + ADD(DO); + ADD_DATA(i); + if (i == IAC) + ADD(IAC); + } + } + + if (his_want_state_is_will(TELOPT_LFLOW)) { + ADD(SB); + ADD(TELOPT_LFLOW); + if (flowmode) { + ADD(LFLOW_ON); + } else { + ADD(LFLOW_OFF); + } + ADD(SE); + + if (restartany >= 0) { + ADD(SB) + ADD(TELOPT_LFLOW); + if (restartany) { + ADD(LFLOW_RESTART_ANY); + } else { + ADD(LFLOW_RESTART_XON); + } + ADD(SE) + ADD(SB); + } + } + +#ifdef LINEMODE + if (his_want_state_is_will(TELOPT_LINEMODE)) { + unsigned char *cp, *cpe; + int len; + + ADD(SB); + ADD(TELOPT_LINEMODE); + ADD(LM_MODE); + ADD_DATA(editmode); + if (editmode == IAC) + ADD(IAC); + ADD(SE); + + ADD(SB); + ADD(TELOPT_LINEMODE); + ADD(LM_SLC); + start_slc(0); + send_slc(); + len = end_slc(&cp); + for (cpe = cp + len; cp < cpe; cp++) + ADD_DATA(*cp); + ADD(SE); + } +#endif /* LINEMODE */ + + ADD(IAC); + ADD(SE); + + writenet(statusbuf, ncp - statusbuf); + netflush(); /* Send it on its way */ + + DIAG(TD_OPTIONS, + {printsub('>', statusbuf, ncp - statusbuf); netflush();}); +} diff --git a/libexec/telnetd/sys_term.c b/libexec/telnetd/sys_term.c new file mode 100644 index 0000000..40610c2 --- /dev/null +++ b/libexec/telnetd/sys_term.c @@ -0,0 +1,2195 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sys_term.c 8.2 (Berkeley) 12/15/93"; +#endif /* not lint */ + +#include "telnetd.h" +#include "pathnames.h" + +#if defined(AUTHENTICATION) +#include <libtelnet/auth.h> +#endif + +#if defined(CRAY) || defined(__hpux) +# define PARENT_DOES_UTMP +#endif + +int utmp_len = MAXHOSTNAMELEN; +#ifdef NEWINIT +#include <initreq.h> +#else /* NEWINIT*/ +# ifdef UTMPX +# include <utmpx.h> +struct utmpx wtmp; +# else +# include <utmp.h> +struct utmp wtmp; +# endif /* UTMPX */ + +# ifndef PARENT_DOES_UTMP +#ifdef _PATH_WTMP +char wtmpf[] = _PATH_WTMP; +#else +char wtmpf[] = "/usr/adm/wtmp"; +#endif +#ifdef _PATH_UTMP +char utmpf[] = _PATH_UTMP; +#else +char utmpf[] = "/etc/utmp"; +#endif +# else /* PARENT_DOES_UTMP */ +char wtmpf[] = "/etc/wtmp"; +# endif /* PARENT_DOES_UTMP */ + +# ifdef CRAY +#include <tmpdir.h> +#include <sys/wait.h> +# if defined(_SC_CRAY_SECURE_SYS) && !defined(SCM_SECURITY) + /* + * UNICOS 6.0/6.1 do not have SCM_SECURITY defined, so we can + * use it to tell us to turn off all the socket security code, + * since that is only used in UNICOS 7.0 and later. + */ +# undef _SC_CRAY_SECURE_SYS +# endif + +# if defined(_SC_CRAY_SECURE_SYS) +#include <sys/sysv.h> +#include <sys/secstat.h> +extern int secflag; +extern struct sysv sysv; +# endif /* _SC_CRAY_SECURE_SYS */ +# endif /* CRAY */ +#endif /* NEWINIT */ + +#ifdef STREAMSPTY +#include <sac.h> +#include <sys/stropts.h> +#endif + +#define SCPYN(a, b) (void) strncpy(a, b, sizeof(a)) +#define SCMPN(a, b) strncmp(a, b, sizeof(a)) + +#ifdef STREAMS +#include <sys/stream.h> +#endif +#ifdef __hpux +#include <sys/resource.h> +#include <sys/proc.h> +#endif +#include <sys/tty.h> +#ifdef t_erase +#undef t_erase +#undef t_kill +#undef t_intrc +#undef t_quitc +#undef t_startc +#undef t_stopc +#undef t_eofc +#undef t_brkc +#undef t_suspc +#undef t_dsuspc +#undef t_rprntc +#undef t_flushc +#undef t_werasc +#undef t_lnextc +#endif + +#if defined(UNICOS5) && defined(CRAY2) && !defined(EXTPROC) +# define EXTPROC 0400 +#endif + +#ifndef USE_TERMIO +struct termbuf { + struct sgttyb sg; + struct tchars tc; + struct ltchars ltc; + int state; + int lflags; +} termbuf, termbuf2; +# define cfsetospeed(tp, val) (tp)->sg.sg_ospeed = (val) +# define cfsetispeed(tp, val) (tp)->sg.sg_ispeed = (val) +# define cfgetospeed(tp) (tp)->sg.sg_ospeed +# define cfgetispeed(tp) (tp)->sg.sg_ispeed +#else /* USE_TERMIO */ +# ifdef SYSV_TERMIO +# define termios termio +# endif +# ifndef TCSANOW +# ifdef TCSETS +# define TCSANOW TCSETS +# define TCSADRAIN TCSETSW +# define tcgetattr(f, t) ioctl(f, TCGETS, (char *)t) +# else +# ifdef TCSETA +# define TCSANOW TCSETA +# define TCSADRAIN TCSETAW +# define tcgetattr(f, t) ioctl(f, TCGETA, (char *)t) +# else +# define TCSANOW TIOCSETA +# define TCSADRAIN TIOCSETAW +# define tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t) +# endif +# endif +# define tcsetattr(f, a, t) ioctl(f, a, t) +# define cfsetospeed(tp, val) (tp)->c_cflag &= ~CBAUD; \ + (tp)->c_cflag |= (val) +# define cfgetospeed(tp) ((tp)->c_cflag & CBAUD) +# ifdef CIBAUD +# define cfsetispeed(tp, val) (tp)->c_cflag &= ~CIBAUD; \ + (tp)->c_cflag |= ((val)<<IBSHIFT) +# define cfgetispeed(tp) (((tp)->c_cflag & CIBAUD)>>IBSHIFT) +# else +# define cfsetispeed(tp, val) (tp)->c_cflag &= ~CBAUD; \ + (tp)->c_cflag |= (val) +# define cfgetispeed(tp) ((tp)->c_cflag & CBAUD) +# endif +# endif /* TCSANOW */ +struct termios termbuf, termbuf2; /* pty control structure */ +# ifdef STREAMSPTY +int ttyfd = -1; +# endif +#endif /* USE_TERMIO */ + +/* + * init_termbuf() + * copy_termbuf(cp) + * set_termbuf() + * + * These three routines are used to get and set the "termbuf" structure + * to and from the kernel. init_termbuf() gets the current settings. + * copy_termbuf() hands in a new "termbuf" to write to the kernel, and + * set_termbuf() writes the structure into the kernel. + */ + + void +init_termbuf() +{ +#ifndef USE_TERMIO + (void) ioctl(pty, TIOCGETP, (char *)&termbuf.sg); + (void) ioctl(pty, TIOCGETC, (char *)&termbuf.tc); + (void) ioctl(pty, TIOCGLTC, (char *)&termbuf.ltc); +# ifdef TIOCGSTATE + (void) ioctl(pty, TIOCGSTATE, (char *)&termbuf.state); +# endif +#else +# ifdef STREAMSPTY + (void) tcgetattr(ttyfd, &termbuf); +# else + (void) tcgetattr(pty, &termbuf); +# endif +#endif + termbuf2 = termbuf; +} + +#if defined(LINEMODE) && defined(TIOCPKT_IOCTL) + void +copy_termbuf(cp, len) + char *cp; + int len; +{ + if (len > sizeof(termbuf)) + len = sizeof(termbuf); + bcopy(cp, (char *)&termbuf, len); + termbuf2 = termbuf; +} +#endif /* defined(LINEMODE) && defined(TIOCPKT_IOCTL) */ + + void +set_termbuf() +{ + /* + * Only make the necessary changes. + */ +#ifndef USE_TERMIO + if (bcmp((char *)&termbuf.sg, (char *)&termbuf2.sg, sizeof(termbuf.sg))) + (void) ioctl(pty, TIOCSETN, (char *)&termbuf.sg); + if (bcmp((char *)&termbuf.tc, (char *)&termbuf2.tc, sizeof(termbuf.tc))) + (void) ioctl(pty, TIOCSETC, (char *)&termbuf.tc); + if (bcmp((char *)&termbuf.ltc, (char *)&termbuf2.ltc, + sizeof(termbuf.ltc))) + (void) ioctl(pty, TIOCSLTC, (char *)&termbuf.ltc); + if (termbuf.lflags != termbuf2.lflags) + (void) ioctl(pty, TIOCLSET, (char *)&termbuf.lflags); +#else /* USE_TERMIO */ + if (bcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf))) +# ifdef STREAMSPTY + (void) tcsetattr(ttyfd, TCSANOW, &termbuf); +# else + (void) tcsetattr(pty, TCSANOW, &termbuf); +# endif +# if defined(CRAY2) && defined(UNICOS5) + needtermstat = 1; +# endif +#endif /* USE_TERMIO */ +} + + +/* + * spcset(func, valp, valpp) + * + * This function takes various special characters (func), and + * sets *valp to the current value of that character, and + * *valpp to point to where in the "termbuf" structure that + * value is kept. + * + * It returns the SLC_ level of support for this function. + */ + +#ifndef USE_TERMIO + int +spcset(func, valp, valpp) + int func; + cc_t *valp; + cc_t **valpp; +{ + switch(func) { + case SLC_EOF: + *valp = termbuf.tc.t_eofc; + *valpp = (cc_t *)&termbuf.tc.t_eofc; + return(SLC_VARIABLE); + case SLC_EC: + *valp = termbuf.sg.sg_erase; + *valpp = (cc_t *)&termbuf.sg.sg_erase; + return(SLC_VARIABLE); + case SLC_EL: + *valp = termbuf.sg.sg_kill; + *valpp = (cc_t *)&termbuf.sg.sg_kill; + return(SLC_VARIABLE); + case SLC_IP: + *valp = termbuf.tc.t_intrc; + *valpp = (cc_t *)&termbuf.tc.t_intrc; + return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); + case SLC_ABORT: + *valp = termbuf.tc.t_quitc; + *valpp = (cc_t *)&termbuf.tc.t_quitc; + return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); + case SLC_XON: + *valp = termbuf.tc.t_startc; + *valpp = (cc_t *)&termbuf.tc.t_startc; + return(SLC_VARIABLE); + case SLC_XOFF: + *valp = termbuf.tc.t_stopc; + *valpp = (cc_t *)&termbuf.tc.t_stopc; + return(SLC_VARIABLE); + case SLC_AO: + *valp = termbuf.ltc.t_flushc; + *valpp = (cc_t *)&termbuf.ltc.t_flushc; + return(SLC_VARIABLE); + case SLC_SUSP: + *valp = termbuf.ltc.t_suspc; + *valpp = (cc_t *)&termbuf.ltc.t_suspc; + return(SLC_VARIABLE); + case SLC_EW: + *valp = termbuf.ltc.t_werasc; + *valpp = (cc_t *)&termbuf.ltc.t_werasc; + return(SLC_VARIABLE); + case SLC_RP: + *valp = termbuf.ltc.t_rprntc; + *valpp = (cc_t *)&termbuf.ltc.t_rprntc; + return(SLC_VARIABLE); + case SLC_LNEXT: + *valp = termbuf.ltc.t_lnextc; + *valpp = (cc_t *)&termbuf.ltc.t_lnextc; + return(SLC_VARIABLE); + case SLC_FORW1: + *valp = termbuf.tc.t_brkc; + *valpp = (cc_t *)&termbuf.ltc.t_lnextc; + return(SLC_VARIABLE); + case SLC_BRK: + case SLC_SYNCH: + case SLC_AYT: + case SLC_EOR: + *valp = (cc_t)0; + *valpp = (cc_t *)0; + return(SLC_DEFAULT); + default: + *valp = (cc_t)0; + *valpp = (cc_t *)0; + return(SLC_NOSUPPORT); + } +} + +#else /* USE_TERMIO */ + + int +spcset(func, valp, valpp) + int func; + cc_t *valp; + cc_t **valpp; +{ + +#define setval(a, b) *valp = termbuf.c_cc[a]; \ + *valpp = &termbuf.c_cc[a]; \ + return(b); +#define defval(a) *valp = ((cc_t)a); *valpp = (cc_t *)0; return(SLC_DEFAULT); + + switch(func) { + case SLC_EOF: + setval(VEOF, SLC_VARIABLE); + case SLC_EC: + setval(VERASE, SLC_VARIABLE); + case SLC_EL: + setval(VKILL, SLC_VARIABLE); + case SLC_IP: + setval(VINTR, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); + case SLC_ABORT: + setval(VQUIT, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); + case SLC_XON: +#ifdef VSTART + setval(VSTART, SLC_VARIABLE); +#else + defval(0x13); +#endif + case SLC_XOFF: +#ifdef VSTOP + setval(VSTOP, SLC_VARIABLE); +#else + defval(0x11); +#endif + case SLC_EW: +#ifdef VWERASE + setval(VWERASE, SLC_VARIABLE); +#else + defval(0); +#endif + case SLC_RP: +#ifdef VREPRINT + setval(VREPRINT, SLC_VARIABLE); +#else + defval(0); +#endif + case SLC_LNEXT: +#ifdef VLNEXT + setval(VLNEXT, SLC_VARIABLE); +#else + defval(0); +#endif + case SLC_AO: +#if !defined(VDISCARD) && defined(VFLUSHO) +# define VDISCARD VFLUSHO +#endif +#ifdef VDISCARD + setval(VDISCARD, SLC_VARIABLE|SLC_FLUSHOUT); +#else + defval(0); +#endif + case SLC_SUSP: +#ifdef VSUSP + setval(VSUSP, SLC_VARIABLE|SLC_FLUSHIN); +#else + defval(0); +#endif +#ifdef VEOL + case SLC_FORW1: + setval(VEOL, SLC_VARIABLE); +#endif +#ifdef VEOL2 + case SLC_FORW2: + setval(VEOL2, SLC_VARIABLE); +#endif + case SLC_AYT: +#ifdef VSTATUS + setval(VSTATUS, SLC_VARIABLE); +#else + defval(0); +#endif + + case SLC_BRK: + case SLC_SYNCH: + case SLC_EOR: + defval(0); + + default: + *valp = 0; + *valpp = 0; + return(SLC_NOSUPPORT); + } +} +#endif /* USE_TERMIO */ + +#ifdef CRAY +/* + * getnpty() + * + * Return the number of pty's configured into the system. + */ + int +getnpty() +{ +#ifdef _SC_CRAY_NPTY + int numptys; + + if ((numptys = sysconf(_SC_CRAY_NPTY)) != -1) + return numptys; + else +#endif /* _SC_CRAY_NPTY */ + return 128; +} +#endif /* CRAY */ + +#ifndef convex +/* + * getpty() + * + * Allocate a pty. As a side effect, the external character + * array "line" contains the name of the slave side. + * + * Returns the file descriptor of the opened pty. + */ +#ifndef __GNUC__ +char *line = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; +#else +static char Xline[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; +char *line = Xline; +#endif +#ifdef CRAY +char *myline = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; +#endif /* CRAY */ + + int +getpty(ptynum) +int *ptynum; +{ + register int p; +#ifdef STREAMSPTY + int t; + char *ptsname(); + + p = open("/dev/ptmx", 2); + if (p > 0) { + grantpt(p); + unlockpt(p); + strcpy(line, ptsname(p)); + return(p); + } + +#else /* ! STREAMSPTY */ +#ifndef CRAY + register char *cp, *p1, *p2; + register int i; +#if defined(sun) && defined(TIOCGPGRP) && BSD < 199207 + int dummy; +#endif + +#ifndef __hpux + (void) sprintf(line, "/dev/ptyXX"); + p1 = &line[8]; + p2 = &line[9]; +#else + (void) sprintf(line, "/dev/ptym/ptyXX"); + p1 = &line[13]; + p2 = &line[14]; +#endif + + for (cp = "pqrsPQRS"; *cp; cp++) { + struct stat stb; + + *p1 = *cp; + *p2 = '0'; + /* + * This stat() check is just to keep us from + * looping through all 256 combinations if there + * aren't that many ptys available. + */ + if (stat(line, &stb) < 0) + break; + for (i = 0; i < 32; i++) { + *p2 = "0123456789abcdefghijklmnopqrstuv"[i]; + p = open(line, 2); + if (p > 0) { +#ifndef __hpux + line[5] = 't'; +#else + for (p1 = &line[8]; *p1; p1++) + *p1 = *(p1+1); + line[9] = 't'; +#endif + chown(line, 0, 0); + chmod(line, 0600); +#if defined(sun) && defined(TIOCGPGRP) && BSD < 199207 + if (ioctl(p, TIOCGPGRP, &dummy) == 0 + || errno != EIO) { + chmod(line, 0666); + close(p); + line[5] = 'p'; + } else +#endif /* defined(sun) && defined(TIOCGPGRP) && BSD < 199207 */ + return(p); + } + } + } +#else /* CRAY */ + extern lowpty, highpty; + struct stat sb; + + for (*ptynum = lowpty; *ptynum <= highpty; (*ptynum)++) { + (void) sprintf(myline, "/dev/pty/%03d", *ptynum); + p = open(myline, 2); + if (p < 0) + continue; + (void) sprintf(line, "/dev/ttyp%03d", *ptynum); + /* + * Here are some shenanigans to make sure that there + * are no listeners lurking on the line. + */ + if(stat(line, &sb) < 0) { + (void) close(p); + continue; + } + if(sb.st_uid || sb.st_gid || sb.st_mode != 0600) { + chown(line, 0, 0); + chmod(line, 0600); + (void)close(p); + p = open(myline, 2); + if (p < 0) + continue; + } + /* + * Now it should be safe...check for accessability. + */ + if (access(line, 6) == 0) + return(p); + else { + /* no tty side to pty so skip it */ + (void) close(p); + } + } +#endif /* CRAY */ +#endif /* STREAMSPTY */ + return(-1); +} +#endif /* convex */ + +#ifdef LINEMODE +/* + * tty_flowmode() Find out if flow control is enabled or disabled. + * tty_linemode() Find out if linemode (external processing) is enabled. + * tty_setlinemod(on) Turn on/off linemode. + * tty_isecho() Find out if echoing is turned on. + * tty_setecho(on) Enable/disable character echoing. + * tty_israw() Find out if terminal is in RAW mode. + * tty_binaryin(on) Turn on/off BINARY on input. + * tty_binaryout(on) Turn on/off BINARY on output. + * tty_isediting() Find out if line editing is enabled. + * tty_istrapsig() Find out if signal trapping is enabled. + * tty_setedit(on) Turn on/off line editing. + * tty_setsig(on) Turn on/off signal trapping. + * tty_issofttab() Find out if tab expansion is enabled. + * tty_setsofttab(on) Turn on/off soft tab expansion. + * tty_islitecho() Find out if typed control chars are echoed literally + * tty_setlitecho() Turn on/off literal echo of control chars + * tty_tspeed(val) Set transmit speed to val. + * tty_rspeed(val) Set receive speed to val. + */ + +#ifdef convex +static int linestate; +#endif + + int +tty_linemode() +{ +#ifndef convex +#ifndef USE_TERMIO + return(termbuf.state & TS_EXTPROC); +#else + return(termbuf.c_lflag & EXTPROC); +#endif +#else + return(linestate); +#endif +} + + void +tty_setlinemode(on) + int on; +{ +#ifdef TIOCEXT +# ifndef convex + set_termbuf(); +# else + linestate = on; +# endif + (void) ioctl(pty, TIOCEXT, (char *)&on); +# ifndef convex + init_termbuf(); +# endif +#else /* !TIOCEXT */ +# ifdef EXTPROC + if (on) + termbuf.c_lflag |= EXTPROC; + else + termbuf.c_lflag &= ~EXTPROC; +# endif +#endif /* TIOCEXT */ +} +#endif /* LINEMODE */ + + int +tty_isecho() +{ +#ifndef USE_TERMIO + return (termbuf.sg.sg_flags & ECHO); +#else + return (termbuf.c_lflag & ECHO); +#endif +} + + int +tty_flowmode() +{ +#ifndef USE_TERMIO + return(((termbuf.tc.t_startc) > 0 && (termbuf.tc.t_stopc) > 0) ? 1 : 0); +#else + return((termbuf.c_iflag & IXON) ? 1 : 0); +#endif +} + + int +tty_restartany() +{ +#ifndef USE_TERMIO +# ifdef DECCTQ + return((termbuf.lflags & DECCTQ) ? 0 : 1); +# else + return(-1); +# endif +#else + return((termbuf.c_iflag & IXANY) ? 1 : 0); +#endif +} + + void +tty_setecho(on) + int on; +{ +#ifndef USE_TERMIO + if (on) + termbuf.sg.sg_flags |= ECHO|CRMOD; + else + termbuf.sg.sg_flags &= ~(ECHO|CRMOD); +#else + if (on) + termbuf.c_lflag |= ECHO; + else + termbuf.c_lflag &= ~ECHO; +#endif +} + + int +tty_israw() +{ +#ifndef USE_TERMIO + return(termbuf.sg.sg_flags & RAW); +#else + return(!(termbuf.c_lflag & ICANON)); +#endif +} + +#if defined (AUTHENTICATION) && defined(NO_LOGIN_F) && defined(LOGIN_R) + int +tty_setraw(on) +{ +# ifndef USE_TERMIO + if (on) + termbuf.sg.sg_flags |= RAW; + else + termbuf.sg.sg_flags &= ~RAW; +# else + if (on) + termbuf.c_lflag &= ~ICANON; + else + termbuf.c_lflag |= ICANON; +# endif +} +#endif + + void +tty_binaryin(on) + int on; +{ +#ifndef USE_TERMIO + if (on) + termbuf.lflags |= LPASS8; + else + termbuf.lflags &= ~LPASS8; +#else + if (on) { + termbuf.c_iflag &= ~ISTRIP; + } else { + termbuf.c_iflag |= ISTRIP; + } +#endif +} + + void +tty_binaryout(on) + int on; +{ +#ifndef USE_TERMIO + if (on) + termbuf.lflags |= LLITOUT; + else + termbuf.lflags &= ~LLITOUT; +#else + if (on) { + termbuf.c_cflag &= ~(CSIZE|PARENB); + termbuf.c_cflag |= CS8; + termbuf.c_oflag &= ~OPOST; + } else { + termbuf.c_cflag &= ~CSIZE; + termbuf.c_cflag |= CS7|PARENB; + termbuf.c_oflag |= OPOST; + } +#endif +} + + int +tty_isbinaryin() +{ +#ifndef USE_TERMIO + return(termbuf.lflags & LPASS8); +#else + return(!(termbuf.c_iflag & ISTRIP)); +#endif +} + + int +tty_isbinaryout() +{ +#ifndef USE_TERMIO + return(termbuf.lflags & LLITOUT); +#else + return(!(termbuf.c_oflag&OPOST)); +#endif +} + +#ifdef LINEMODE + int +tty_isediting() +{ +#ifndef USE_TERMIO + return(!(termbuf.sg.sg_flags & (CBREAK|RAW))); +#else + return(termbuf.c_lflag & ICANON); +#endif +} + + int +tty_istrapsig() +{ +#ifndef USE_TERMIO + return(!(termbuf.sg.sg_flags&RAW)); +#else + return(termbuf.c_lflag & ISIG); +#endif +} + + void +tty_setedit(on) + int on; +{ +#ifndef USE_TERMIO + if (on) + termbuf.sg.sg_flags &= ~CBREAK; + else + termbuf.sg.sg_flags |= CBREAK; +#else + if (on) + termbuf.c_lflag |= ICANON; + else + termbuf.c_lflag &= ~ICANON; +#endif +} + + void +tty_setsig(on) + int on; +{ +#ifndef USE_TERMIO + if (on) + ; +#else + if (on) + termbuf.c_lflag |= ISIG; + else + termbuf.c_lflag &= ~ISIG; +#endif +} +#endif /* LINEMODE */ + + int +tty_issofttab() +{ +#ifndef USE_TERMIO + return (termbuf.sg.sg_flags & XTABS); +#else +# ifdef OXTABS + return (termbuf.c_oflag & OXTABS); +# endif +# ifdef TABDLY + return ((termbuf.c_oflag & TABDLY) == TAB3); +# endif +#endif +} + + void +tty_setsofttab(on) + int on; +{ +#ifndef USE_TERMIO + if (on) + termbuf.sg.sg_flags |= XTABS; + else + termbuf.sg.sg_flags &= ~XTABS; +#else + if (on) { +# ifdef OXTABS + termbuf.c_oflag |= OXTABS; +# endif +# ifdef TABDLY + termbuf.c_oflag &= ~TABDLY; + termbuf.c_oflag |= TAB3; +# endif + } else { +# ifdef OXTABS + termbuf.c_oflag &= ~OXTABS; +# endif +# ifdef TABDLY + termbuf.c_oflag &= ~TABDLY; + termbuf.c_oflag |= TAB0; +# endif + } +#endif +} + + int +tty_islitecho() +{ +#ifndef USE_TERMIO + return (!(termbuf.lflags & LCTLECH)); +#else +# ifdef ECHOCTL + return (!(termbuf.c_lflag & ECHOCTL)); +# endif +# ifdef TCTLECH + return (!(termbuf.c_lflag & TCTLECH)); +# endif +# if !defined(ECHOCTL) && !defined(TCTLECH) + return (0); /* assumes ctl chars are echoed '^x' */ +# endif +#endif +} + + void +tty_setlitecho(on) + int on; +{ +#ifndef USE_TERMIO + if (on) + termbuf.lflags &= ~LCTLECH; + else + termbuf.lflags |= LCTLECH; +#else +# ifdef ECHOCTL + if (on) + termbuf.c_lflag &= ~ECHOCTL; + else + termbuf.c_lflag |= ECHOCTL; +# endif +# ifdef TCTLECH + if (on) + termbuf.c_lflag &= ~TCTLECH; + else + termbuf.c_lflag |= TCTLECH; +# endif +#endif +} + + int +tty_iscrnl() +{ +#ifndef USE_TERMIO + return (termbuf.sg.sg_flags & CRMOD); +#else + return (termbuf.c_iflag & ICRNL); +#endif +} + +/* + * Try to guess whether speeds are "encoded" (4.2BSD) or just numeric (4.4BSD). + */ +#if B4800 != 4800 +#define DECODE_BAUD +#endif + +#ifdef DECODE_BAUD +/* + * A table of available terminal speeds + */ +struct termspeeds { + int speed; + int value; +} termspeeds[] = { + { 0, B0 }, { 50, B50 }, { 75, B75 }, + { 110, B110 }, { 134, B134 }, { 150, B150 }, + { 200, B200 }, { 300, B300 }, { 600, B600 }, + { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 }, + { 4800, B4800 }, { 9600, B9600 }, { 19200, B9600 }, + { 38400, B9600 }, { -1, B9600 } +}; +#endif /* DECODE_BAUD */ + + void +tty_tspeed(val) + int val; +{ +#ifdef DECODE_BAUD + register struct termspeeds *tp; + + for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++) + ; + cfsetospeed(&termbuf, tp->value); +#else /* DECODE_BAUD */ + cfsetospeed(&termbuf, val); +#endif /* DECODE_BAUD */ +} + + void +tty_rspeed(val) + int val; +{ +#ifdef DECODE_BAUD + register struct termspeeds *tp; + + for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++) + ; + cfsetispeed(&termbuf, tp->value); +#else /* DECODE_BAUD */ + cfsetispeed(&termbuf, val); +#endif /* DECODE_BAUD */ +} + +#if defined(CRAY2) && defined(UNICOS5) + int +tty_isnewmap() +{ + return((termbuf.c_oflag & OPOST) && (termbuf.c_oflag & ONLCR) && + !(termbuf.c_oflag & ONLRET)); +} +#endif + +#ifdef PARENT_DOES_UTMP +# ifndef NEWINIT +extern struct utmp wtmp; +extern char wtmpf[]; +# else /* NEWINIT */ +int gotalarm; + + /* ARGSUSED */ + void +nologinproc(sig) + int sig; +{ + gotalarm++; +} +# endif /* NEWINIT */ +#endif /* PARENT_DOES_UTMP */ + +#ifndef NEWINIT +# ifdef PARENT_DOES_UTMP +extern void utmp_sig_init P((void)); +extern void utmp_sig_reset P((void)); +extern void utmp_sig_wait P((void)); +extern void utmp_sig_notify P((int)); +# endif /* PARENT_DOES_UTMP */ +#endif + +/* + * getptyslave() + * + * Open the slave side of the pty, and do any initialization + * that is necessary. The return value is a file descriptor + * for the slave side. + */ + int +getptyslave() +{ + register int t = -1; + char erase; + +#if !defined(CRAY) || !defined(NEWINIT) +# ifdef LINEMODE + int waslm; +# endif +# ifdef TIOCGWINSZ + struct winsize ws; + extern int def_row, def_col; +# endif + extern int def_tspeed, def_rspeed; + /* + * Opening the slave side may cause initilization of the + * kernel tty structure. We need remember the state of + * if linemode was turned on + * terminal window size + * terminal speed + * erase character + * so that we can re-set them if we need to. + */ +# ifdef LINEMODE + waslm = tty_linemode(); +# endif + erase = termbuf.c_cc[VERASE]; + + /* + * Make sure that we don't have a controlling tty, and + * that we are the session (process group) leader. + */ +# ifdef TIOCNOTTY + t = open(_PATH_TTY, O_RDWR); + if (t >= 0) { + (void) ioctl(t, TIOCNOTTY, (char *)0); + (void) close(t); + } +# endif + + +# ifdef PARENT_DOES_UTMP + /* + * Wait for our parent to get the utmp stuff to get done. + */ + utmp_sig_wait(); +# endif + + t = cleanopen(line); + if (t < 0) + fatalperror(net, line); + +#ifdef STREAMSPTY +#ifdef USE_TERMIO + ttyfd = t; +#endif + if (ioctl(t, I_PUSH, "ptem") < 0) + fatal(net, "I_PUSH ptem"); + if (ioctl(t, I_PUSH, "ldterm") < 0) + fatal(net, "I_PUSH ldterm"); + if (ioctl(t, I_PUSH, "ttcompat") < 0) + fatal(net, "I_PUSH ttcompat"); + if (ioctl(pty, I_PUSH, "pckt") < 0) + fatal(net, "I_PUSH pckt"); +#endif + + /* + * set up the tty modes as we like them to be. + */ + init_termbuf(); +# ifdef TIOCGWINSZ + if (def_row || def_col) { + bzero((char *)&ws, sizeof(ws)); + ws.ws_col = def_col; + ws.ws_row = def_row; + (void)ioctl(t, TIOCSWINSZ, (char *)&ws); + } +# endif + + /* + * Settings for sgtty based systems + */ +# ifndef USE_TERMIO + termbuf.sg.sg_flags |= CRMOD|ANYP|ECHO|XTABS; +# endif /* USE_TERMIO */ + + /* + * Settings for UNICOS (and HPUX) + */ +# if defined(CRAY) || defined(__hpux) + termbuf.c_oflag = OPOST|ONLCR|TAB3; + termbuf.c_iflag = IGNPAR|ISTRIP|ICRNL|IXON; + termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK; + termbuf.c_cflag = EXTB|HUPCL|CS8; +# endif + + /* + * Settings for all other termios/termio based + * systems, other than 4.4BSD. In 4.4BSD the + * kernel does the initial terminal setup. + */ +# if defined(USE_TERMIO) && !(defined(CRAY) || defined(__hpux)) && (BSD <= 43) +# ifndef OXTABS +# define OXTABS 0 +# endif + termbuf.c_lflag |= ECHO; + termbuf.c_oflag |= ONLCR|OXTABS; + termbuf.c_iflag |= ICRNL; + termbuf.c_iflag &= ~IXOFF; +# endif /* defined(USE_TERMIO) && !defined(CRAY) && (BSD <= 43) */ + tty_rspeed((def_rspeed > 0) ? def_rspeed : 9600); + tty_tspeed((def_tspeed > 0) ? def_tspeed : 9600); + if (erase) + termbuf.c_cc[VERASE] = erase; +# ifdef LINEMODE + if (waslm) + tty_setlinemode(1); +# endif /* LINEMODE */ + + /* + * Set the tty modes, and make this our controlling tty. + */ + set_termbuf(); + if (login_tty(t) == -1) + fatalperror(net, "login_tty"); +#endif /* !defined(CRAY) || !defined(NEWINIT) */ + if (net > 2) + (void) close(net); +#if defined(AUTHENTICATION) && defined(NO_LOGIN_F) && defined(LOGIN_R) + /* + * Leave the pty open so that we can write out the rlogin + * protocol for /bin/login, if the authentication works. + */ +#else + if (pty > 2) { + (void) close(pty); + pty = -1; + } +#endif +} + +#if !defined(CRAY) || !defined(NEWINIT) +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif +/* + * Open the specified slave side of the pty, + * making sure that we have a clean tty. + */ + int +cleanopen(line) + char *line; +{ + register int t; +#if defined(_SC_CRAY_SECURE_SYS) + struct secstat secbuf; +#endif /* _SC_CRAY_SECURE_SYS */ + +#ifndef STREAMSPTY + /* + * Make sure that other people can't open the + * slave side of the connection. + */ + (void) chown(line, 0, 0); + (void) chmod(line, 0600); +#endif + +# if !defined(CRAY) && (BSD > 43) + (void) revoke(line); +# endif +#if defined(_SC_CRAY_SECURE_SYS) + if (secflag) { + if (secstat(line, &secbuf) < 0) + return(-1); + if (setulvl(secbuf.st_slevel) < 0) + return(-1); + if (setucmp(secbuf.st_compart) < 0) + return(-1); + } +#endif /* _SC_CRAY_SECURE_SYS */ + + t = open(line, O_RDWR|O_NOCTTY); + +#if defined(_SC_CRAY_SECURE_SYS) + if (secflag) { + if (setulvl(sysv.sy_minlvl) < 0) + return(-1); + if (setucmp(0) < 0) + return(-1); + } +#endif /* _SC_CRAY_SECURE_SYS */ + + if (t < 0) + return(-1); + + /* + * Hangup anybody else using this ttyp, then reopen it for + * ourselves. + */ +# if !(defined(CRAY) || defined(__hpux)) && (BSD <= 43) && !defined(STREAMSPTY) + (void) signal(SIGHUP, SIG_IGN); + vhangup(); + (void) signal(SIGHUP, SIG_DFL); + t = open(line, O_RDWR|O_NOCTTY); + if (t < 0) + return(-1); +# endif +# if defined(CRAY) && defined(TCVHUP) + { + register int i; + (void) signal(SIGHUP, SIG_IGN); + (void) ioctl(t, TCVHUP, (char *)0); + (void) signal(SIGHUP, SIG_DFL); + setpgrp(); + +#if defined(_SC_CRAY_SECURE_SYS) + if (secflag) { + if (secstat(line, &secbuf) < 0) + return(-1); + if (setulvl(secbuf.st_slevel) < 0) + return(-1); + if (setucmp(secbuf.st_compart) < 0) + return(-1); + } +#endif /* _SC_CRAY_SECURE_SYS */ + + i = open(line, O_RDWR); + +#if defined(_SC_CRAY_SECURE_SYS) + if (secflag) { + if (setulvl(sysv.sy_minlvl) < 0) + return(-1); + if (setucmp(0) < 0) + return(-1); + } +#endif /* _SC_CRAY_SECURE_SYS */ + + if (i < 0) + return(-1); + (void) close(t); + t = i; + } +# endif /* defined(CRAY) && defined(TCVHUP) */ + return(t); +} +#endif /* !defined(CRAY) || !defined(NEWINIT) */ + +#if BSD <= 43 + + int +login_tty(t) + int t; +{ + if (setsid() < 0) { +#ifdef ultrix + /* + * The setsid() may have failed because we + * already have a pgrp == pid. Zero out + * our pgrp and try again... + */ + if ((setpgrp(0, 0) < 0) || (setsid() < 0)) +#endif + fatalperror(net, "setsid()"); + } +# ifdef TIOCSCTTY + if (ioctl(t, TIOCSCTTY, (char *)0) < 0) + fatalperror(net, "ioctl(sctty)"); +# if defined(CRAY) + /* + * Close the hard fd to /dev/ttypXXX, and re-open through + * the indirect /dev/tty interface. + */ + close(t); + if ((t = open("/dev/tty", O_RDWR)) < 0) + fatalperror(net, "open(/dev/tty)"); +# endif +# else + /* + * We get our controlling tty assigned as a side-effect + * of opening up a tty device. But on BSD based systems, + * this only happens if our process group is zero. The + * setsid() call above may have set our pgrp, so clear + * it out before opening the tty... + */ + (void) setpgrp(0, 0); + close(open(line, O_RDWR)); +# endif + if (t != 0) + (void) dup2(t, 0); + if (t != 1) + (void) dup2(t, 1); + if (t != 2) + (void) dup2(t, 2); + if (t > 2) + close(t); + return(0); +} +#endif /* BSD <= 43 */ + +#ifdef NEWINIT +char *gen_id = "fe"; +#endif + +/* + * startslave(host) + * + * Given a hostname, do whatever + * is necessary to startup the login process on the slave side of the pty. + */ + +/* ARGSUSED */ + void +startslave(host, autologin, autoname) + char *host; + int autologin; + char *autoname; +{ + register int i; + long time(); + char name[256]; +#ifdef NEWINIT + extern char *ptyip; + struct init_request request; + void nologinproc(); + register int n; +#endif /* NEWINIT */ + +#if defined(AUTHENTICATION) + if (!autoname || !autoname[0]) + autologin = 0; + + if (autologin < auth_level) { + fatal(net, "Authorization failed"); + exit(1); + } +#endif + +#ifndef NEWINIT +# ifdef PARENT_DOES_UTMP + utmp_sig_init(); +# endif /* PARENT_DOES_UTMP */ + + if ((i = fork()) < 0) + fatalperror(net, "fork"); + if (i) { +# ifdef PARENT_DOES_UTMP + /* + * Cray parent will create utmp entry for child and send + * signal to child to tell when done. Child waits for signal + * before doing anything important. + */ + register int pid = i; + void sigjob P((int)); + + setpgrp(); + utmp_sig_reset(); /* reset handler to default */ + /* + * Create utmp entry for child + */ + (void) time(&wtmp.ut_time); + wtmp.ut_type = LOGIN_PROCESS; + wtmp.ut_pid = pid; + SCPYN(wtmp.ut_user, "LOGIN"); + SCPYN(wtmp.ut_host, host); + SCPYN(wtmp.ut_line, line + sizeof("/dev/") - 1); +#ifndef __hpux + SCPYN(wtmp.ut_id, wtmp.ut_line+3); +#else + SCPYN(wtmp.ut_id, wtmp.ut_line+7); +#endif + pututline(&wtmp); + endutent(); + if ((i = open(wtmpf, O_WRONLY|O_APPEND)) >= 0) { + (void) write(i, (char *)&wtmp, sizeof(struct utmp)); + (void) close(i); + } +#ifdef CRAY + (void) signal(WJSIGNAL, sigjob); +#endif + utmp_sig_notify(pid); +# endif /* PARENT_DOES_UTMP */ + } else { + getptyslave(autologin); + start_login(host, autologin, autoname); + /*NOTREACHED*/ + } +#else /* NEWINIT */ + + /* + * Init will start up login process if we ask nicely. We only wait + * for it to start up and begin normal telnet operation. + */ + if ((i = open(INIT_FIFO, O_WRONLY)) < 0) { + char tbuf[128]; + (void) sprintf(tbuf, "Can't open %s\n", INIT_FIFO); + fatalperror(net, tbuf); + } + memset((char *)&request, 0, sizeof(request)); + request.magic = INIT_MAGIC; + SCPYN(request.gen_id, gen_id); + SCPYN(request.tty_id, &line[8]); + SCPYN(request.host, host); + SCPYN(request.term_type, terminaltype ? terminaltype : "network"); +#if !defined(UNICOS5) + request.signal = SIGCLD; + request.pid = getpid(); +#endif +#ifdef BFTPDAEMON + /* + * Are we working as the bftp daemon? + */ + if (bftpd) { + SCPYN(request.exec_name, BFTPPATH); + } +#endif /* BFTPDAEMON */ + if (write(i, (char *)&request, sizeof(request)) < 0) { + char tbuf[128]; + (void) sprintf(tbuf, "Can't write to %s\n", INIT_FIFO); + fatalperror(net, tbuf); + } + (void) close(i); + (void) signal(SIGALRM, nologinproc); + for (i = 0; ; i++) { + char tbuf[128]; + alarm(15); + n = read(pty, ptyip, BUFSIZ); + if (i == 3 || n >= 0 || !gotalarm) + break; + gotalarm = 0; + sprintf(tbuf, "telnetd: waiting for /etc/init to start login process on %s\r\n", line); + (void) write(net, tbuf, strlen(tbuf)); + } + if (n < 0 && gotalarm) + fatal(net, "/etc/init didn't start login process"); + pcc += n; + alarm(0); + (void) signal(SIGALRM, SIG_DFL); + + return; +#endif /* NEWINIT */ +} + +char *envinit[3]; +extern char **environ; + + void +init_env() +{ + extern char *getenv(); + char **envp; + + envp = envinit; + if (*envp = getenv("TZ")) + *envp++ -= 3; +#if defined(CRAY) || defined(__hpux) + else + *envp++ = "TZ=GMT0"; +#endif + *envp = 0; + environ = envinit; +} + +#ifndef NEWINIT + +/* + * start_login(host) + * + * Assuming that we are now running as a child processes, this + * function will turn us into the login process. + */ + + void +start_login(host, autologin, name) + char *host; + int autologin; + char *name; +{ + register char *cp; + register char **argv; + char **addarg(), *user; + extern char *getenv(); +#ifdef UTMPX + register int pid = getpid(); + struct utmpx utmpx; +#endif +#ifdef SOLARIS + char *term; + char termbuf[64]; +#endif + +#ifdef UTMPX + /* + * Create utmp entry for child + */ + + bzero(&utmpx, sizeof(utmpx)); + SCPYN(utmpx.ut_user, ".telnet"); + SCPYN(utmpx.ut_line, line + sizeof("/dev/") - 1); + utmpx.ut_pid = pid; + utmpx.ut_id[0] = 't'; + utmpx.ut_id[1] = 'n'; + utmpx.ut_id[2] = SC_WILDC; + utmpx.ut_id[3] = SC_WILDC; + utmpx.ut_type = LOGIN_PROCESS; + (void) time(&utmpx.ut_tv.tv_sec); + if (makeutx(&utmpx) == NULL) + fatal(net, "makeutx failed"); +#endif + + scrub_env(); + + /* + * -h : pass on name of host. + * WARNING: -h is accepted by login if and only if + * getuid() == 0. + * -p : don't clobber the environment (so terminal type stays set). + * + * -f : force this login, he has already been authenticated + */ + argv = addarg(0, "login"); + +#if !defined(NO_LOGIN_H) + +# if defined (AUTHENTICATION) && defined(NO_LOGIN_F) && defined(LOGIN_R) + /* + * Don't add the "-h host" option if we are going + * to be adding the "-r host" option down below... + */ + if ((auth_level < 0) || (autologin != AUTH_VALID)) +# endif + { + argv = addarg(argv, "-h"); + argv = addarg(argv, host); +#ifdef SOLARIS + /* + * SVR4 version of -h takes TERM= as second arg, or - + */ + term = getenv("TERM"); + if (term == NULL || term[0] == 0) { + term = "-"; + } else { + strcpy(termbuf, "TERM="); + strncat(termbuf, term, sizeof(termbuf) - 6); + term = termbuf; + } + argv = addarg(argv, term); +#endif + } +#endif +#if !defined(NO_LOGIN_P) + argv = addarg(argv, "-p"); +#endif +#ifdef BFTPDAEMON + /* + * Are we working as the bftp daemon? If so, then ask login + * to start bftp instead of shell. + */ + if (bftpd) { + argv = addarg(argv, "-e"); + argv = addarg(argv, BFTPPATH); + } else +#endif +#if defined (SecurID) + /* + * don't worry about the -f that might get sent. + * A -s is supposed to override it anyhow. + */ + if (require_SecurID) + argv = addarg(argv, "-s"); +#endif +#if defined (AUTHENTICATION) + if (auth_level >= 0 && autologin == AUTH_VALID) { +# if !defined(NO_LOGIN_F) + argv = addarg(argv, "-f"); + argv = addarg(argv, name); +# else +# if defined(LOGIN_R) + /* + * We don't have support for "login -f", but we + * can fool /bin/login into thinking that we are + * rlogind, and allow us to log in without a + * password. The rlogin protocol expects + * local-user\0remote-user\0term/speed\0 + */ + + if (pty > 2) { + register char *cp; + char speed[128]; + int isecho, israw, xpty, len; + extern int def_rspeed; +# ifndef LOGIN_HOST + /* + * Tell login that we are coming from "localhost". + * If we passed in the real host name, then the + * user would have to allow .rhost access from + * every machine that they want authenticated + * access to work from, which sort of defeats + * the purpose of an authenticated login... + * So, we tell login that the session is coming + * from "localhost", and the user will only have + * to have "localhost" in their .rhost file. + */ +# define LOGIN_HOST "localhost" +# endif + argv = addarg(argv, "-r"); + argv = addarg(argv, LOGIN_HOST); + + xpty = pty; +# ifndef STREAMSPTY + pty = 0; +# else + ttyfd = 0; +# endif + init_termbuf(); + isecho = tty_isecho(); + israw = tty_israw(); + if (isecho || !israw) { + tty_setecho(0); /* Turn off echo */ + tty_setraw(1); /* Turn on raw */ + set_termbuf(); + } + len = strlen(name)+1; + write(xpty, name, len); + write(xpty, name, len); + sprintf(speed, "%s/%d", (cp = getenv("TERM")) ? cp : "", + (def_rspeed > 0) ? def_rspeed : 9600); + len = strlen(speed)+1; + write(xpty, speed, len); + + if (isecho || !israw) { + init_termbuf(); + tty_setecho(isecho); + tty_setraw(israw); + set_termbuf(); + if (!israw) { + /* + * Write a newline to ensure + * that login will be able to + * read the line... + */ + write(xpty, "\n", 1); + } + } + pty = xpty; + } +# else + argv = addarg(argv, name); +# endif +# endif + } else +#endif + if (user = getenv("USER")) { + if (strchr(user, '-')) { + syslog(LOG_ERR, "tried to pass user \"%s\" to login", + user); + fatal(net, "invalid user"); + } + argv = addarg(argv, getenv("USER")); +#if defined(LOGIN_ARGS) && defined(NO_LOGIN_P) + { + register char **cpp; + for (cpp = environ; *cpp; cpp++) + argv = addarg(argv, *cpp); + } +#endif + /* + * Assume that login will set the USER variable + * correctly. For SysV systems, this means that + * USER will no longer be set, just LOGNAME by + * login. (The problem is that if the auto-login + * fails, and the user then specifies a different + * account name, he can get logged in with both + * LOGNAME and USER in his environment, but the + * USER value will be wrong. + */ + unsetenv("USER"); + } +#if defined(AUTHENTICATION) && defined(NO_LOGIN_F) && defined(LOGIN_R) + if (pty > 2) + close(pty); +#endif + closelog(); + execv(_PATH_LOGIN, argv); + + syslog(LOG_ERR, "%s: %m\n", _PATH_LOGIN); + fatalperror(net, _PATH_LOGIN); + /*NOTREACHED*/ +} + + char ** +addarg(argv, val) + register char **argv; + register char *val; +{ + register char **cpp; + + if (argv == NULL) { + /* + * 10 entries, a leading length, and a null + */ + argv = (char **)malloc(sizeof(*argv) * 12); + if (argv == NULL) + return(NULL); + *argv++ = (char *)10; + *argv = (char *)0; + } + for (cpp = argv; *cpp; cpp++) + ; + if (cpp == &argv[(int)argv[-1]]) { + --argv; + *argv = (char *)((int)(*argv) + 10); + argv = (char **)realloc(argv, (int)(*argv) + 2); + if (argv == NULL) + return(NULL); + argv++; + cpp = &argv[(int)argv[-1] - 10]; + } + *cpp++ = val; + *cpp = 0; + return(argv); +} +#endif /* NEWINIT */ + +/* + * scrub_env() + * + * Remove a few things from the environment that + * don't need to be there. + */ +scrub_env() +{ + register char **cpp, **cpp2; + + for (cpp2 = cpp = environ; *cpp; cpp++) { +#ifdef __FreeBSD__ + if (strncmp(*cpp, "LD_LIBRARY_PATH=", 16) && + strncmp(*cpp, "LD_PRELOAD=", 11) && +#else + if (strncmp(*cpp, "LD_", 3) && + strncmp(*cpp, "_RLD_", 5) && + strncmp(*cpp, "LIBPATH=", 8) && +#endif + strncmp(*cpp, "IFS=", 4)) + *cpp2++ = *cpp; + } + *cpp2 = 0; +} + +/* + * cleanup() + * + * This is the routine to call when we are all through, to + * clean up anything that needs to be cleaned up. + */ + /* ARGSUSED */ + void +cleanup(sig) + int sig; +{ +#ifndef PARENT_DOES_UTMP +# if (BSD > 43) || defined(convex) + char *p; + + p = line + sizeof("/dev/") - 1; + if (logout(p)) + logwtmp(p, "", ""); + (void)chmod(line, 0666); + (void)chown(line, 0, 0); + *p = 'p'; + (void)chmod(line, 0666); + (void)chown(line, 0, 0); + (void) shutdown(net, 2); + exit(1); +# else + void rmut(); + + rmut(); + vhangup(); /* XXX */ + (void) shutdown(net, 2); + exit(1); +# endif +#else /* PARENT_DOES_UTMP */ +# ifdef NEWINIT + (void) shutdown(net, 2); + exit(1); +# else /* NEWINIT */ +# ifdef CRAY + static int incleanup = 0; + register int t; + + /* + * 1: Pick up the zombie, if we are being called + * as the signal handler. + * 2: If we are a nested cleanup(), return. + * 3: Try to clean up TMPDIR. + * 4: Fill in utmp with shutdown of process. + * 5: Close down the network and pty connections. + * 6: Finish up the TMPDIR cleanup, if needed. + */ + if (sig == SIGCHLD) + while (waitpid(-1, 0, WNOHANG) > 0) + ; /* VOID */ + t = sigblock(sigmask(SIGCHLD)); + if (incleanup) { + sigsetmask(t); + return; + } + incleanup = 1; + sigsetmask(t); + if (secflag) { + /* + * We need to set ourselves back to a null + * label to clean up. + */ + + setulvl(sysv.sy_minlvl); + setucmp((long)0); + } + + t = cleantmp(&wtmp); + setutent(); /* just to make sure */ +# endif /* CRAY */ + rmut(line); + close(pty); + (void) shutdown(net, 2); +# ifdef CRAY + if (t == 0) + cleantmp(&wtmp); +# endif /* CRAY */ + exit(1); +# endif /* NEWINT */ +#endif /* PARENT_DOES_UTMP */ +} + +#if defined(PARENT_DOES_UTMP) && !defined(NEWINIT) +/* + * _utmp_sig_rcv + * utmp_sig_init + * utmp_sig_wait + * These three functions are used to coordinate the handling of + * the utmp file between the server and the soon-to-be-login shell. + * The server actually creates the utmp structure, the child calls + * utmp_sig_wait(), until the server calls utmp_sig_notify() and + * signals the future-login shell to proceed. + */ +static int caught=0; /* NZ when signal intercepted */ +static void (*func)(); /* address of previous handler */ + + void +_utmp_sig_rcv(sig) + int sig; +{ + caught = 1; + (void) signal(SIGUSR1, func); +} + + void +utmp_sig_init() +{ + /* + * register signal handler for UTMP creation + */ + if ((int)(func = signal(SIGUSR1, _utmp_sig_rcv)) == -1) + fatalperror(net, "telnetd/signal"); +} + + void +utmp_sig_reset() +{ + (void) signal(SIGUSR1, func); /* reset handler to default */ +} + +# ifdef __hpux +# define sigoff() /* do nothing */ +# define sigon() /* do nothing */ +# endif + + void +utmp_sig_wait() +{ + /* + * Wait for parent to write our utmp entry. + */ + sigoff(); + while (caught == 0) { + pause(); /* wait until we get a signal (sigon) */ + sigoff(); /* turn off signals while we check caught */ + } + sigon(); /* turn on signals again */ +} + + void +utmp_sig_notify(pid) +{ + kill(pid, SIGUSR1); +} + +# ifdef CRAY +static int gotsigjob = 0; + + /*ARGSUSED*/ + void +sigjob(sig) + int sig; +{ + register int jid; + register struct jobtemp *jp; + + while ((jid = waitjob(NULL)) != -1) { + if (jid == 0) { + return; + } + gotsigjob++; + jobend(jid, NULL, NULL); + } +} + +/* + * Clean up the TMPDIR that login created. + * The first time this is called we pick up the info + * from the utmp. If the job has already gone away, + * then we'll clean up and be done. If not, then + * when this is called the second time it will wait + * for the signal that the job is done. + */ + int +cleantmp(wtp) + register struct utmp *wtp; +{ + struct utmp *utp; + static int first = 1; + register int mask, omask, ret; + extern struct utmp *getutid P((const struct utmp *_Id)); + + + mask = sigmask(WJSIGNAL); + + if (first == 0) { + omask = sigblock(mask); + while (gotsigjob == 0) + sigpause(omask); + return(1); + } + first = 0; + setutent(); /* just to make sure */ + + utp = getutid(wtp); + if (utp == 0) { + syslog(LOG_ERR, "Can't get /etc/utmp entry to clean TMPDIR"); + return(-1); + } + /* + * Nothing to clean up if the user shell was never started. + */ + if (utp->ut_type != USER_PROCESS || utp->ut_jid == 0) + return(1); + + /* + * Block the WJSIGNAL while we are in jobend(). + */ + omask = sigblock(mask); + ret = jobend(utp->ut_jid, utp->ut_tpath, utp->ut_user); + sigsetmask(omask); + return(ret); +} + + int +jobend(jid, path, user) + register int jid; + register char *path; + register char *user; +{ + static int saved_jid = 0; + static char saved_path[sizeof(wtmp.ut_tpath)+1]; + static char saved_user[sizeof(wtmp.ut_user)+1]; + + if (path) { + strncpy(saved_path, path, sizeof(wtmp.ut_tpath)); + strncpy(saved_user, user, sizeof(wtmp.ut_user)); + saved_path[sizeof(saved_path)] = '\0'; + saved_user[sizeof(saved_user)] = '\0'; + } + if (saved_jid == 0) { + saved_jid = jid; + return(0); + } + cleantmpdir(jid, saved_path, saved_user); + return(1); +} + +/* + * Fork a child process to clean up the TMPDIR + */ +cleantmpdir(jid, tpath, user) + register int jid; + register char *tpath; + register char *user; +{ + switch(fork()) { + case -1: + syslog(LOG_ERR, "TMPDIR cleanup(%s): fork() failed: %m\n", + tpath); + break; + case 0: + execl(CLEANTMPCMD, CLEANTMPCMD, user, tpath, 0); + syslog(LOG_ERR, "TMPDIR cleanup(%s): execl(%s) failed: %m\n", + tpath, CLEANTMPCMD); + exit(1); + default: + /* + * Forget about child. We will exit, and + * /etc/init will pick it up. + */ + break; + } +} +# endif /* CRAY */ +#endif /* defined(PARENT_DOES_UTMP) && !defined(NEWINIT) */ + +/* + * rmut() + * + * This is the function called by cleanup() to + * remove the utmp entry for this person. + */ + +#ifdef UTMPX + void +rmut() +{ + register f; + int found = 0; + struct utmp *u, *utmp; + int nutmp; + struct stat statbf; + + struct utmpx *utxp, utmpx; + + /* + * This updates the utmpx and utmp entries and make a wtmp/x entry + */ + + SCPYN(utmpx.ut_line, line + sizeof("/dev/") - 1); + utxp = getutxline(&utmpx); + if (utxp) { + utxp->ut_type = DEAD_PROCESS; + utxp->ut_exit.e_termination = 0; + utxp->ut_exit.e_exit = 0; + (void) time(&utmpx.ut_tv.tv_sec); + utmpx.ut_tv.tv_usec = 0; + modutx(utxp); + } + endutxent(); +} /* end of rmut */ +#endif + +#if !defined(UTMPX) && !(defined(CRAY) || defined(__hpux)) && BSD <= 43 + void +rmut() +{ + register f; + int found = 0; + struct utmp *u, *utmp; + int nutmp; + struct stat statbf; + + f = open(utmpf, O_RDWR); + if (f >= 0) { + (void) fstat(f, &statbf); + utmp = (struct utmp *)malloc((unsigned)statbf.st_size); + if (!utmp) + syslog(LOG_ERR, "utmp malloc failed"); + if (statbf.st_size && utmp) { + nutmp = read(f, (char *)utmp, (int)statbf.st_size); + nutmp /= sizeof(struct utmp); + + for (u = utmp ; u < &utmp[nutmp] ; u++) { + if (SCMPN(u->ut_line, line+5) || + u->ut_name[0]==0) + continue; + (void) lseek(f, ((long)u)-((long)utmp), L_SET); + SCPYN(u->ut_name, ""); + SCPYN(u->ut_host, ""); + (void) time(&u->ut_time); + (void) write(f, (char *)u, sizeof(wtmp)); + found++; + } + } + (void) close(f); + } + if (found) { + f = open(wtmpf, O_WRONLY|O_APPEND); + if (f >= 0) { + SCPYN(wtmp.ut_line, line+5); + SCPYN(wtmp.ut_name, ""); + SCPYN(wtmp.ut_host, ""); + (void) time(&wtmp.ut_time); + (void) write(f, (char *)&wtmp, sizeof(wtmp)); + (void) close(f); + } + } + (void) chmod(line, 0666); + (void) chown(line, 0, 0); + line[strlen("/dev/")] = 'p'; + (void) chmod(line, 0666); + (void) chown(line, 0, 0); +} /* end of rmut */ +#endif /* CRAY */ + +#ifdef __hpux +rmut (line) +char *line; +{ + struct utmp utmp; + struct utmp *utptr; + int fd; /* for /etc/wtmp */ + + utmp.ut_type = USER_PROCESS; + (void) strncpy(utmp.ut_id, line+12, sizeof(utmp.ut_id)); + (void) setutent(); + utptr = getutid(&utmp); + /* write it out only if it exists */ + if (utptr) { + utptr->ut_type = DEAD_PROCESS; + utptr->ut_time = time((long *) 0); + (void) pututline(utptr); + /* set wtmp entry if wtmp file exists */ + if ((fd = open(wtmpf, O_WRONLY | O_APPEND)) >= 0) { + (void) write(fd, utptr, sizeof(utmp)); + (void) close(fd); + } + } + (void) endutent(); + + (void) chmod(line, 0666); + (void) chown(line, 0, 0); + line[14] = line[13]; + line[13] = line[12]; + line[8] = 'm'; + line[9] = '/'; + line[10] = 'p'; + line[11] = 't'; + line[12] = 'y'; + (void) chmod(line, 0666); + (void) chown(line, 0, 0); +} +#endif diff --git a/libexec/telnetd/telnetd.8 b/libexec/telnetd/telnetd.8 new file mode 100644 index 0000000..fee5526 --- /dev/null +++ b/libexec/telnetd/telnetd.8 @@ -0,0 +1,605 @@ +.\" Copyright (c) 1983, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)telnetd.8 8.3 (Berkeley) 3/1/94 +.\" +.Dd March 1, 1994 +.Dt TELNETD 8 +.Os BSD 4.2 +.Sh NAME +.Nm telnetd +.Nd DARPA +.Tn TELNET +protocol server +.Sh SYNOPSIS +.Nm /usr/libexec/telnetd +.Op Fl BUhlkns +.Op Fl D Ar debugmode +.Op Fl I Ns Ar initid +.Op Fl S Ar tos +.Op Fl X Ar authtype +.Op Fl a Ar authmode +.Op Fl edebug +.Op Fl r Ns Ar lowpty-highpty +.Op Fl u Ar len +.Op Fl debug Op Ar port +.Sh DESCRIPTION +The +.Nm telnetd +command is a server which supports the +.Tn DARPA +standard +.Tn TELNET +virtual terminal protocol. +.Nm Telnetd +is normally invoked by the internet server (see +.Xr inetd 8 ) +for requests to connect to the +.Tn TELNET +port as indicated by the +.Pa /etc/services +file (see +.Xr services 5 ) . +The +.Fl debug +option may be used to start up +.Nm telnetd +manually, instead of through +.Xr inetd 8 . +If started up this way, +.Ar port +may be specified to run +.Nm telnetd +on an alternate +.Tn TCP +port number. +.Pp +The +.Nm telnetd +command accepts the following options: +.Bl -tag -width "-a authmode" +.It Fl a Ar authmode +This option may be used for specifying what mode should +be used for authentication. +Note that this option is only useful if +.Nm telnetd +has been compiled with support for the +.Dv AUTHENTICATION +option. +There are several valid values for +.Ar authmode: +.Bl -tag -width debug +.It debug +Turns on authentication debugging code. +.It user +Only allow connections when the remote user +can provide valid authentication information +to identify the remote user, +and is allowed access to the specified account +without providing a password. +.It valid +Only allow connections when the remote user +can provide valid authentication information +to identify the remote user. +The +.Xr login 1 +command will provide any additional user verification +needed if the remote user is not allowed automatic +access to the specified account. +.It other +Only allow connections that supply some authentication information. +This option is currently not supported +by any of the existing authentication mechanisms, +and is thus the same as specifying +.Fl a +.Cm valid . +.It none +This is the default state. +Authentication information is not required. +If no or insufficient authentication information +is provided, then the +.Xr login 1 +program will provide the necessary user +verification. +.It off +This disables the authentication code. +All user verification will happen through the +.Xr login 1 +program. +.El +.It Fl B +Specifies bftp server mode. In this mode, +.Nm telnetd +causes login to start a +.Xr bftp 1 +session rather than the user's +normal shell. In bftp daemon mode normal +logins are not supported, and it must be used +on a port other than the normal +.Tn TELNET +port. +.It Fl D Ar debugmode +This option may be used for debugging purposes. +This allows +.Nm telnetd +to print out debugging information +to the connection, allowing the user to see what +.Nm telnetd +is doing. +There are several possible values for +.Ar debugmode: +.Bl -tag -width exercise +.It Cm options +Prints information about the negotiation of +.Tn TELNET +options. +.It Cm report +Prints the +.Cm options +information, plus some additional information +about what processing is going on. +.It Cm netdata +Displays the data stream received by +.Nm telnetd. +.It Cm ptydata +Displays data written to the pty. +.It Cm exercise +Has not been implemented yet. +.El +.It Fl debug +Enables debugging on each socket created by +.Nm telnetd +(see +.Dv SO_DEBUG +in +.Xr socket 2 ) . +.It Fl edebug +If +.Nm telnetd +has been compiled with support for data encryption, then the +.Fl edebug +option may be used to enable encryption debugging code. +.It Fl h +Disables the printing of host-specific information before +login has been completed. +.It Fl I Ar initid +This option is only applicable to +.Tn UNICOS +systems prior to 7.0. +It specifies the +.Dv ID +from +.Pa /etc/inittab +to use when init starts login sessions. The default +.Dv ID +is +.Dv fe. +.It Fl k +This option is only useful if +.Nm telnetd +has been compiled with both linemode and kludge linemode +support. If the +.Fl k +option is specified, then if the remote client does not +support the +.Dv LINEMODE +option, then +.Nm telnetd +will operate in character at a time mode. +It will still support kludge linemode, but will only +go into kludge linemode if the remote client requests +it. +(This is done by by the client sending +.Dv DONT SUPPRESS-GO-AHEAD +and +.Dv DONT ECHO . ) +The +.Fl k +option is most useful when there are remote clients +that do not support kludge linemode, but pass the heuristic +(if they respond with +.Dv WILL TIMING-MARK +in response to a +.Dv DO TIMING-MARK) +for kludge linemode support. +.It Fl l +Specifies line mode. Tries to force clients to use line- +at-a-time mode. +If the +.Dv LINEMODE +option is not supported, it will go +into kludge linemode. +.It Fl n +Disable +.Dv TCP +keep-alives. Normally +.Nm telnetd +enables the +.Tn TCP +keep-alive mechanism to probe connections that +have been idle for some period of time to determine +if the client is still there, so that idle connections +from machines that have crashed or can no longer +be reached may be cleaned up. +.It Fl r Ar lowpty-highpty +This option is only enabled when +.Nm telnetd +is compiled for +.Dv UNICOS. +It specifies an inclusive range of pseudo-terminal devices to +use. If the system has sysconf variable +.Dv _SC_CRAY_NPTY +configured, the default pty search range is 0 to +.Dv _SC_CRAY_NPTY; +otherwise, the default range is 0 to 128. Either +.Ar lowpty +or +.Ar highpty +may be omitted to allow changing +either end of the search range. If +.Ar lowpty +is omitted, the - character is still required so that +.Nm telnetd +can differentiate +.Ar highpty +from +.Ar lowpty . +.It Fl s +This option is only enabled if +.Nm telnetd +is compiled with support for +.Tn SecurID +cards. +It causes the +.Fl s +option to be passed on to +.Xr login 1 , +and thus is only useful if +.Xr login 1 +supports the +.Fl s +flag to indicate that only +.Tn SecurID +validated logins are allowed, and is +usually useful for controlling remote logins +from outside of a firewall. +.It Fl S Ar tos +.It Fl u Ar len +This option is used to specify the size of the field +in the +.Dv utmp +structure that holds the remote host name. +If the resolved host name is longer than +.Ar len , +the dotted decimal value will be used instead. +This allows hosts with very long host names that +overflow this field to still be uniquely identified. +Specifying +.Fl u0 +indicates that only dotted decimal addresses +should be put into the +.Pa utmp +file. +.It Fl U +This option causes +.Nm telnetd +to refuse connections from addresses that +cannot be mapped back into a symbolic name +via the +.Xr gethostbyaddr 3 +routine. +.It Fl X Ar authtype +This option is only valid if +.Nm telnetd +has been built with support for the authentication option. +It disables the use of +.Ar authtype +authentication, and +can be used to temporarily disable +a specific authentication type without having to recompile +.Nm telnetd . +.El +.Pp +.Nm Telnetd +operates by allocating a pseudo-terminal device (see +.Xr pty 4 ) +for a client, then creating a login process which has +the slave side of the pseudo-terminal as +.Dv stdin , +.Dv stdout +and +.Dv stderr . +.Nm Telnetd +manipulates the master side of the pseudo-terminal, +implementing the +.Tn TELNET +protocol and passing characters +between the remote client and the login process. +.Pp +When a +.Tn TELNET +session is started up, +.Nm telnetd +sends +.Tn TELNET +options to the client side indicating +a willingness to do the +following +.Tn TELNET +options, which are described in more detail below: +.Bd -literal -offset indent +DO AUTHENTICATION +WILL ENCRYPT +DO TERMINAL TYPE +DO TSPEED +DO XDISPLOC +DO NEW-ENVIRON +DO ENVIRON +WILL SUPPRESS GO AHEAD +DO ECHO +DO LINEMODE +DO NAWS +WILL STATUS +DO LFLOW +DO TIMING-MARK +.Ed +.Pp +The pseudo-terminal allocated to the client is configured +to operate in \*(lqcooked\*(rq mode, and with +.Dv XTABS and +.Dv CRMOD +enabled (see +.Xr tty 4 ) . +.Pp +.Nm Telnetd +has support for enabling locally the following +.Tn TELNET +options: +.Bl -tag -width "DO AUTHENTICATION" +.It "WILL ECHO" +When the +.Dv LINEMODE +option is enabled, a +.Dv WILL ECHO +or +.Dv WONT ECHO +will be sent to the client to indicate the +current state of terminal echoing. +When terminal echo is not desired, a +.Dv WILL ECHO +is sent to indicate that +.Tn telnetd +will take care of echoing any data that needs to be +echoed to the terminal, and then nothing is echoed. +When terminal echo is desired, a +.Dv WONT ECHO +is sent to indicate that +.Tn telnetd +will not be doing any terminal echoing, so the +client should do any terminal echoing that is needed. +.It "WILL BINARY" +Indicates that the client is willing to send a +8 bits of data, rather than the normal 7 bits +of the Network Virtual Terminal. +.It "WILL SGA" +Indicates that it will not be sending +.Dv IAC GA, +go ahead, commands. +.It "WILL STATUS" +Indicates a willingness to send the client, upon +request, of the current status of all +.Tn TELNET +options. +.It "WILL TIMING-MARK" +Whenever a +.Dv DO TIMING-MARK +command is received, it is always responded +to with a +.Dv WILL TIMING-MARK +.It "WILL LOGOUT" +When a +.Dv DO LOGOUT +is received, a +.Dv WILL LOGOUT +is sent in response, and the +.Tn TELNET +session is shut down. +.It "WILL ENCRYPT" +Only sent if +.Nm telnetd +is compiled with support for data encryption, and +indicates a willingness to decrypt +the data stream. +.El +.Pp +.Nm Telnetd +has support for enabling remotely the following +.Tn TELNET +options: +.Bl -tag -width "DO AUTHENTICATION" +.It "DO BINARY" +Sent to indicate that +.Tn telnetd +is willing to receive an 8 bit data stream. +.It "DO LFLOW" +Requests that the client handle flow control +characters remotely. +.It "DO ECHO" +This is not really supported, but is sent to identify a 4.2BSD +.Xr telnet 1 +client, which will improperly respond with +.Dv WILL ECHO. +If a +.Dv WILL ECHO +is received, a +.Dv DONT ECHO +will be sent in response. +.It "DO TERMINAL-TYPE" +Indicates a desire to be able to request the +name of the type of terminal that is attached +to the client side of the connection. +.It "DO SGA" +Indicates that it does not need to receive +.Dv IAC GA, +the go ahead command. +.It "DO NAWS" +Requests that the client inform the server when +the window (display) size changes. +.It "DO TERMINAL-SPEED" +Indicates a desire to be able to request information +about the speed of the serial line to which +the client is attached. +.It "DO XDISPLOC" +Indicates a desire to be able to request the name +of the X windows display that is associated with +the telnet client. +.It "DO NEW-ENVIRON" +Indicates a desire to be able to request environment +variable information, as described in RFC 1572. +.It "DO ENVIRON" +Indicates a desire to be able to request environment +variable information, as described in RFC 1408. +.It "DO LINEMODE" +Only sent if +.Nm telnetd +is compiled with support for linemode, and +requests that the client do line by line processing. +.It "DO TIMING-MARK" +Only sent if +.Nm telnetd +is compiled with support for both linemode and +kludge linemode, and the client responded with +.Dv WONT LINEMODE. +If the client responds with +.Dv WILL TM, +the it is assumed that the client supports +kludge linemode. +Note that the +.Op Fl k +option can be used to disable this. +.It "DO AUTHENTICATION" +Only sent if +.Nm telnetd +is compiled with support for authentication, and +indicates a willingness to receive authentication +information for automatic login. +.It "DO ENCRYPT" +Only sent if +.Nm telnetd +is compiled with support for data encryption, and +indicates a willingness to decrypt +the data stream. +.Sh ENVIRONMENT +.Sh FILES +.Pa /etc/services +.br +.Pa /etc/inittab +(UNICOS systems only) +.br +.Pa /etc/iptos +(if supported) +.br +.Pa /usr/ucb/bftp +(if supported) +.Sh "SEE ALSO" +.Xr telnet 1 , +.Xr login 1 , +.Xr bftp 1 +(if supported) +.Sh STANDARDS +.Bl -tag -compact -width RFC-1572 +.It Cm RFC-854 +.Tn TELNET +PROTOCOL SPECIFICATION +.It Cm RFC-855 +TELNET OPTION SPECIFICATIONS +.It Cm RFC-856 +TELNET BINARY TRANSMISSION +.It Cm RFC-857 +TELNET ECHO OPTION +.It Cm RFC-858 +TELNET SUPPRESS GO AHEAD OPTION +.It Cm RFC-859 +TELNET STATUS OPTION +.It Cm RFC-860 +TELNET TIMING MARK OPTION +.It Cm RFC-861 +TELNET EXTENDED OPTIONS - LIST OPTION +.It Cm RFC-885 +TELNET END OF RECORD OPTION +.It Cm RFC-1073 +Telnet Window Size Option +.It Cm RFC-1079 +Telnet Terminal Speed Option +.It Cm RFC-1091 +Telnet Terminal-Type Option +.It Cm RFC-1096 +Telnet X Display Location Option +.It Cm RFC-1123 +Requirements for Internet Hosts -- Application and Support +.It Cm RFC-1184 +Telnet Linemode Option +.It Cm RFC-1372 +Telnet Remote Flow Control Option +.It Cm RFC-1416 +Telnet Authentication Option +.It Cm RFC-1411 +Telnet Authentication: Kerberos Version 4 +.It Cm RFC-1412 +Telnet Authentication: SPX +.It Cm RFC-1571 +Telnet Environment Option Interoperability Issues +.It Cm RFC-1572 +Telnet Environment Option +.Sh BUGS +Some +.Tn TELNET +commands are only partially implemented. +.Pp +Because of bugs in the original 4.2 BSD +.Xr telnet 1 , +.Nm telnetd +performs some dubious protocol exchanges to try to discover if the remote +client is, in fact, a 4.2 BSD +.Xr telnet 1 . +.Pp +Binary mode +has no common interpretation except between similar operating systems +(Unix in this case). +.Pp +The terminal type name received from the remote client is converted to +lower case. +.Pp +.Nm Telnetd +never sends +.Tn TELNET +.Dv IAC GA +(go ahead) commands. diff --git a/libexec/telnetd/telnetd.c b/libexec/telnetd/telnetd.c new file mode 100644 index 0000000..b83b6a0 --- /dev/null +++ b/libexec/telnetd/telnetd.c @@ -0,0 +1,1535 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)telnetd.c 8.2 (Berkeley) 12/15/93"; +#endif /* not lint */ + +#include "telnetd.h" +#include "pathnames.h" + +#if defined(_SC_CRAY_SECURE_SYS) && !defined(SCM_SECURITY) +/* + * UNICOS 6.0/6.1 do not have SCM_SECURITY defined, so we can + * use it to tell us to turn off all the socket security code, + * since that is only used in UNICOS 7.0 and later. + */ +# undef _SC_CRAY_SECURE_SYS +#endif + +#if defined(_SC_CRAY_SECURE_SYS) +#include <sys/sysv.h> +#include <sys/secdev.h> +# ifdef SO_SEC_MULTI /* 8.0 code */ +#include <sys/secparm.h> +#include <sys/usrv.h> +# endif /* SO_SEC_MULTI */ +int secflag; +char tty_dev[16]; +struct secdev dv; +struct sysv sysv; +# ifdef SO_SEC_MULTI /* 8.0 code */ +struct socksec ss; +# else /* SO_SEC_MULTI */ /* 7.0 code */ +struct socket_security ss; +# endif /* SO_SEC_MULTI */ +#endif /* _SC_CRAY_SECURE_SYS */ + +#if defined(AUTHENTICATION) +#include <libtelnet/auth.h> +int auth_level = 0; +#endif +#if defined(SecurID) +int require_SecurID = 0; +#endif + +extern int utmp_len; +int registerd_host_only = 0; + +#ifdef STREAMSPTY +# include <stropts.h> +# include <termio.h> +/* make sure we don't get the bsd version */ +# include "/usr/include/sys/tty.h" +# include <sys/ptyvar.h> + +/* + * Because of the way ptyibuf is used with streams messages, we need + * ptyibuf+1 to be on a full-word boundary. The following wierdness + * is simply to make that happen. + */ +long ptyibufbuf[BUFSIZ/sizeof(long)+1]; +char *ptyibuf = ((char *)&ptyibufbuf[1])-1; +char *ptyip = ((char *)&ptyibufbuf[1])-1; +char ptyibuf2[BUFSIZ]; +unsigned char ctlbuf[BUFSIZ]; +struct strbuf strbufc, strbufd; + +int readstream(); + +#else /* ! STREAMPTY */ + +/* + * I/O data buffers, + * pointers, and counters. + */ +char ptyibuf[BUFSIZ], *ptyip = ptyibuf; +char ptyibuf2[BUFSIZ]; + +#endif /* ! STREAMPTY */ + +int hostinfo = 1; /* do we print login banner? */ + +#ifdef CRAY +extern int newmap; /* nonzero if \n maps to ^M^J */ +int lowpty = 0, highpty; /* low, high pty numbers */ +#endif /* CRAY */ + +int debug = 0; +int keepalive = 1; +char *progname; + +extern void usage P((void)); + +/* + * The string to pass to getopt(). We do it this way so + * that only the actual options that we support will be + * passed off to getopt(). + */ +char valid_opts[] = { + 'd', ':', 'h', 'k', 'n', 'S', ':', 'u', ':', 'U', +#ifdef AUTHENTICATION + 'a', ':', 'X', ':', +#endif +#ifdef BFTPDAEMON + 'B', +#endif +#ifdef DIAGNOSTICS + 'D', ':', +#endif +#if defined(CRAY) && defined(NEWINIT) + 'I', ':', +#endif +#ifdef LINEMODE + 'l', +#endif +#ifdef CRAY + 'r', ':', +#endif +#ifdef SecurID + 's', +#endif + '\0' +}; + +main(argc, argv) + char *argv[]; +{ + struct sockaddr_in from; + int on = 1, fromlen; + register int ch; + extern char *optarg; + extern int optind; +#if defined(IPPROTO_IP) && defined(IP_TOS) + int tos = -1; +#endif + + pfrontp = pbackp = ptyobuf; + netip = netibuf; + nfrontp = nbackp = netobuf; + + progname = *argv; + +#ifdef CRAY + /* + * Get number of pty's before trying to process options, + * which may include changing pty range. + */ + highpty = getnpty(); +#endif /* CRAY */ + + while ((ch = getopt(argc, argv, valid_opts)) != EOF) { + switch(ch) { + +#ifdef AUTHENTICATION + case 'a': + /* + * Check for required authentication level + */ + if (strcmp(optarg, "debug") == 0) { + extern int auth_debug_mode; + auth_debug_mode = 1; + } else if (strcasecmp(optarg, "none") == 0) { + auth_level = 0; + } else if (strcasecmp(optarg, "other") == 0) { + auth_level = AUTH_OTHER; + } else if (strcasecmp(optarg, "user") == 0) { + auth_level = AUTH_USER; + } else if (strcasecmp(optarg, "valid") == 0) { + auth_level = AUTH_VALID; + } else if (strcasecmp(optarg, "off") == 0) { + /* + * This hack turns off authentication + */ + auth_level = -1; + } else { + fprintf(stderr, + "telnetd: unknown authorization level for -a\n"); + } + break; +#endif /* AUTHENTICATION */ + +#ifdef BFTPDAEMON + case 'B': + bftpd++; + break; +#endif /* BFTPDAEMON */ + + case 'd': + if (strcmp(optarg, "ebug") == 0) { + debug++; + break; + } + usage(); + /* NOTREACHED */ + break; + +#ifdef DIAGNOSTICS + case 'D': + /* + * Check for desired diagnostics capabilities. + */ + if (!strcmp(optarg, "report")) { + diagnostic |= TD_REPORT|TD_OPTIONS; + } else if (!strcmp(optarg, "exercise")) { + diagnostic |= TD_EXERCISE; + } else if (!strcmp(optarg, "netdata")) { + diagnostic |= TD_NETDATA; + } else if (!strcmp(optarg, "ptydata")) { + diagnostic |= TD_PTYDATA; + } else if (!strcmp(optarg, "options")) { + diagnostic |= TD_OPTIONS; + } else { + usage(); + /* NOT REACHED */ + } + break; +#endif /* DIAGNOSTICS */ + + + case 'h': + hostinfo = 0; + break; + +#if defined(CRAY) && defined(NEWINIT) + case 'I': + { + extern char *gen_id; + gen_id = optarg; + break; + } +#endif /* defined(CRAY) && defined(NEWINIT) */ + +#ifdef LINEMODE + case 'l': + alwayslinemode = 1; + break; +#endif /* LINEMODE */ + + case 'k': +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + lmodetype = NO_AUTOKLUDGE; +#else + /* ignore -k option if built without kludge linemode */ +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + break; + + case 'n': + keepalive = 0; + break; + +#ifdef CRAY + case 'r': + { + char *strchr(); + char *c; + + /* + * Allow the specification of alterations + * to the pty search range. It is legal to + * specify only one, and not change the + * other from its default. + */ + c = strchr(optarg, '-'); + if (c) { + *c++ = '\0'; + highpty = atoi(c); + } + if (*optarg != '\0') + lowpty = atoi(optarg); + if ((lowpty > highpty) || (lowpty < 0) || + (highpty > 32767)) { + usage(); + /* NOT REACHED */ + } + break; + } +#endif /* CRAY */ + +#ifdef SecurID + case 's': + /* SecurID required */ + require_SecurID = 1; + break; +#endif /* SecurID */ + case 'S': +#ifdef HAS_GETTOS + if ((tos = parsetos(optarg, "tcp")) < 0) + fprintf(stderr, "%s%s%s\n", + "telnetd: Bad TOS argument '", optarg, + "'; will try to use default TOS"); +#else + fprintf(stderr, "%s%s\n", "TOS option unavailable; ", + "-S flag not supported\n"); +#endif + break; + + case 'u': + utmp_len = atoi(optarg); + break; + + case 'U': + registerd_host_only = 1; + break; + +#ifdef AUTHENTICATION + case 'X': + /* + * Check for invalid authentication types + */ + auth_disable_name(optarg); + break; +#endif /* AUTHENTICATION */ + + default: + fprintf(stderr, "telnetd: %c: unknown option\n", ch); + /* FALLTHROUGH */ + case '?': + usage(); + /* NOTREACHED */ + } + } + + argc -= optind; + argv += optind; + + if (debug) { + int s, ns, foo; + struct servent *sp; + static struct sockaddr_in sin = { AF_INET }; + + if (argc > 1) { + usage(); + /* NOT REACHED */ + } else if (argc == 1) { + if (sp = getservbyname(*argv, "tcp")) { + sin.sin_port = sp->s_port; + } else { + sin.sin_port = atoi(*argv); + if ((int)sin.sin_port <= 0) { + fprintf(stderr, "telnetd: %s: bad port #\n", *argv); + usage(); + /* NOT REACHED */ + } + sin.sin_port = htons((u_short)sin.sin_port); + } + } else { + sp = getservbyname("telnet", "tcp"); + if (sp == 0) { + fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); + exit(1); + } + sin.sin_port = sp->s_port; + } + + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) { + perror("telnetd: socket");; + exit(1); + } + (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, + (char *)&on, sizeof(on)); + if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) { + perror("bind"); + exit(1); + } + if (listen(s, 1) < 0) { + perror("listen"); + exit(1); + } + foo = sizeof sin; + ns = accept(s, (struct sockaddr *)&sin, &foo); + if (ns < 0) { + perror("accept"); + exit(1); + } + (void) dup2(ns, 0); + (void) close(ns); + (void) close(s); +#ifdef convex + } else if (argc == 1) { + ; /* VOID*/ /* Just ignore the host/port name */ +#endif + } else if (argc > 0) { + usage(); + /* NOT REACHED */ + } + +#if defined(_SC_CRAY_SECURE_SYS) + secflag = sysconf(_SC_CRAY_SECURE_SYS); + + /* + * Get socket's security label + */ + if (secflag) { + int szss = sizeof(ss); +#ifdef SO_SEC_MULTI /* 8.0 code */ + int sock_multi; + int szi = sizeof(int); +#endif /* SO_SEC_MULTI */ + + bzero((char *)&dv, sizeof(dv)); + + if (getsysv(&sysv, sizeof(struct sysv)) != 0) { + perror("getsysv"); + exit(1); + } + + /* + * Get socket security label and set device values + * {security label to be set on ttyp device} + */ +#ifdef SO_SEC_MULTI /* 8.0 code */ + if ((getsockopt(0, SOL_SOCKET, SO_SECURITY, + (char *)&ss, &szss) < 0) || + (getsockopt(0, SOL_SOCKET, SO_SEC_MULTI, + (char *)&sock_multi, &szi) < 0)) { + perror("getsockopt"); + exit(1); + } else { + dv.dv_actlvl = ss.ss_actlabel.lt_level; + dv.dv_actcmp = ss.ss_actlabel.lt_compart; + if (!sock_multi) { + dv.dv_minlvl = dv.dv_maxlvl = dv.dv_actlvl; + dv.dv_valcmp = dv.dv_actcmp; + } else { + dv.dv_minlvl = ss.ss_minlabel.lt_level; + dv.dv_maxlvl = ss.ss_maxlabel.lt_level; + dv.dv_valcmp = ss.ss_maxlabel.lt_compart; + } + dv.dv_devflg = 0; + } +#else /* SO_SEC_MULTI */ /* 7.0 code */ + if (getsockopt(0, SOL_SOCKET, SO_SECURITY, + (char *)&ss, &szss) >= 0) { + dv.dv_actlvl = ss.ss_slevel; + dv.dv_actcmp = ss.ss_compart; + dv.dv_minlvl = ss.ss_minlvl; + dv.dv_maxlvl = ss.ss_maxlvl; + dv.dv_valcmp = ss.ss_maxcmp; + } +#endif /* SO_SEC_MULTI */ + } +#endif /* _SC_CRAY_SECURE_SYS */ + + openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); + fromlen = sizeof (from); + if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { + fprintf(stderr, "%s: ", progname); + perror("getpeername"); + _exit(1); + } + if (keepalive && + setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, + (char *)&on, sizeof (on)) < 0) { + syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); + } + +#if defined(IPPROTO_IP) && defined(IP_TOS) + { +# if defined(HAS_GETTOS) + struct tosent *tp; + if (tos < 0 && (tp = gettosbyname("telnet", "tcp"))) + tos = tp->t_tos; +# endif + if (tos < 0) + tos = 020; /* Low Delay bit */ + if (tos + && (setsockopt(0, IPPROTO_IP, IP_TOS, + (char *)&tos, sizeof(tos)) < 0) + && (errno != ENOPROTOOPT) ) + syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + } +#endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ + net = 0; + doit(&from); + /* NOTREACHED */ +} /* end of main */ + + void +usage() +{ + fprintf(stderr, "Usage: telnetd"); +#ifdef AUTHENTICATION + fprintf(stderr, " [-a (debug|other|user|valid|off|none)]\n\t"); +#endif +#ifdef BFTPDAEMON + fprintf(stderr, " [-B]"); +#endif + fprintf(stderr, " [-debug]"); +#ifdef DIAGNOSTICS + fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]\n\t"); +#endif +#ifdef AUTHENTICATION + fprintf(stderr, " [-edebug]"); +#endif + fprintf(stderr, " [-h]"); +#if defined(CRAY) && defined(NEWINIT) + fprintf(stderr, " [-Iinitid]"); +#endif +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + fprintf(stderr, " [-k]"); +#endif +#ifdef LINEMODE + fprintf(stderr, " [-l]"); +#endif + fprintf(stderr, " [-n]"); +#ifdef CRAY + fprintf(stderr, " [-r[lowpty]-[highpty]]"); +#endif + fprintf(stderr, "\n\t"); +#ifdef SecurID + fprintf(stderr, " [-s]"); +#endif +#ifdef HAS_GETTOS + fprintf(stderr, " [-S tos]"); +#endif +#ifdef AUTHENTICATION + fprintf(stderr, " [-X auth-type]"); +#endif + fprintf(stderr, " [-u utmp_hostname_length] [-U]"); + fprintf(stderr, " [port]\n"); + exit(1); +} + +/* + * getterminaltype + * + * Ask the other end to send along its terminal type and speed. + * Output is the variable terminaltype filled in. + */ +static unsigned char ttytype_sbbuf[] = { + IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE +}; + + int +getterminaltype(name) + char *name; +{ + int retval = -1; + void _gettermname(); + + settimer(baseline); +#if defined(AUTHENTICATION) + /* + * Handle the Authentication option before we do anything else. + */ + send_do(TELOPT_AUTHENTICATION, 1); + while (his_will_wont_is_changing(TELOPT_AUTHENTICATION)) + ttloop(); + if (his_state_is_will(TELOPT_AUTHENTICATION)) { + retval = auth_wait(name); + } +#endif + + send_do(TELOPT_TTYPE, 1); + send_do(TELOPT_TSPEED, 1); + send_do(TELOPT_XDISPLOC, 1); + send_do(TELOPT_NEW_ENVIRON, 1); + send_do(TELOPT_OLD_ENVIRON, 1); + while ( + his_will_wont_is_changing(TELOPT_TTYPE) || + his_will_wont_is_changing(TELOPT_TSPEED) || + his_will_wont_is_changing(TELOPT_XDISPLOC) || + his_will_wont_is_changing(TELOPT_NEW_ENVIRON) || + his_will_wont_is_changing(TELOPT_OLD_ENVIRON)) { + ttloop(); + } + if (his_state_is_will(TELOPT_TSPEED)) { + static unsigned char sb[] = + { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; + + bcopy(sb, nfrontp, sizeof sb); + nfrontp += sizeof sb; + } + if (his_state_is_will(TELOPT_XDISPLOC)) { + static unsigned char sb[] = + { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE }; + + bcopy(sb, nfrontp, sizeof sb); + nfrontp += sizeof sb; + } + if (his_state_is_will(TELOPT_NEW_ENVIRON)) { + static unsigned char sb[] = + { IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND, IAC, SE }; + + bcopy(sb, nfrontp, sizeof sb); + nfrontp += sizeof sb; + } + else if (his_state_is_will(TELOPT_OLD_ENVIRON)) { + static unsigned char sb[] = + { IAC, SB, TELOPT_OLD_ENVIRON, TELQUAL_SEND, IAC, SE }; + + bcopy(sb, nfrontp, sizeof sb); + nfrontp += sizeof sb; + } + if (his_state_is_will(TELOPT_TTYPE)) { + + bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); + nfrontp += sizeof ttytype_sbbuf; + } + if (his_state_is_will(TELOPT_TSPEED)) { + while (sequenceIs(tspeedsubopt, baseline)) + ttloop(); + } + if (his_state_is_will(TELOPT_XDISPLOC)) { + while (sequenceIs(xdisplocsubopt, baseline)) + ttloop(); + } + if (his_state_is_will(TELOPT_NEW_ENVIRON)) { + while (sequenceIs(environsubopt, baseline)) + ttloop(); + } + if (his_state_is_will(TELOPT_OLD_ENVIRON)) { + while (sequenceIs(oenvironsubopt, baseline)) + ttloop(); + } + if (his_state_is_will(TELOPT_TTYPE)) { + char first[256], last[256]; + + while (sequenceIs(ttypesubopt, baseline)) + ttloop(); + + /* + * If the other side has already disabled the option, then + * we have to just go with what we (might) have already gotten. + */ + if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) { + (void) strncpy(first, terminaltype, sizeof(first)); + for(;;) { + /* + * Save the unknown name, and request the next name. + */ + (void) strncpy(last, terminaltype, sizeof(last)); + _gettermname(); + if (terminaltypeok(terminaltype)) + break; + if ((strncmp(last, terminaltype, sizeof(last)) == 0) || + his_state_is_wont(TELOPT_TTYPE)) { + /* + * We've hit the end. If this is the same as + * the first name, just go with it. + */ + if (strncmp(first, terminaltype, sizeof(first)) == 0) + break; + /* + * Get the terminal name one more time, so that + * RFC1091 compliant telnets will cycle back to + * the start of the list. + */ + _gettermname(); + if (strncmp(first, terminaltype, sizeof(first)) != 0) + (void) strncpy(terminaltype, first, sizeof(first)); + break; + } + } + } + } + return(retval); +} /* end of getterminaltype */ + + void +_gettermname() +{ + /* + * If the client turned off the option, + * we can't send another request, so we + * just return. + */ + if (his_state_is_wont(TELOPT_TTYPE)) + return; + settimer(baseline); + bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); + nfrontp += sizeof ttytype_sbbuf; + while (sequenceIs(ttypesubopt, baseline)) + ttloop(); +} + + int +terminaltypeok(s) + char *s; +{ + char buf[1024]; + + if (terminaltype == NULL) + return(1); + + /* + * tgetent() will return 1 if the type is known, and + * 0 if it is not known. If it returns -1, it couldn't + * open the database. But if we can't open the database, + * it won't help to say we failed, because we won't be + * able to verify anything else. So, we treat -1 like 1. + */ + if (tgetent(buf, s) == 0) + return(0); + return(1); +} + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 +#endif /* MAXHOSTNAMELEN */ + +char *hostname; +char host_name[MAXHOSTNAMELEN]; +char remote_host_name[MAXHOSTNAMELEN]; + +extern void telnet P((int, int, char *)); + +int level; +char user_name[256]; +/* + * Get a pty, scan input lines. + */ +doit(who) + struct sockaddr_in *who; +{ + char *host, *inet_ntoa(); + int t; + struct hostent *hp; + int ptynum; + + /* + * Find an available pty to use. + */ +#ifndef convex + pty = getpty(&ptynum); + if (pty < 0) + fatal(net, "All network ports in use"); +#else + for (;;) { + char *lp; + extern char *line, *getpty(); + + if ((lp = getpty()) == NULL) + fatal(net, "Out of ptys"); + + if ((pty = open(lp, 2)) >= 0) { + strcpy(line,lp); + line[5] = 't'; + break; + } + } +#endif + +#if defined(_SC_CRAY_SECURE_SYS) + /* + * set ttyp line security label + */ + if (secflag) { + char slave_dev[16]; + + sprintf(tty_dev, "/dev/pty/%03d", ptynum); + if (setdevs(tty_dev, &dv) < 0) + fatal(net, "cannot set pty security"); + sprintf(slave_dev, "/dev/ttyp%03d", ptynum); + if (setdevs(slave_dev, &dv) < 0) + fatal(net, "cannot set tty security"); + } +#endif /* _SC_CRAY_SECURE_SYS */ + + /* get name of connected client */ + hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr), + who->sin_family); + + if (hp == NULL && registerd_host_only) { + fatal(net, "Couldn't resolve your address into a host name.\r\n\ + Please contact your net administrator"); + } else if (hp && + (strlen(hp->h_name) <= ((utmp_len < 0) ? -utmp_len : utmp_len))) { + host = hp->h_name; + } else { + host = inet_ntoa(who->sin_addr); + } + /* + * We must make a copy because Kerberos is probably going + * to also do a gethost* and overwrite the static data... + */ + strncpy(remote_host_name, host, sizeof(remote_host_name)-1); + remote_host_name[sizeof(remote_host_name)-1] = 0; + host = remote_host_name; + + (void) gethostname(host_name, sizeof (host_name)); + hostname = host_name; + +#if defined(AUTHENTICATION) + auth_encrypt_init(hostname, host, "TELNETD", 1); +#endif + + init_env(); + /* + * get terminal type. + */ + *user_name = 0; + level = getterminaltype(user_name); + setenv("TERM", terminaltype ? terminaltype : "network", 1); + +#if defined(_SC_CRAY_SECURE_SYS) + if (secflag) { + if (setulvl(dv.dv_actlvl) < 0) + fatal(net,"cannot setulvl()"); + if (setucmp(dv.dv_actcmp) < 0) + fatal(net, "cannot setucmp()"); + } +#endif /* _SC_CRAY_SECURE_SYS */ + + telnet(net, pty, host); /* begin server process */ + + /*NOTREACHED*/ +} /* end of doit */ + +#if defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50) + int +Xterm_output(ibufp, obuf, icountp, ocount) + char **ibufp, *obuf; + int *icountp, ocount; +{ + int ret; + ret = term_output(*ibufp, obuf, *icountp, ocount); + *ibufp += *icountp; + *icountp = 0; + return(ret); +} +#define term_output Xterm_output +#endif /* defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50) */ + +/* + * Main loop. Select from pty and network, and + * hand data to telnet receiver finite state machine. + */ + void +telnet(f, p, host) + int f, p; + char *host; +{ + int on = 1; +#define TABBUFSIZ 512 + char defent[TABBUFSIZ]; + char defstrs[TABBUFSIZ]; +#undef TABBUFSIZ + char *HE; + char *HN; + char *IM; + void netflush(); + + /* + * Initialize the slc mapping table. + */ + get_slc_defaults(); + + /* + * Do some tests where it is desireable to wait for a response. + * Rather than doing them slowly, one at a time, do them all + * at once. + */ + if (my_state_is_wont(TELOPT_SGA)) + send_will(TELOPT_SGA, 1); + /* + * Is the client side a 4.2 (NOT 4.3) system? We need to know this + * because 4.2 clients are unable to deal with TCP urgent data. + * + * To find out, we send out a "DO ECHO". If the remote system + * answers "WILL ECHO" it is probably a 4.2 client, and we note + * that fact ("WILL ECHO" ==> that the client will echo what + * WE, the server, sends it; it does NOT mean that the client will + * echo the terminal input). + */ + send_do(TELOPT_ECHO, 1); + +#ifdef LINEMODE + if (his_state_is_wont(TELOPT_LINEMODE)) { + /* Query the peer for linemode support by trying to negotiate + * the linemode option. + */ + linemode = 0; + editmode = 0; + send_do(TELOPT_LINEMODE, 1); /* send do linemode */ + } +#endif /* LINEMODE */ + + /* + * Send along a couple of other options that we wish to negotiate. + */ + send_do(TELOPT_NAWS, 1); + send_will(TELOPT_STATUS, 1); + flowmode = 1; /* default flow control state */ + restartany = -1; /* uninitialized... */ + send_do(TELOPT_LFLOW, 1); + + /* + * Spin, waiting for a response from the DO ECHO. However, + * some REALLY DUMB telnets out there might not respond + * to the DO ECHO. So, we spin looking for NAWS, (most dumb + * telnets so far seem to respond with WONT for a DO that + * they don't understand...) because by the time we get the + * response, it will already have processed the DO ECHO. + * Kludge upon kludge. + */ + while (his_will_wont_is_changing(TELOPT_NAWS)) + ttloop(); + + /* + * But... + * The client might have sent a WILL NAWS as part of its + * startup code; if so, we'll be here before we get the + * response to the DO ECHO. We'll make the assumption + * that any implementation that understands about NAWS + * is a modern enough implementation that it will respond + * to our DO ECHO request; hence we'll do another spin + * waiting for the ECHO option to settle down, which is + * what we wanted to do in the first place... + */ + if (his_want_state_is_will(TELOPT_ECHO) && + his_state_is_will(TELOPT_NAWS)) { + while (his_will_wont_is_changing(TELOPT_ECHO)) + ttloop(); + } + /* + * On the off chance that the telnet client is broken and does not + * respond to the DO ECHO we sent, (after all, we did send the + * DO NAWS negotiation after the DO ECHO, and we won't get here + * until a response to the DO NAWS comes back) simulate the + * receipt of a will echo. This will also send a WONT ECHO + * to the client, since we assume that the client failed to + * respond because it believes that it is already in DO ECHO + * mode, which we do not want. + */ + if (his_want_state_is_will(TELOPT_ECHO)) { + DIAG(TD_OPTIONS, + {sprintf(nfrontp, "td: simulating recv\r\n"); + nfrontp += strlen(nfrontp);}); + willoption(TELOPT_ECHO); + } + + /* + * Finally, to clean things up, we turn on our echo. This + * will break stupid 4.2 telnets out of local terminal echo. + */ + + if (my_state_is_wont(TELOPT_ECHO)) + send_will(TELOPT_ECHO, 1); + +#ifndef STREAMSPTY + /* + * Turn on packet mode + */ + (void) ioctl(p, TIOCPKT, (char *)&on); +#endif + +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + /* + * Continuing line mode support. If client does not support + * real linemode, attempt to negotiate kludge linemode by sending + * the do timing mark sequence. + */ + if (lmodetype < REAL_LINEMODE) + send_do(TELOPT_TM, 1); +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + + /* + * Call telrcv() once to pick up anything received during + * terminal type negotiation, 4.2/4.3 determination, and + * linemode negotiation. + */ + telrcv(); + + (void) ioctl(f, FIONBIO, (char *)&on); + (void) ioctl(p, FIONBIO, (char *)&on); +#if defined(CRAY2) && defined(UNICOS5) + init_termdriver(f, p, interrupt, sendbrk); +#endif + +#if defined(SO_OOBINLINE) + (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, + (char *)&on, sizeof on); +#endif /* defined(SO_OOBINLINE) */ + +#ifdef SIGTSTP + (void) signal(SIGTSTP, SIG_IGN); +#endif +#ifdef SIGTTOU + /* + * Ignoring SIGTTOU keeps the kernel from blocking us + * in ttioct() in /sys/tty.c. + */ + (void) signal(SIGTTOU, SIG_IGN); +#endif + + (void) signal(SIGCHLD, cleanup); + +#if defined(CRAY2) && defined(UNICOS5) + /* + * Cray-2 will send a signal when pty modes are changed by slave + * side. Set up signal handler now. + */ + if ((int)signal(SIGUSR1, termstat) < 0) + perror("signal"); + else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0) + perror("ioctl:TCSIGME"); + /* + * Make processing loop check terminal characteristics early on. + */ + termstat(); +#endif + +#ifdef TIOCNOTTY + { + register int t; + t = open(_PATH_TTY, O_RDWR); + if (t >= 0) { + (void) ioctl(t, TIOCNOTTY, (char *)0); + (void) close(t); + } + } +#endif + +#if defined(CRAY) && defined(NEWINIT) && defined(TIOCSCTTY) + (void) setsid(); + ioctl(p, TIOCSCTTY, 0); +#endif + + /* + * Show banner that getty never gave. + * + * We put the banner in the pty input buffer. This way, it + * gets carriage return null processing, etc., just like all + * other pty --> client data. + */ + +#if !defined(CRAY) || !defined(NEWINIT) + if (getenv("USER")) + hostinfo = 0; +#endif + + if (getent(defent, "default") == 1) { + char *getstr(); + char *cp=defstrs; + + HE = getstr("he", &cp); + HN = getstr("hn", &cp); + IM = getstr("im", &cp); + if (HN && *HN) + (void) strcpy(host_name, HN); + if (IM == 0) + IM = ""; + } else { + IM = DEFAULT_IM; + HE = 0; + } + edithost(HE, host_name); + if (hostinfo && *IM) + putf(IM, ptyibuf2); + + if (pcc) + (void) strncat(ptyibuf2, ptyip, pcc+1); + ptyip = ptyibuf2; + pcc = strlen(ptyip); +#ifdef LINEMODE + /* + * Last check to make sure all our states are correct. + */ + init_termbuf(); + localstat(); +#endif /* LINEMODE */ + + DIAG(TD_REPORT, + {sprintf(nfrontp, "td: Entering processing loop\r\n"); + nfrontp += strlen(nfrontp);}); + + /* + * Startup the login process on the slave side of the terminal + * now. We delay this until here to insure option negotiation + * is complete. + */ + startslave(host, level, user_name); + + for (;;) { + fd_set ibits, obits, xbits; + register int c; + + if (ncc < 0 && pcc < 0) + break; + +#if defined(CRAY2) && defined(UNICOS5) + if (needtermstat) + _termstat(); +#endif /* defined(CRAY2) && defined(UNICOS5) */ + FD_ZERO(&ibits); + FD_ZERO(&obits); + FD_ZERO(&xbits); + /* + * Never look for input if there's still + * stuff in the corresponding output buffer + */ + if (nfrontp - nbackp || pcc > 0) { + FD_SET(f, &obits); + } else { + FD_SET(p, &ibits); + } + if (pfrontp - pbackp || ncc > 0) { + FD_SET(p, &obits); + } else { + FD_SET(f, &ibits); + } + if (!SYNCHing) { + FD_SET(f, &xbits); + } + if ((c = select(16, &ibits, &obits, &xbits, + (struct timeval *)0)) < 1) { + if (c == -1) { + if (errno == EINTR) { + continue; + } + } + sleep(5); + continue; + } + + /* + * Any urgent data? + */ + if (FD_ISSET(net, &xbits)) { + SYNCHing = 1; + } + + /* + * Something to read from the network... + */ + if (FD_ISSET(net, &ibits)) { +#if !defined(SO_OOBINLINE) + /* + * In 4.2 (and 4.3 beta) systems, the + * OOB indication and data handling in the kernel + * is such that if two separate TCP Urgent requests + * come in, one byte of TCP data will be overlaid. + * This is fatal for Telnet, but we try to live + * with it. + * + * In addition, in 4.2 (and...), a special protocol + * is needed to pick up the TCP Urgent data in + * the correct sequence. + * + * What we do is: if we think we are in urgent + * mode, we look to see if we are "at the mark". + * If we are, we do an OOB receive. If we run + * this twice, we will do the OOB receive twice, + * but the second will fail, since the second + * time we were "at the mark", but there wasn't + * any data there (the kernel doesn't reset + * "at the mark" until we do a normal read). + * Once we've read the OOB data, we go ahead + * and do normal reads. + * + * There is also another problem, which is that + * since the OOB byte we read doesn't put us + * out of OOB state, and since that byte is most + * likely the TELNET DM (data mark), we would + * stay in the TELNET SYNCH (SYNCHing) state. + * So, clocks to the rescue. If we've "just" + * received a DM, then we test for the + * presence of OOB data when the receive OOB + * fails (and AFTER we did the normal mode read + * to clear "at the mark"). + */ + if (SYNCHing) { + int atmark; + + (void) ioctl(net, SIOCATMARK, (char *)&atmark); + if (atmark) { + ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); + if ((ncc == -1) && (errno == EINVAL)) { + ncc = read(net, netibuf, sizeof (netibuf)); + if (sequenceIs(didnetreceive, gotDM)) { + SYNCHing = stilloob(net); + } + } + } else { + ncc = read(net, netibuf, sizeof (netibuf)); + } + } else { + ncc = read(net, netibuf, sizeof (netibuf)); + } + settimer(didnetreceive); +#else /* !defined(SO_OOBINLINE)) */ + ncc = read(net, netibuf, sizeof (netibuf)); +#endif /* !defined(SO_OOBINLINE)) */ + if (ncc < 0 && errno == EWOULDBLOCK) + ncc = 0; + else { + if (ncc <= 0) { + break; + } + netip = netibuf; + } + DIAG((TD_REPORT | TD_NETDATA), + {sprintf(nfrontp, "td: netread %d chars\r\n", ncc); + nfrontp += strlen(nfrontp);}); + DIAG(TD_NETDATA, printdata("nd", netip, ncc)); + } + + /* + * Something to read from the pty... + */ + if (FD_ISSET(p, &ibits)) { +#ifndef STREAMSPTY + pcc = read(p, ptyibuf, BUFSIZ); +#else + pcc = readstream(p, ptyibuf, BUFSIZ); +#endif + /* + * On some systems, if we try to read something + * off the master side before the slave side is + * opened, we get EIO. + */ + if (pcc < 0 && (errno == EWOULDBLOCK || +#ifdef EAGAIN + errno == EAGAIN || +#endif + errno == EIO)) { + pcc = 0; + } else { + if (pcc <= 0) + break; +#if !defined(CRAY2) || !defined(UNICOS5) +#ifdef LINEMODE + /* + * If ioctl from pty, pass it through net + */ + if (ptyibuf[0] & TIOCPKT_IOCTL) { + copy_termbuf(ptyibuf+1, pcc-1); + localstat(); + pcc = 1; + } +#endif /* LINEMODE */ + if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { + netclear(); /* clear buffer back */ +#ifndef NO_URGENT + /* + * There are client telnets on some + * operating systems get screwed up + * royally if we send them urgent + * mode data. + */ + *nfrontp++ = IAC; + *nfrontp++ = DM; + neturg = nfrontp-1; /* off by one XXX */ +#endif + } + if (his_state_is_will(TELOPT_LFLOW) && + (ptyibuf[0] & + (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { + int newflow = + ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0; + if (newflow != flowmode) { + flowmode = newflow; + (void) sprintf(nfrontp, + "%c%c%c%c%c%c", + IAC, SB, TELOPT_LFLOW, + flowmode ? LFLOW_ON + : LFLOW_OFF, + IAC, SE); + nfrontp += 6; + } + } + pcc--; + ptyip = ptyibuf+1; +#else /* defined(CRAY2) && defined(UNICOS5) */ + if (!uselinemode) { + unpcc = pcc; + unptyip = ptyibuf; + pcc = term_output(&unptyip, ptyibuf2, + &unpcc, BUFSIZ); + ptyip = ptyibuf2; + } else + ptyip = ptyibuf; +#endif /* defined(CRAY2) && defined(UNICOS5) */ + } + } + + while (pcc > 0) { + if ((&netobuf[BUFSIZ] - nfrontp) < 2) + break; + c = *ptyip++ & 0377, pcc--; + if (c == IAC) + *nfrontp++ = c; +#if defined(CRAY2) && defined(UNICOS5) + else if (c == '\n' && + my_state_is_wont(TELOPT_BINARY) && newmap) + *nfrontp++ = '\r'; +#endif /* defined(CRAY2) && defined(UNICOS5) */ + *nfrontp++ = c; + if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) { + if (pcc > 0 && ((*ptyip & 0377) == '\n')) { + *nfrontp++ = *ptyip++ & 0377; + pcc--; + } else + *nfrontp++ = '\0'; + } + } +#if defined(CRAY2) && defined(UNICOS5) + /* + * If chars were left over from the terminal driver, + * note their existence. + */ + if (!uselinemode && unpcc) { + pcc = unpcc; + unpcc = 0; + ptyip = unptyip; + } +#endif /* defined(CRAY2) && defined(UNICOS5) */ + + if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) + netflush(); + if (ncc > 0) + telrcv(); + if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) + ptyflush(); + } + cleanup(0); +} /* end of telnet */ + +#ifndef TCSIG +# ifdef TIOCSIG +# define TCSIG TIOCSIG +# endif +#endif + +#ifdef STREAMSPTY + +int flowison = -1; /* current state of flow: -1 is unknown */ + +int readstream(p, ibuf, bufsize) + int p; + char *ibuf; + int bufsize; +{ + int flags = 0; + int ret = 0; + struct termios *tsp; + struct termio *tp; + struct iocblk *ip; + char vstop, vstart; + int ixon; + int newflow; + + strbufc.maxlen = BUFSIZ; + strbufc.buf = (char *)ctlbuf; + strbufd.maxlen = bufsize-1; + strbufd.len = 0; + strbufd.buf = ibuf+1; + ibuf[0] = 0; + + ret = getmsg(p, &strbufc, &strbufd, &flags); + if (ret < 0) /* error of some sort -- probably EAGAIN */ + return(-1); + + if (strbufc.len <= 0 || ctlbuf[0] == M_DATA) { + /* data message */ + if (strbufd.len > 0) { /* real data */ + return(strbufd.len + 1); /* count header char */ + } else { + /* nothing there */ + errno = EAGAIN; + return(-1); + } + } + + /* + * It's a control message. Return 1, to look at the flag we set + */ + + switch (ctlbuf[0]) { + case M_FLUSH: + if (ibuf[1] & FLUSHW) + ibuf[0] = TIOCPKT_FLUSHWRITE; + return(1); + + case M_IOCTL: + ip = (struct iocblk *) (ibuf+1); + + switch (ip->ioc_cmd) { + case TCSETS: + case TCSETSW: + case TCSETSF: + tsp = (struct termios *) + (ibuf+1 + sizeof(struct iocblk)); + vstop = tsp->c_cc[VSTOP]; + vstart = tsp->c_cc[VSTART]; + ixon = tsp->c_iflag & IXON; + break; + case TCSETA: + case TCSETAW: + case TCSETAF: + tp = (struct termio *) (ibuf+1 + sizeof(struct iocblk)); + vstop = tp->c_cc[VSTOP]; + vstart = tp->c_cc[VSTART]; + ixon = tp->c_iflag & IXON; + break; + default: + errno = EAGAIN; + return(-1); + } + + newflow = (ixon && (vstart == 021) && (vstop == 023)) ? 1 : 0; + if (newflow != flowison) { /* it's a change */ + flowison = newflow; + ibuf[0] = newflow ? TIOCPKT_DOSTOP : TIOCPKT_NOSTOP; + return(1); + } + } + + /* nothing worth doing anything about */ + errno = EAGAIN; + return(-1); +} +#endif /* STREAMSPTY */ + +/* + * Send interrupt to process on other side of pty. + * If it is in raw mode, just write NULL; + * otherwise, write intr char. + */ + void +interrupt() +{ + ptyflush(); /* half-hearted */ + +#ifdef TCSIG + (void) ioctl(pty, TCSIG, (char *)SIGINT); +#else /* TCSIG */ + init_termbuf(); + *pfrontp++ = slctab[SLC_IP].sptr ? + (unsigned char)*slctab[SLC_IP].sptr : '\177'; +#endif /* TCSIG */ +} + +/* + * Send quit to process on other side of pty. + * If it is in raw mode, just write NULL; + * otherwise, write quit char. + */ + void +sendbrk() +{ + ptyflush(); /* half-hearted */ +#ifdef TCSIG + (void) ioctl(pty, TCSIG, (char *)SIGQUIT); +#else /* TCSIG */ + init_termbuf(); + *pfrontp++ = slctab[SLC_ABORT].sptr ? + (unsigned char)*slctab[SLC_ABORT].sptr : '\034'; +#endif /* TCSIG */ +} + + void +sendsusp() +{ +#ifdef SIGTSTP + ptyflush(); /* half-hearted */ +# ifdef TCSIG + (void) ioctl(pty, TCSIG, (char *)SIGTSTP); +# else /* TCSIG */ + *pfrontp++ = slctab[SLC_SUSP].sptr ? + (unsigned char)*slctab[SLC_SUSP].sptr : '\032'; +# endif /* TCSIG */ +#endif /* SIGTSTP */ +} + +/* + * When we get an AYT, if ^T is enabled, use that. Otherwise, + * just send back "[Yes]". + */ + void +recv_ayt() +{ +#if defined(SIGINFO) && defined(TCSIG) + if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) { + (void) ioctl(pty, TCSIG, (char *)SIGINFO); + return; + } +#endif + (void) strcpy(nfrontp, "\r\n[Yes]\r\n"); + nfrontp += 9; +} + + void +doeof() +{ + init_termbuf(); + +#if defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN) + if (!tty_isediting()) { + extern char oldeofc; + *pfrontp++ = oldeofc; + return; + } +#endif + *pfrontp++ = slctab[SLC_EOF].sptr ? + (unsigned char)*slctab[SLC_EOF].sptr : '\004'; +} diff --git a/libexec/telnetd/telnetd.h b/libexec/telnetd/telnetd.h new file mode 100644 index 0000000..234b973 --- /dev/null +++ b/libexec/telnetd/telnetd.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)telnetd.h 8.1 (Berkeley) 6/4/93 + */ + + +#include "defs.h" +#include "ext.h" + +#ifdef DIAGNOSTICS +#define DIAG(a,b) if (diagnostic & (a)) b +#else +#define DIAG(a,b) +#endif + +/* other external variables */ +extern char **environ; +extern int errno; + diff --git a/libexec/telnetd/termstat.c b/libexec/telnetd/termstat.c new file mode 100644 index 0000000..b08e50f --- /dev/null +++ b/libexec/telnetd/termstat.c @@ -0,0 +1,650 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)termstat.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include "telnetd.h" + +/* + * local variables + */ +int def_tspeed = -1, def_rspeed = -1; +#ifdef TIOCSWINSZ +int def_row = 0, def_col = 0; +#endif +#ifdef LINEMODE +static int _terminit = 0; +#endif /* LINEMODE */ + +#if defined(CRAY2) && defined(UNICOS5) +int newmap = 1; /* nonzero if \n maps to ^M^J */ +#endif + +#ifdef LINEMODE +/* + * localstat + * + * This function handles all management of linemode. + * + * Linemode allows the client to do the local editing of data + * and send only complete lines to the server. Linemode state is + * based on the state of the pty driver. If the pty is set for + * external processing, then we can use linemode. Further, if we + * can use real linemode, then we can look at the edit control bits + * in the pty to determine what editing the client should do. + * + * Linemode support uses the following state flags to keep track of + * current and desired linemode state. + * alwayslinemode : true if -l was specified on the telnetd + * command line. It means to have linemode on as much as + * possible. + * + * lmodetype: signifies whether the client can + * handle real linemode, or if use of kludgeomatic linemode + * is preferred. It will be set to one of the following: + * REAL_LINEMODE : use linemode option + * NO_KLUDGE : don't initiate kludge linemode. + * KLUDGE_LINEMODE : use kludge linemode + * NO_LINEMODE : client is ignorant of linemode + * + * linemode, uselinemode : linemode is true if linemode + * is currently on, uselinemode is the state that we wish + * to be in. If another function wishes to turn linemode + * on or off, it sets or clears uselinemode. + * + * editmode, useeditmode : like linemode/uselinemode, but + * these contain the edit mode states (edit and trapsig). + * + * The state variables correspond to some of the state information + * in the pty. + * linemode: + * In real linemode, this corresponds to whether the pty + * expects external processing of incoming data. + * In kludge linemode, this more closely corresponds to the + * whether normal processing is on or not. (ICANON in + * system V, or COOKED mode in BSD.) + * If the -l option was specified (alwayslinemode), then + * an attempt is made to force external processing on at + * all times. + * + * The following heuristics are applied to determine linemode + * handling within the server. + * 1) Early on in starting up the server, an attempt is made + * to negotiate the linemode option. If this succeeds + * then lmodetype is set to REAL_LINEMODE and all linemode + * processing occurs in the context of the linemode option. + * 2) If the attempt to negotiate the linemode option failed, + * and the "-k" (don't initiate kludge linemode) isn't set, + * then we try to use kludge linemode. We test for this + * capability by sending "do Timing Mark". If a positive + * response comes back, then we assume that the client + * understands kludge linemode (ech!) and the + * lmodetype flag is set to KLUDGE_LINEMODE. + * 3) Otherwise, linemode is not supported at all and + * lmodetype remains set to NO_LINEMODE (which happens + * to be 0 for convenience). + * 4) At any time a command arrives that implies a higher + * state of linemode support in the client, we move to that + * linemode support. + * + * A short explanation of kludge linemode is in order here. + * 1) The heuristic to determine support for kludge linemode + * is to send a do timing mark. We assume that a client + * that supports timing marks also supports kludge linemode. + * A risky proposition at best. + * 2) Further negotiation of linemode is done by changing the + * the server's state regarding SGA. If server will SGA, + * then linemode is off, if server won't SGA, then linemode + * is on. + */ + void +localstat() +{ + void netflush(); + int need_will_echo = 0; + +#if defined(CRAY2) && defined(UNICOS5) + /* + * Keep track of that ol' CR/NL mapping while we're in the + * neighborhood. + */ + newmap = tty_isnewmap(); +#endif /* defined(CRAY2) && defined(UNICOS5) */ + + /* + * Check for changes to flow control if client supports it. + */ + flowstat(); + + /* + * Check linemode on/off state + */ + uselinemode = tty_linemode(); + + /* + * If alwayslinemode is on, and pty is changing to turn it off, then + * force linemode back on. + */ + if (alwayslinemode && linemode && !uselinemode) { + uselinemode = 1; + tty_setlinemode(uselinemode); + } + + if (uselinemode) { + + /* + * Check for state of BINARY options. + * + * We only need to do the binary dance if we are actually going + * to use linemode. As this confuses some telnet clients that dont + * support linemode, and doesnt gain us anything, we dont do it + * unless we're doing linemode. -Crh (henrich@msu.edu) + */ + + if (tty_isbinaryin()) { + if (his_want_state_is_wont(TELOPT_BINARY)) + send_do(TELOPT_BINARY, 1); + } else { + if (his_want_state_is_will(TELOPT_BINARY)) + send_dont(TELOPT_BINARY, 1); + } + + if (tty_isbinaryout()) { + if (my_want_state_is_wont(TELOPT_BINARY)) + send_will(TELOPT_BINARY, 1); + } else { + if (my_want_state_is_will(TELOPT_BINARY)) + send_wont(TELOPT_BINARY, 1); + } + + } + + /* + * Do echo mode handling as soon as we know what the + * linemode is going to be. + * If the pty has echo turned off, then tell the client that + * the server will echo. If echo is on, then the server + * will echo if in character mode, but in linemode the + * client should do local echoing. The state machine will + * not send anything if it is unnecessary, so don't worry + * about that here. + * + * If we need to send the WILL ECHO (because echo is off), + * then delay that until after we have changed the MODE. + * This way, when the user is turning off both editing + * and echo, the client will get editing turned off first. + * This keeps the client from going into encryption mode + * and then right back out if it is doing auto-encryption + * when passwords are being typed. + */ + if (uselinemode) { + if (tty_isecho()) + send_wont(TELOPT_ECHO, 1); + else + need_will_echo = 1; +#ifdef KLUDGELINEMODE + if (lmodetype == KLUDGE_OK) + lmodetype = KLUDGE_LINEMODE; +#endif + } + + /* + * If linemode is being turned off, send appropriate + * command and then we're all done. + */ + if (!uselinemode && linemode) { +# ifdef KLUDGELINEMODE + if (lmodetype == REAL_LINEMODE) { +# endif /* KLUDGELINEMODE */ + send_dont(TELOPT_LINEMODE, 1); +# ifdef KLUDGELINEMODE + } else if (lmodetype == KLUDGE_LINEMODE) + send_will(TELOPT_SGA, 1); +# endif /* KLUDGELINEMODE */ + send_will(TELOPT_ECHO, 1); + linemode = uselinemode; + goto done; + } + +# ifdef KLUDGELINEMODE + /* + * If using real linemode check edit modes for possible later use. + * If we are in kludge linemode, do the SGA negotiation. + */ + if (lmodetype == REAL_LINEMODE) { +# endif /* KLUDGELINEMODE */ + useeditmode = 0; + if (tty_isediting()) + useeditmode |= MODE_EDIT; + if (tty_istrapsig()) + useeditmode |= MODE_TRAPSIG; + if (tty_issofttab()) + useeditmode |= MODE_SOFT_TAB; + if (tty_islitecho()) + useeditmode |= MODE_LIT_ECHO; +# ifdef KLUDGELINEMODE + } else if (lmodetype == KLUDGE_LINEMODE) { + if (tty_isediting() && uselinemode) + send_wont(TELOPT_SGA, 1); + else + send_will(TELOPT_SGA, 1); + } +# endif /* KLUDGELINEMODE */ + + /* + * Negotiate linemode on if pty state has changed to turn it on. + * Send appropriate command and send along edit mode, then all done. + */ + if (uselinemode && !linemode) { +# ifdef KLUDGELINEMODE + if (lmodetype == KLUDGE_LINEMODE) { + send_wont(TELOPT_SGA, 1); + } else if (lmodetype == REAL_LINEMODE) { +# endif /* KLUDGELINEMODE */ + send_do(TELOPT_LINEMODE, 1); + /* send along edit modes */ + (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB, + TELOPT_LINEMODE, LM_MODE, useeditmode, + IAC, SE); + nfrontp += 7; + editmode = useeditmode; +# ifdef KLUDGELINEMODE + } +# endif /* KLUDGELINEMODE */ + linemode = uselinemode; + goto done; + } + +# ifdef KLUDGELINEMODE + /* + * None of what follows is of any value if not using + * real linemode. + */ + if (lmodetype < REAL_LINEMODE) + goto done; +# endif /* KLUDGELINEMODE */ + + if (linemode && his_state_is_will(TELOPT_LINEMODE)) { + /* + * If edit mode changed, send edit mode. + */ + if (useeditmode != editmode) { + /* + * Send along appropriate edit mode mask. + */ + (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB, + TELOPT_LINEMODE, LM_MODE, useeditmode, + IAC, SE); + nfrontp += 7; + editmode = useeditmode; + } + + + /* + * Check for changes to special characters in use. + */ + start_slc(0); + check_slc(); + (void) end_slc(0); + } + +done: + if (need_will_echo) + send_will(TELOPT_ECHO, 1); + /* + * Some things should be deferred until after the pty state has + * been set by the local process. Do those things that have been + * deferred now. This only happens once. + */ + if (_terminit == 0) { + _terminit = 1; + defer_terminit(); + } + + netflush(); + set_termbuf(); + return; + +} /* end of localstat */ +#endif /* LINEMODE */ + +/* + * flowstat + * + * Check for changes to flow control + */ + void +flowstat() +{ + if (his_state_is_will(TELOPT_LFLOW)) { + if (tty_flowmode() != flowmode) { + flowmode = tty_flowmode(); + (void) sprintf(nfrontp, "%c%c%c%c%c%c", + IAC, SB, TELOPT_LFLOW, + flowmode ? LFLOW_ON : LFLOW_OFF, + IAC, SE); + nfrontp += 6; + } + if (tty_restartany() != restartany) { + restartany = tty_restartany(); + (void) sprintf(nfrontp, "%c%c%c%c%c%c", + IAC, SB, TELOPT_LFLOW, + restartany ? LFLOW_RESTART_ANY + : LFLOW_RESTART_XON, + IAC, SE); + nfrontp += 6; + } + } +} + +/* + * clientstat + * + * Process linemode related requests from the client. + * Client can request a change to only one of linemode, editmode or slc's + * at a time, and if using kludge linemode, then only linemode may be + * affected. + */ + void +clientstat(code, parm1, parm2) + register int code, parm1, parm2; +{ + void netflush(); + + /* + * Get a copy of terminal characteristics. + */ + init_termbuf(); + + /* + * Process request from client. code tells what it is. + */ + switch (code) { +#ifdef LINEMODE + case TELOPT_LINEMODE: + /* + * Don't do anything unless client is asking us to change + * modes. + */ + uselinemode = (parm1 == WILL); + if (uselinemode != linemode) { +# ifdef KLUDGELINEMODE + /* + * If using kludge linemode, make sure that + * we can do what the client asks. + * We can not turn off linemode if alwayslinemode + * and the ICANON bit is set. + */ + if (lmodetype == KLUDGE_LINEMODE) { + if (alwayslinemode && tty_isediting()) { + uselinemode = 1; + } + } + + /* + * Quit now if we can't do it. + */ + if (uselinemode == linemode) + return; + + /* + * If using real linemode and linemode is being + * turned on, send along the edit mode mask. + */ + if (lmodetype == REAL_LINEMODE && uselinemode) +# else /* KLUDGELINEMODE */ + if (uselinemode) +# endif /* KLUDGELINEMODE */ + { + useeditmode = 0; + if (tty_isediting()) + useeditmode |= MODE_EDIT; + if (tty_istrapsig) + useeditmode |= MODE_TRAPSIG; + if (tty_issofttab()) + useeditmode |= MODE_SOFT_TAB; + if (tty_islitecho()) + useeditmode |= MODE_LIT_ECHO; + (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, + SB, TELOPT_LINEMODE, LM_MODE, + useeditmode, IAC, SE); + nfrontp += 7; + editmode = useeditmode; + } + + + tty_setlinemode(uselinemode); + + linemode = uselinemode; + + if (!linemode) + send_will(TELOPT_ECHO, 1); + } + break; + + case LM_MODE: + { + register int ack, changed; + + /* + * Client has sent along a mode mask. If it agrees with + * what we are currently doing, ignore it; if not, it could + * be viewed as a request to change. Note that the server + * will change to the modes in an ack if it is different from + * what we currently have, but we will not ack the ack. + */ + useeditmode &= MODE_MASK; + ack = (useeditmode & MODE_ACK); + useeditmode &= ~MODE_ACK; + + if (changed = (useeditmode ^ editmode)) { + /* + * This check is for a timing problem. If the + * state of the tty has changed (due to the user + * application) we need to process that info + * before we write in the state contained in the + * ack!!! This gets out the new MODE request, + * and when the ack to that command comes back + * we'll set it and be in the right mode. + */ + if (ack) + localstat(); + if (changed & MODE_EDIT) + tty_setedit(useeditmode & MODE_EDIT); + + if (changed & MODE_TRAPSIG) + tty_setsig(useeditmode & MODE_TRAPSIG); + + if (changed & MODE_SOFT_TAB) + tty_setsofttab(useeditmode & MODE_SOFT_TAB); + + if (changed & MODE_LIT_ECHO) + tty_setlitecho(useeditmode & MODE_LIT_ECHO); + + set_termbuf(); + + if (!ack) { + (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, + SB, TELOPT_LINEMODE, LM_MODE, + useeditmode|MODE_ACK, + IAC, SE); + nfrontp += 7; + } + + editmode = useeditmode; + } + + break; + + } /* end of case LM_MODE */ +#endif /* LINEMODE */ + + case TELOPT_NAWS: +#ifdef TIOCSWINSZ + { + struct winsize ws; + + def_col = parm1; + def_row = parm2; +#ifdef LINEMODE + /* + * Defer changing window size until after terminal is + * initialized. + */ + if (terminit() == 0) + return; +#endif /* LINEMODE */ + + /* + * Change window size as requested by client. + */ + + ws.ws_col = parm1; + ws.ws_row = parm2; + (void) ioctl(pty, TIOCSWINSZ, (char *)&ws); + } +#endif /* TIOCSWINSZ */ + + break; + + case TELOPT_TSPEED: + { + def_tspeed = parm1; + def_rspeed = parm2; +#ifdef LINEMODE + /* + * Defer changing the terminal speed. + */ + if (terminit() == 0) + return; +#endif /* LINEMODE */ + /* + * Change terminal speed as requested by client. + * We set the receive speed first, so that if we can't + * store seperate receive and transmit speeds, the transmit + * speed will take precedence. + */ + tty_rspeed(parm2); + tty_tspeed(parm1); + set_termbuf(); + + break; + + } /* end of case TELOPT_TSPEED */ + + default: + /* What? */ + break; + } /* end of switch */ + +#if defined(CRAY2) && defined(UNICOS5) + /* + * Just in case of the likely event that we changed the pty state. + */ + rcv_ioctl(); +#endif /* defined(CRAY2) && defined(UNICOS5) */ + + netflush(); + +} /* end of clientstat */ + +#if defined(CRAY2) && defined(UNICOS5) + void +termstat() +{ + needtermstat = 1; +} + + void +_termstat() +{ + needtermstat = 0; + init_termbuf(); + localstat(); + rcv_ioctl(); +} +#endif /* defined(CRAY2) && defined(UNICOS5) */ + +#ifdef LINEMODE +/* + * defer_terminit + * + * Some things should not be done until after the login process has started + * and all the pty modes are set to what they are supposed to be. This + * function is called when the pty state has been processed for the first time. + * It calls other functions that do things that were deferred in each module. + */ + void +defer_terminit() +{ + + /* + * local stuff that got deferred. + */ + if (def_tspeed != -1) { + clientstat(TELOPT_TSPEED, def_tspeed, def_rspeed); + def_tspeed = def_rspeed = 0; + } + +#ifdef TIOCSWINSZ + if (def_col || def_row) { + struct winsize ws; + + bzero((char *)&ws, sizeof(ws)); + ws.ws_col = def_col; + ws.ws_row = def_row; + (void) ioctl(pty, TIOCSWINSZ, (char *)&ws); + } +#endif + + /* + * The only other module that currently defers anything. + */ + deferslc(); + +} /* end of defer_terminit */ + +/* + * terminit + * + * Returns true if the pty state has been processed yet. + */ + int +terminit() +{ + return(_terminit); + +} /* end of terminit */ +#endif /* LINEMODE */ diff --git a/libexec/telnetd/utility.c b/libexec/telnetd/utility.c new file mode 100644 index 0000000..4f5c2e6 --- /dev/null +++ b/libexec/telnetd/utility.c @@ -0,0 +1,1071 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)utility.c 8.2 (Berkeley) 12/15/93"; +#endif /* not lint */ + +#define PRINTOPTIONS +#include "telnetd.h" + +/* + * utility functions performing io related tasks + */ + +/* + * ttloop + * + * A small subroutine to flush the network output buffer, get some data + * from the network, and pass it through the telnet state machine. We + * also flush the pty input buffer (by dropping its data) if it becomes + * too full. + */ + + void +ttloop() +{ + void netflush(); + + DIAG(TD_REPORT, {sprintf(nfrontp, "td: ttloop\r\n"); + nfrontp += strlen(nfrontp);}); + if (nfrontp-nbackp) { + netflush(); + } + ncc = read(net, netibuf, sizeof netibuf); + if (ncc < 0) { + syslog(LOG_INFO, "ttloop: read: %m\n"); + exit(1); + } else if (ncc == 0) { + syslog(LOG_INFO, "ttloop: peer died: %m\n"); + exit(1); + } + DIAG(TD_REPORT, {sprintf(nfrontp, "td: ttloop read %d chars\r\n", ncc); + nfrontp += strlen(nfrontp);}); + netip = netibuf; + telrcv(); /* state machine */ + if (ncc > 0) { + pfrontp = pbackp = ptyobuf; + telrcv(); + } +} /* end of ttloop */ + +/* + * Check a descriptor to see if out of band data exists on it. + */ + int +stilloob(s) + int s; /* socket number */ +{ + static struct timeval timeout = { 0 }; + fd_set excepts; + int value; + + do { + FD_ZERO(&excepts); + FD_SET(s, &excepts); + value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); + } while ((value == -1) && (errno == EINTR)); + + if (value < 0) { + fatalperror(pty, "select"); + } + if (FD_ISSET(s, &excepts)) { + return 1; + } else { + return 0; + } +} + + void +ptyflush() +{ + int n; + + if ((n = pfrontp - pbackp) > 0) { + DIAG((TD_REPORT | TD_PTYDATA), + { sprintf(nfrontp, "td: ptyflush %d chars\r\n", n); + nfrontp += strlen(nfrontp); }); + DIAG(TD_PTYDATA, printdata("pd", pbackp, n)); + n = write(pty, pbackp, n); + } + if (n < 0) { + if (errno == EWOULDBLOCK || errno == EINTR) + return; + cleanup(0); + } + pbackp += n; + if (pbackp == pfrontp) + pbackp = pfrontp = ptyobuf; +} + +/* + * nextitem() + * + * Return the address of the next "item" in the TELNET data + * stream. This will be the address of the next character if + * the current address is a user data character, or it will + * be the address of the character following the TELNET command + * if the current address is a TELNET IAC ("I Am a Command") + * character. + */ + char * +nextitem(current) + char *current; +{ + if ((*current&0xff) != IAC) { + return current+1; + } + switch (*(current+1)&0xff) { + case DO: + case DONT: + case WILL: + case WONT: + return current+3; + case SB: /* loop forever looking for the SE */ + { + register char *look = current+2; + + for (;;) { + if ((*look++&0xff) == IAC) { + if ((*look++&0xff) == SE) { + return look; + } + } + } + } + default: + return current+2; + } +} /* end of nextitem */ + + +/* + * netclear() + * + * We are about to do a TELNET SYNCH operation. Clear + * the path to the network. + * + * Things are a bit tricky since we may have sent the first + * byte or so of a previous TELNET command into the network. + * So, we have to scan the network buffer from the beginning + * until we are up to where we want to be. + * + * A side effect of what we do, just to keep things + * simple, is to clear the urgent data pointer. The principal + * caller should be setting the urgent data pointer AFTER calling + * us in any case. + */ + void +netclear() +{ + register char *thisitem, *next; + char *good; +#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ + ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) + + thisitem = netobuf; + + while ((next = nextitem(thisitem)) <= nbackp) { + thisitem = next; + } + + /* Now, thisitem is first before/at boundary. */ + + good = netobuf; /* where the good bytes go */ + + while (nfrontp > thisitem) { + if (wewant(thisitem)) { + int length; + + next = thisitem; + do { + next = nextitem(next); + } while (wewant(next) && (nfrontp > next)); + length = next-thisitem; + bcopy(thisitem, good, length); + good += length; + thisitem = next; + } else { + thisitem = nextitem(thisitem); + } + } + + nbackp = netobuf; + nfrontp = good; /* next byte to be sent */ + neturg = 0; +} /* end of netclear */ + +/* + * netflush + * Send as much data as possible to the network, + * handling requests for urgent data. + */ + void +netflush() +{ + int n; + extern int not42; + + if ((n = nfrontp - nbackp) > 0) { + DIAG(TD_REPORT, + { sprintf(nfrontp, "td: netflush %d chars\r\n", n); + n += strlen(nfrontp); /* get count first */ + nfrontp += strlen(nfrontp); /* then move pointer */ + }); + /* + * if no urgent data, or if the other side appears to be an + * old 4.2 client (and thus unable to survive TCP urgent data), + * write the entire buffer in non-OOB mode. + */ + if ((neturg == 0) || (not42 == 0)) { + n = write(net, nbackp, n); /* normal write */ + } else { + n = neturg - nbackp; + /* + * In 4.2 (and 4.3) systems, there is some question about + * what byte in a sendOOB operation is the "OOB" data. + * To make ourselves compatible, we only send ONE byte + * out of band, the one WE THINK should be OOB (though + * we really have more the TCP philosophy of urgent data + * rather than the Unix philosophy of OOB data). + */ + if (n > 1) { + n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ + } else { + n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ + } + } + } + if (n < 0) { + if (errno == EWOULDBLOCK || errno == EINTR) + return; + cleanup(0); + } + nbackp += n; + if (nbackp >= neturg) { + neturg = 0; + } + if (nbackp == nfrontp) { + nbackp = nfrontp = netobuf; + } + return; +} /* end of netflush */ + + +/* + * writenet + * + * Just a handy little function to write a bit of raw data to the net. + * It will force a transmit of the buffer if necessary + * + * arguments + * ptr - A pointer to a character string to write + * len - How many bytes to write + */ + void +writenet(ptr, len) + register unsigned char *ptr; + register int len; +{ + /* flush buffer if no room for new data) */ + if ((&netobuf[BUFSIZ] - nfrontp) < len) { + /* if this fails, don't worry, buffer is a little big */ + netflush(); + } + + bcopy(ptr, nfrontp, len); + nfrontp += len; + +} /* end of writenet */ + + +/* + * miscellaneous functions doing a variety of little jobs follow ... + */ + + + void +fatal(f, msg) + int f; + char *msg; +{ + char buf[BUFSIZ]; + + (void) sprintf(buf, "telnetd: %s.\r\n", msg); + (void) write(f, buf, (int)strlen(buf)); + sleep(1); /*XXX*/ + exit(1); +} + + void +fatalperror(f, msg) + int f; + char *msg; +{ + char buf[BUFSIZ], *strerror(); + + (void) sprintf(buf, "%s: %s\r\n", msg, strerror(errno)); + fatal(f, buf); +} + +char editedhost[32]; + + void +edithost(pat, host) + register char *pat; + register char *host; +{ + register char *res = editedhost; + char *strncpy(); + + if (!pat) + pat = ""; + while (*pat) { + switch (*pat) { + + case '#': + if (*host) + host++; + break; + + case '@': + if (*host) + *res++ = *host++; + break; + + default: + *res++ = *pat; + break; + } + if (res == &editedhost[sizeof editedhost - 1]) { + *res = '\0'; + return; + } + pat++; + } + if (*host) + (void) strncpy(res, host, + sizeof editedhost - (res - editedhost) -1); + else + *res = '\0'; + editedhost[sizeof editedhost - 1] = '\0'; +} + +static char *putlocation; + + void +putstr(s) + register char *s; +{ + + while (*s) + putchr(*s++); +} + + void +putchr(cc) + int cc; +{ + *putlocation++ = cc; +} + +/* + * This is split on two lines so that SCCS will not see the M + * between two % signs and expand it... + */ +static char fmtstr[] = { "%l:%M\ +%P on %A, %d %B %Y" }; + + void +putf(cp, where) + register char *cp; + char *where; +{ + char *slash; + time_t t; + char db[100]; +#ifdef STREAMSPTY + extern char *index(); +#else + extern char *rindex(); +#endif + + putlocation = where; + + while (*cp) { + if (*cp != '%') { + putchr(*cp++); + continue; + } + switch (*++cp) { + + case 't': +#ifdef STREAMSPTY + /* names are like /dev/pts/2 -- we want pts/2 */ + slash = index(line+1, '/'); +#else + slash = rindex(line, '/'); +#endif + if (slash == (char *) 0) + putstr(line); + else + putstr(&slash[1]); + break; + + case 'h': + putstr(editedhost); + break; + + case 'd': + (void)time(&t); + (void)strftime(db, sizeof(db), fmtstr, localtime(&t)); + putstr(db); + break; + + case '%': + putchr('%'); + break; + } + cp++; + } +} + +#ifdef DIAGNOSTICS +/* + * Print telnet options and commands in plain text, if possible. + */ + void +printoption(fmt, option) + register char *fmt; + register int option; +{ + if (TELOPT_OK(option)) + sprintf(nfrontp, "%s %s\r\n", fmt, TELOPT(option)); + else if (TELCMD_OK(option)) + sprintf(nfrontp, "%s %s\r\n", fmt, TELCMD(option)); + else + sprintf(nfrontp, "%s %d\r\n", fmt, option); + nfrontp += strlen(nfrontp); + return; +} + + void +printsub(direction, pointer, length) + char direction; /* '<' or '>' */ + unsigned char *pointer; /* where suboption data sits */ + int length; /* length of suboption data */ +{ + register int i; + char buf[512]; + + if (!(diagnostic & TD_OPTIONS)) + return; + + if (direction) { + sprintf(nfrontp, "td: %s suboption ", + direction == '<' ? "recv" : "send"); + nfrontp += strlen(nfrontp); + if (length >= 3) { + register int j; + + i = pointer[length-2]; + j = pointer[length-1]; + + if (i != IAC || j != SE) { + sprintf(nfrontp, "(terminated by "); + nfrontp += strlen(nfrontp); + if (TELOPT_OK(i)) + sprintf(nfrontp, "%s ", TELOPT(i)); + else if (TELCMD_OK(i)) + sprintf(nfrontp, "%s ", TELCMD(i)); + else + sprintf(nfrontp, "%d ", i); + nfrontp += strlen(nfrontp); + if (TELOPT_OK(j)) + sprintf(nfrontp, "%s", TELOPT(j)); + else if (TELCMD_OK(j)) + sprintf(nfrontp, "%s", TELCMD(j)); + else + sprintf(nfrontp, "%d", j); + nfrontp += strlen(nfrontp); + sprintf(nfrontp, ", not IAC SE!) "); + nfrontp += strlen(nfrontp); + } + } + length -= 2; + } + if (length < 1) { + sprintf(nfrontp, "(Empty suboption??\?)"); + nfrontp += strlen(nfrontp); + return; + } + switch (pointer[0]) { + case TELOPT_TTYPE: + sprintf(nfrontp, "TERMINAL-TYPE "); + nfrontp += strlen(nfrontp); + switch (pointer[1]) { + case TELQUAL_IS: + sprintf(nfrontp, "IS \"%.*s\"", length-2, (char *)pointer+2); + break; + case TELQUAL_SEND: + sprintf(nfrontp, "SEND"); + break; + default: + sprintf(nfrontp, + "- unknown qualifier %d (0x%x).", + pointer[1], pointer[1]); + } + nfrontp += strlen(nfrontp); + break; + case TELOPT_TSPEED: + sprintf(nfrontp, "TERMINAL-SPEED"); + nfrontp += strlen(nfrontp); + if (length < 2) { + sprintf(nfrontp, " (empty suboption??\?)"); + nfrontp += strlen(nfrontp); + break; + } + switch (pointer[1]) { + case TELQUAL_IS: + sprintf(nfrontp, " IS %.*s", length-2, (char *)pointer+2); + nfrontp += strlen(nfrontp); + break; + default: + if (pointer[1] == 1) + sprintf(nfrontp, " SEND"); + else + sprintf(nfrontp, " %d (unknown)", pointer[1]); + nfrontp += strlen(nfrontp); + for (i = 2; i < length; i++) { + sprintf(nfrontp, " ?%d?", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + } + break; + + case TELOPT_LFLOW: + sprintf(nfrontp, "TOGGLE-FLOW-CONTROL"); + nfrontp += strlen(nfrontp); + if (length < 2) { + sprintf(nfrontp, " (empty suboption??\?)"); + nfrontp += strlen(nfrontp); + break; + } + switch (pointer[1]) { + case LFLOW_OFF: + sprintf(nfrontp, " OFF"); break; + case LFLOW_ON: + sprintf(nfrontp, " ON"); break; + case LFLOW_RESTART_ANY: + sprintf(nfrontp, " RESTART-ANY"); break; + case LFLOW_RESTART_XON: + sprintf(nfrontp, " RESTART-XON"); break; + default: + sprintf(nfrontp, " %d (unknown)", pointer[1]); + } + nfrontp += strlen(nfrontp); + for (i = 2; i < length; i++) { + sprintf(nfrontp, " ?%d?", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + + case TELOPT_NAWS: + sprintf(nfrontp, "NAWS"); + nfrontp += strlen(nfrontp); + if (length < 2) { + sprintf(nfrontp, " (empty suboption??\?)"); + nfrontp += strlen(nfrontp); + break; + } + if (length == 2) { + sprintf(nfrontp, " ?%d?", pointer[1]); + nfrontp += strlen(nfrontp); + break; + } + sprintf(nfrontp, " %d %d (%d)", + pointer[1], pointer[2], + (int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2]))); + nfrontp += strlen(nfrontp); + if (length == 4) { + sprintf(nfrontp, " ?%d?", pointer[3]); + nfrontp += strlen(nfrontp); + break; + } + sprintf(nfrontp, " %d %d (%d)", + pointer[3], pointer[4], + (int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4]))); + nfrontp += strlen(nfrontp); + for (i = 5; i < length; i++) { + sprintf(nfrontp, " ?%d?", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + + case TELOPT_LINEMODE: + sprintf(nfrontp, "LINEMODE "); + nfrontp += strlen(nfrontp); + if (length < 2) { + sprintf(nfrontp, " (empty suboption??\?)"); + nfrontp += strlen(nfrontp); + break; + } + switch (pointer[1]) { + case WILL: + sprintf(nfrontp, "WILL "); + goto common; + case WONT: + sprintf(nfrontp, "WONT "); + goto common; + case DO: + sprintf(nfrontp, "DO "); + goto common; + case DONT: + sprintf(nfrontp, "DONT "); + common: + nfrontp += strlen(nfrontp); + if (length < 3) { + sprintf(nfrontp, "(no option??\?)"); + nfrontp += strlen(nfrontp); + break; + } + switch (pointer[2]) { + case LM_FORWARDMASK: + sprintf(nfrontp, "Forward Mask"); + nfrontp += strlen(nfrontp); + for (i = 3; i < length; i++) { + sprintf(nfrontp, " %x", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + default: + sprintf(nfrontp, "%d (unknown)", pointer[2]); + nfrontp += strlen(nfrontp); + for (i = 3; i < length; i++) { + sprintf(nfrontp, " %d", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + } + break; + + case LM_SLC: + sprintf(nfrontp, "SLC"); + nfrontp += strlen(nfrontp); + for (i = 2; i < length - 2; i += 3) { + if (SLC_NAME_OK(pointer[i+SLC_FUNC])) + sprintf(nfrontp, " %s", SLC_NAME(pointer[i+SLC_FUNC])); + else + sprintf(nfrontp, " %d", pointer[i+SLC_FUNC]); + nfrontp += strlen(nfrontp); + switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) { + case SLC_NOSUPPORT: + sprintf(nfrontp, " NOSUPPORT"); break; + case SLC_CANTCHANGE: + sprintf(nfrontp, " CANTCHANGE"); break; + case SLC_VARIABLE: + sprintf(nfrontp, " VARIABLE"); break; + case SLC_DEFAULT: + sprintf(nfrontp, " DEFAULT"); break; + } + nfrontp += strlen(nfrontp); + sprintf(nfrontp, "%s%s%s", + pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "", + pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "", + pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : ""); + nfrontp += strlen(nfrontp); + if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN| + SLC_FLUSHOUT| SLC_LEVELBITS)) { + sprintf(nfrontp, "(0x%x)", pointer[i+SLC_FLAGS]); + nfrontp += strlen(nfrontp); + } + sprintf(nfrontp, " %d;", pointer[i+SLC_VALUE]); + nfrontp += strlen(nfrontp); + if ((pointer[i+SLC_VALUE] == IAC) && + (pointer[i+SLC_VALUE+1] == IAC)) + i++; + } + for (; i < length; i++) { + sprintf(nfrontp, " ?%d?", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + + case LM_MODE: + sprintf(nfrontp, "MODE "); + nfrontp += strlen(nfrontp); + if (length < 3) { + sprintf(nfrontp, "(no mode??\?)"); + nfrontp += strlen(nfrontp); + break; + } + { + char tbuf[32]; + sprintf(tbuf, "%s%s%s%s%s", + pointer[2]&MODE_EDIT ? "|EDIT" : "", + pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "", + pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "", + pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "", + pointer[2]&MODE_ACK ? "|ACK" : ""); + sprintf(nfrontp, "%s", tbuf[1] ? &tbuf[1] : "0"); + nfrontp += strlen(nfrontp); + } + if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) { + sprintf(nfrontp, " (0x%x)", pointer[2]); + nfrontp += strlen(nfrontp); + } + for (i = 3; i < length; i++) { + sprintf(nfrontp, " ?0x%x?", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + default: + sprintf(nfrontp, "%d (unknown)", pointer[1]); + nfrontp += strlen(nfrontp); + for (i = 2; i < length; i++) { + sprintf(nfrontp, " %d", pointer[i]); + nfrontp += strlen(nfrontp); + } + } + break; + + case TELOPT_STATUS: { + register char *cp; + register int j, k; + + sprintf(nfrontp, "STATUS"); + nfrontp += strlen(nfrontp); + + switch (pointer[1]) { + default: + if (pointer[1] == TELQUAL_SEND) + sprintf(nfrontp, " SEND"); + else + sprintf(nfrontp, " %d (unknown)", pointer[1]); + nfrontp += strlen(nfrontp); + for (i = 2; i < length; i++) { + sprintf(nfrontp, " ?%d?", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + case TELQUAL_IS: + sprintf(nfrontp, " IS\r\n"); + nfrontp += strlen(nfrontp); + + for (i = 2; i < length; i++) { + switch(pointer[i]) { + case DO: cp = "DO"; goto common2; + case DONT: cp = "DONT"; goto common2; + case WILL: cp = "WILL"; goto common2; + case WONT: cp = "WONT"; goto common2; + common2: + i++; + if (TELOPT_OK(pointer[i])) + sprintf(nfrontp, " %s %s", cp, TELOPT(pointer[i])); + else + sprintf(nfrontp, " %s %d", cp, pointer[i]); + nfrontp += strlen(nfrontp); + + sprintf(nfrontp, "\r\n"); + nfrontp += strlen(nfrontp); + break; + + case SB: + sprintf(nfrontp, " SB "); + nfrontp += strlen(nfrontp); + i++; + j = k = i; + while (j < length) { + if (pointer[j] == SE) { + if (j+1 == length) + break; + if (pointer[j+1] == SE) + j++; + else + break; + } + pointer[k++] = pointer[j++]; + } + printsub(0, &pointer[i], k - i); + if (i < length) { + sprintf(nfrontp, " SE"); + nfrontp += strlen(nfrontp); + i = j; + } else + i = j - 1; + + sprintf(nfrontp, "\r\n"); + nfrontp += strlen(nfrontp); + + break; + + default: + sprintf(nfrontp, " %d", pointer[i]); + nfrontp += strlen(nfrontp); + break; + } + } + break; + } + break; + } + + case TELOPT_XDISPLOC: + sprintf(nfrontp, "X-DISPLAY-LOCATION "); + nfrontp += strlen(nfrontp); + switch (pointer[1]) { + case TELQUAL_IS: + sprintf(nfrontp, "IS \"%.*s\"", length-2, (char *)pointer+2); + break; + case TELQUAL_SEND: + sprintf(nfrontp, "SEND"); + break; + default: + sprintf(nfrontp, "- unknown qualifier %d (0x%x).", + pointer[1], pointer[1]); + } + nfrontp += strlen(nfrontp); + break; + + case TELOPT_NEW_ENVIRON: + sprintf(nfrontp, "NEW-ENVIRON "); + goto env_common1; + case TELOPT_OLD_ENVIRON: + sprintf(nfrontp, "OLD-ENVIRON"); + env_common1: + nfrontp += strlen(nfrontp); + switch (pointer[1]) { + case TELQUAL_IS: + sprintf(nfrontp, "IS "); + goto env_common; + case TELQUAL_SEND: + sprintf(nfrontp, "SEND "); + goto env_common; + case TELQUAL_INFO: + sprintf(nfrontp, "INFO "); + env_common: + nfrontp += strlen(nfrontp); + { + register int noquote = 2; + for (i = 2; i < length; i++ ) { + switch (pointer[i]) { + case NEW_ENV_VAR: + sprintf(nfrontp, "\" VAR " + noquote); + nfrontp += strlen(nfrontp); + noquote = 2; + break; + + case NEW_ENV_VALUE: + sprintf(nfrontp, "\" VALUE " + noquote); + nfrontp += strlen(nfrontp); + noquote = 2; + break; + + case ENV_ESC: + sprintf(nfrontp, "\" ESC " + noquote); + nfrontp += strlen(nfrontp); + noquote = 2; + break; + + case ENV_USERVAR: + sprintf(nfrontp, "\" USERVAR " + noquote); + nfrontp += strlen(nfrontp); + noquote = 2; + break; + + default: + def_case: + if (isprint(pointer[i]) && pointer[i] != '"') { + if (noquote) { + *nfrontp++ = '"'; + noquote = 0; + } + *nfrontp++ = pointer[i]; + } else { + sprintf(nfrontp, "\" %03o " + noquote, + pointer[i]); + nfrontp += strlen(nfrontp); + noquote = 2; + } + break; + } + } + if (!noquote) + *nfrontp++ = '"'; + break; + } + } + break; + +#if defined(AUTHENTICATION) + case TELOPT_AUTHENTICATION: + sprintf(nfrontp, "AUTHENTICATION"); + nfrontp += strlen(nfrontp); + + if (length < 2) { + sprintf(nfrontp, " (empty suboption??\?)"); + nfrontp += strlen(nfrontp); + break; + } + switch (pointer[1]) { + case TELQUAL_REPLY: + case TELQUAL_IS: + sprintf(nfrontp, " %s ", (pointer[1] == TELQUAL_IS) ? + "IS" : "REPLY"); + nfrontp += strlen(nfrontp); + if (AUTHTYPE_NAME_OK(pointer[2])) + sprintf(nfrontp, "%s ", AUTHTYPE_NAME(pointer[2])); + else + sprintf(nfrontp, "%d ", pointer[2]); + nfrontp += strlen(nfrontp); + if (length < 3) { + sprintf(nfrontp, "(partial suboption??\?)"); + nfrontp += strlen(nfrontp); + break; + } + sprintf(nfrontp, "%s|%s", + ((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ? + "CLIENT" : "SERVER", + ((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ? + "MUTUAL" : "ONE-WAY"); + nfrontp += strlen(nfrontp); + + auth_printsub(&pointer[1], length - 1, buf, sizeof(buf)); + sprintf(nfrontp, "%s", buf); + nfrontp += strlen(nfrontp); + break; + + case TELQUAL_SEND: + i = 2; + sprintf(nfrontp, " SEND "); + nfrontp += strlen(nfrontp); + while (i < length) { + if (AUTHTYPE_NAME_OK(pointer[i])) + sprintf(nfrontp, "%s ", AUTHTYPE_NAME(pointer[i])); + else + sprintf(nfrontp, "%d ", pointer[i]); + nfrontp += strlen(nfrontp); + if (++i >= length) { + sprintf(nfrontp, "(partial suboption??\?)"); + nfrontp += strlen(nfrontp); + break; + } + sprintf(nfrontp, "%s|%s ", + ((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ? + "CLIENT" : "SERVER", + ((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ? + "MUTUAL" : "ONE-WAY"); + nfrontp += strlen(nfrontp); + ++i; + } + break; + + case TELQUAL_NAME: + i = 2; + sprintf(nfrontp, " NAME \""); + nfrontp += strlen(nfrontp); + while (i < length) + *nfrontp += pointer[i++]; + *nfrontp += '"'; + break; + + default: + for (i = 2; i < length; i++) { + sprintf(nfrontp, " ?%d?", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + } + break; +#endif + + + default: + if (TELOPT_OK(pointer[0])) + sprintf(nfrontp, "%s (unknown)", TELOPT(pointer[0])); + else + sprintf(nfrontp, "%d (unknown)", pointer[i]); + nfrontp += strlen(nfrontp); + for (i = 1; i < length; i++) { + sprintf(nfrontp, " %d", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + } + sprintf(nfrontp, "\r\n"); + nfrontp += strlen(nfrontp); +} + +/* + * Dump a data buffer in hex and ascii to the output data stream. + */ + void +printdata(tag, ptr, cnt) + register char *tag; + register char *ptr; + register int cnt; +{ + register int i; + char xbuf[30]; + + while (cnt) { + /* flush net output buffer if no room for new data) */ + if ((&netobuf[BUFSIZ] - nfrontp) < 80) { + netflush(); + } + + /* add a line of output */ + sprintf(nfrontp, "%s: ", tag); + nfrontp += strlen(nfrontp); + for (i = 0; i < 20 && cnt; i++) { + sprintf(nfrontp, "%02x", *ptr); + nfrontp += strlen(nfrontp); + if (isprint(*ptr)) { + xbuf[i] = *ptr; + } else { + xbuf[i] = '.'; + } + if (i % 2) { + *nfrontp = ' '; + nfrontp++; + } + cnt--; + ptr++; + } + xbuf[i] = '\0'; + sprintf(nfrontp, " %s\r\n", xbuf ); + nfrontp += strlen(nfrontp); + } +} +#endif /* DIAGNOSTICS */ diff --git a/libexec/tftpd/Makefile b/libexec/tftpd/Makefile new file mode 100644 index 0000000..728e2a2 --- /dev/null +++ b/libexec/tftpd/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= tftpd +SRCS= tftpd.c tftpsubs.c +MAN8= tftpd.8 +CFLAGS+=-I${.CURDIR}/../../usr.bin/tftp +.PATH: ${.CURDIR}/../../usr.bin/tftp + +.include <bsd.prog.mk> diff --git a/libexec/tftpd/tftpd.8 b/libexec/tftpd/tftpd.8 new file mode 100644 index 0000000..430c1c4 --- /dev/null +++ b/libexec/tftpd/tftpd.8 @@ -0,0 +1,106 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)tftpd.8 8.1 (Berkeley) 6/4/93 +.\" +.Dd June 4, 1993 +.Dt TFTPD 8 +.Os BSD 4.2 +.Sh NAME +.Nm tftpd +.Nd +Internet Trivial File Transfer Protocol server +.Sh SYNOPSIS +.Nm tftpd +.Op Fl l +.Op Fl n +.Op Ar directory ... +.Sh DESCRIPTION +.Nm Tftpd +is a server which supports the +Internet Trivial File Transfer +Protocol (\c +.Tn RFC 783). +The +.Tn TFTP +server operates +at the port indicated in the +.Ql tftp +service description; +see +.Xr services 5 . +The server is normally started by +.Xr inetd 8 . +.Pp +The use of +.Xr tftp 1 +does not require an account or password on the remote system. +Due to the lack of authentication information, +.Nm tftpd +will allow only publicly readable files to be +accessed. +Files containing the string ``/\|\fB.\|.\fP\|/'' are not allowed. +Files may be written only if they already exist and are publicly writable. +Note that this extends the concept of +.Dq public +to include +all users on all hosts that can be reached through the network; +this may not be appropriate on all systems, and its implications +should be considered before enabling tftp service. +The server should have the user ID with the lowest possible privilege. +.Pp +Access to files may be restricted by invoking +.Nm tftpd +with a list of directories by including up to 20 pathnames +as server program arguments in +.Pa /etc/inetd.conf . +In this case access is restricted to files whose +names are prefixed by the one of the given directories. +The given directories are also treated as a search path for +relative filename requests. +.Pp +The options are: +.Bl -tag -width Ds +.It Fl l +Logs all requests using +.Xr syslog 3 . +.It Fl n +Suppresses negative acknowledgement of requests for nonexistent +relative filenames. +.El +.Sh SEE ALSO +.Xr tftp 1 , +.Xr inetd 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/libexec/tftpd/tftpd.c b/libexec/tftpd/tftpd.c new file mode 100644 index 0000000..7500abf --- /dev/null +++ b/libexec/tftpd/tftpd.c @@ -0,0 +1,650 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * Trivial file transfer protocol server. + * + * This version includes many modifications by Jim Guyton + * <guyton@rand-unix>. + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/tftp.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "tftpsubs.h" + +#define TIMEOUT 5 + +int peer; +int rexmtval = TIMEOUT; +int maxtimeout = 5*TIMEOUT; + +#define PKTSIZE SEGSIZE+4 +char buf[PKTSIZE]; +char ackbuf[PKTSIZE]; +struct sockaddr_in from; +int fromlen; + +void tftp __P((struct tftphdr *, int)); + +/* + * Null-terminated directory prefix list for absolute pathname requests and + * search list for relative pathname requests. + * + * MAXDIRS should be at least as large as the number of arguments that + * inetd allows (currently 20). + */ +#define MAXDIRS 20 +static struct dirlist { + char *name; + int len; +} dirs[MAXDIRS+1]; +static int suppress_naks; +static int logging; + +static char *errtomsg __P((int)); +static void nak __P((int)); +static char *verifyhost __P((struct sockaddr_in *)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register struct tftphdr *tp; + register int n; + int ch, on; + struct sockaddr_in sin; + + openlog("tftpd", LOG_PID, LOG_FTP); + while ((ch = getopt(argc, argv, "ln")) != EOF) { + switch (ch) { + case 'l': + logging = 1; + break; + case 'n': + suppress_naks = 1; + break; + default: + syslog(LOG_WARNING, "ignoring unknown option -%c", ch); + } + } + if (optind < argc) { + struct dirlist *dirp; + + /* Get list of directory prefixes. Skip relative pathnames. */ + for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS]; + optind++) { + if (argv[optind][0] == '/') { + dirp->name = argv[optind]; + dirp->len = strlen(dirp->name); + dirp++; + } + } + } + + on = 1; + if (ioctl(0, FIONBIO, &on) < 0) { + syslog(LOG_ERR, "ioctl(FIONBIO): %m\n"); + exit(1); + } + fromlen = sizeof (from); + n = recvfrom(0, buf, sizeof (buf), 0, + (struct sockaddr *)&from, &fromlen); + if (n < 0) { + syslog(LOG_ERR, "recvfrom: %m\n"); + exit(1); + } + /* + * Now that we have read the message out of the UDP + * socket, we fork and exit. Thus, inetd will go back + * to listening to the tftp port, and the next request + * to come in will start up a new instance of tftpd. + * + * We do this so that inetd can run tftpd in "wait" mode. + * The problem with tftpd running in "nowait" mode is that + * inetd may get one or more successful "selects" on the + * tftp port before we do our receive, so more than one + * instance of tftpd may be started up. Worse, if tftpd + * break before doing the above "recvfrom", inetd would + * spawn endless instances, clogging the system. + */ + { + int pid; + int i, j; + + for (i = 1; i < 20; i++) { + pid = fork(); + if (pid < 0) { + sleep(i); + /* + * flush out to most recently sent request. + * + * This may drop some request, but those + * will be resent by the clients when + * they timeout. The positive effect of + * this flush is to (try to) prevent more + * than one tftpd being started up to service + * a single request from a single client. + */ + j = sizeof from; + i = recvfrom(0, buf, sizeof (buf), 0, + (struct sockaddr *)&from, &j); + if (i > 0) { + n = i; + fromlen = j; + } + } else { + break; + } + } + if (pid < 0) { + syslog(LOG_ERR, "fork: %m\n"); + exit(1); + } else if (pid != 0) { + exit(0); + } + } + from.sin_family = AF_INET; + alarm(0); + close(0); + close(1); + peer = socket(AF_INET, SOCK_DGRAM, 0); + if (peer < 0) { + syslog(LOG_ERR, "socket: %m\n"); + exit(1); + } + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) { + syslog(LOG_ERR, "bind: %m\n"); + exit(1); + } + if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) { + syslog(LOG_ERR, "connect: %m\n"); + exit(1); + } + tp = (struct tftphdr *)buf; + tp->th_opcode = ntohs(tp->th_opcode); + if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) + tftp(tp, n); + exit(1); +} + +struct formats; +int validate_access __P((char **, int)); +void sendfile __P((struct formats *)); +void recvfile __P((struct formats *)); + +struct formats { + char *f_mode; + int (*f_validate) __P((char **, int)); + void (*f_send) __P((struct formats *)); + void (*f_recv) __P((struct formats *)); + int f_convert; +} formats[] = { + { "netascii", validate_access, sendfile, recvfile, 1 }, + { "octet", validate_access, sendfile, recvfile, 0 }, +#ifdef notdef + { "mail", validate_user, sendmail, recvmail, 1 }, +#endif + { 0 } +}; + +/* + * Handle initial connection protocol. + */ +void +tftp(tp, size) + struct tftphdr *tp; + int size; +{ + register char *cp; + int first = 1, ecode; + register struct formats *pf; + char *filename, *mode; + + filename = cp = tp->th_stuff; +again: + while (cp < buf + size) { + if (*cp == '\0') + break; + cp++; + } + if (*cp != '\0') { + nak(EBADOP); + exit(1); + } + if (first) { + mode = ++cp; + first = 0; + goto again; + } + for (cp = mode; *cp; cp++) + if (isupper(*cp)) + *cp = tolower(*cp); + for (pf = formats; pf->f_mode; pf++) + if (strcmp(pf->f_mode, mode) == 0) + break; + if (pf->f_mode == 0) { + nak(EBADOP); + exit(1); + } + ecode = (*pf->f_validate)(&filename, tp->th_opcode); + if (logging) { + syslog(LOG_INFO, "%s: %s request for %s: %s", + verifyhost(&from), + tp->th_opcode == WRQ ? "write" : "read", + filename, errtomsg(ecode)); + } + if (ecode) { + /* + * Avoid storms of naks to a RRQ broadcast for a relative + * bootfile pathname from a diskless Sun. + */ + if (suppress_naks && *filename != '/' && ecode == ENOTFOUND) + exit(0); + nak(ecode); + exit(1); + } + if (tp->th_opcode == WRQ) + (*pf->f_recv)(pf); + else + (*pf->f_send)(pf); + exit(0); +} + + +FILE *file; + +/* + * Validate file access. Since we + * have no uid or gid, for now require + * file to exist and be publicly + * readable/writable. + * If we were invoked with arguments + * from inetd then the file must also be + * in one of the given directory prefixes. + * Note also, full path name must be + * given as we have no login directory. + */ +int +validate_access(filep, mode) + char **filep; + int mode; +{ + struct stat stbuf; + int fd; + struct dirlist *dirp; + static char pathname[MAXPATHLEN]; + char *filename = *filep; + + /* + * Prevent tricksters from getting around the directory restrictions + */ + if (strstr(filename, "/../")) + return (EACCESS); + + if (*filename == '/') { + /* + * Allow the request if it's in one of the approved locations. + * Special case: check the null prefix ("/") by looking + * for length = 1 and relying on the arg. processing that + * it's a /. + */ + for (dirp = dirs; dirp->name != NULL; dirp++) { + if (dirp->len == 1 || + (!strncmp(filename, dirp->name, dirp->len) && + filename[dirp->len] == '/')) + break; + } + /* If directory list is empty, allow access to any file */ + if (dirp->name == NULL && dirp != dirs) + return (EACCESS); + if (stat(filename, &stbuf) < 0) + return (errno == ENOENT ? ENOTFOUND : EACCESS); + if ((stbuf.st_mode & S_IFMT) != S_IFREG) + return (ENOTFOUND); + if (mode == RRQ) { + if ((stbuf.st_mode & S_IROTH) == 0) + return (EACCESS); + } else { + if ((stbuf.st_mode & S_IWOTH) == 0) + return (EACCESS); + } + } else { + int err; + + /* + * Relative file name: search the approved locations for it. + * Don't allow write requests that avoid directory + * restrictions. + */ + + if (!strncmp(filename, "../", 3)) + return (EACCESS); + + /* + * If the file exists in one of the directories and isn't + * readable, continue looking. However, change the error code + * to give an indication that the file exists. + */ + err = ENOTFOUND; + for (dirp = dirs; dirp->name != NULL; dirp++) { + sprintf(pathname, "%s/%s", dirp->name, filename); + if (stat(pathname, &stbuf) == 0 && + (stbuf.st_mode & S_IFMT) == S_IFREG) { + if ((stbuf.st_mode & S_IROTH) != 0) { + break; + } + err = EACCESS; + } + } + if (dirp->name == NULL) + return (err); + *filep = filename = pathname; + } + fd = open(filename, mode == RRQ ? 0 : 1); + if (fd < 0) + return (errno + 100); + file = fdopen(fd, (mode == RRQ)? "r":"w"); + if (file == NULL) { + return errno+100; + } + return (0); +} + +int timeout; +jmp_buf timeoutbuf; + +void +timer() +{ + + timeout += rexmtval; + if (timeout >= maxtimeout) + exit(1); + longjmp(timeoutbuf, 1); +} + +/* + * Send the requested file. + */ +void +sendfile(pf) + struct formats *pf; +{ + struct tftphdr *dp, *r_init(); + register struct tftphdr *ap; /* ack packet */ + register int size, n; + volatile int block; + + signal(SIGALRM, timer); + dp = r_init(); + ap = (struct tftphdr *)ackbuf; + block = 1; + do { + size = readit(file, &dp, pf->f_convert); + if (size < 0) { + nak(errno + 100); + goto abort; + } + dp->th_opcode = htons((u_short)DATA); + dp->th_block = htons((u_short)block); + timeout = 0; + (void)setjmp(timeoutbuf); + +send_data: + if (send(peer, dp, size + 4, 0) != size + 4) { + syslog(LOG_ERR, "tftpd: write: %m\n"); + goto abort; + } + read_ahead(file, pf->f_convert); + for ( ; ; ) { + alarm(rexmtval); /* read the ack */ + n = recv(peer, ackbuf, sizeof (ackbuf), 0); + alarm(0); + if (n < 0) { + syslog(LOG_ERR, "tftpd: read: %m\n"); + goto abort; + } + ap->th_opcode = ntohs((u_short)ap->th_opcode); + ap->th_block = ntohs((u_short)ap->th_block); + + if (ap->th_opcode == ERROR) + goto abort; + + if (ap->th_opcode == ACK) { + if (ap->th_block == block) + break; + /* Re-synchronize with the other side */ + (void) synchnet(peer); + if (ap->th_block == (block -1)) + goto send_data; + } + + } + block++; + } while (size == SEGSIZE); +abort: + (void) fclose(file); +} + +void +justquit() +{ + exit(0); +} + + +/* + * Receive a file. + */ +void +recvfile(pf) + struct formats *pf; +{ + struct tftphdr *dp, *w_init(); + register struct tftphdr *ap; /* ack buffer */ + register int n, size; + volatile int block; + + signal(SIGALRM, timer); + dp = w_init(); + ap = (struct tftphdr *)ackbuf; + block = 0; + do { + timeout = 0; + ap->th_opcode = htons((u_short)ACK); + ap->th_block = htons((u_short)block); + block++; + (void) setjmp(timeoutbuf); +send_ack: + if (send(peer, ackbuf, 4, 0) != 4) { + syslog(LOG_ERR, "tftpd: write: %m\n"); + goto abort; + } + write_behind(file, pf->f_convert); + for ( ; ; ) { + alarm(rexmtval); + n = recv(peer, dp, PKTSIZE, 0); + alarm(0); + if (n < 0) { /* really? */ + syslog(LOG_ERR, "tftpd: read: %m\n"); + goto abort; + } + dp->th_opcode = ntohs((u_short)dp->th_opcode); + dp->th_block = ntohs((u_short)dp->th_block); + if (dp->th_opcode == ERROR) + goto abort; + if (dp->th_opcode == DATA) { + if (dp->th_block == block) { + break; /* normal */ + } + /* Re-synchronize with the other side */ + (void) synchnet(peer); + if (dp->th_block == (block-1)) + goto send_ack; /* rexmit */ + } + } + /* size = write(file, dp->th_data, n - 4); */ + size = writeit(file, &dp, n - 4, pf->f_convert); + if (size != (n-4)) { /* ahem */ + if (size < 0) nak(errno + 100); + else nak(ENOSPACE); + goto abort; + } + } while (size == SEGSIZE); + write_behind(file, pf->f_convert); + (void) fclose(file); /* close data file */ + + ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ + ap->th_block = htons((u_short)(block)); + (void) send(peer, ackbuf, 4, 0); + + signal(SIGALRM, justquit); /* just quit on timeout */ + alarm(rexmtval); + n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ + alarm(0); + if (n >= 4 && /* if read some data */ + dp->th_opcode == DATA && /* and got a data block */ + block == dp->th_block) { /* then my last ack was lost */ + (void) send(peer, ackbuf, 4, 0); /* resend final ack */ + } +abort: + return; +} + +struct errmsg { + int e_code; + char *e_msg; +} errmsgs[] = { + { EUNDEF, "Undefined error code" }, + { ENOTFOUND, "File not found" }, + { EACCESS, "Access violation" }, + { ENOSPACE, "Disk full or allocation exceeded" }, + { EBADOP, "Illegal TFTP operation" }, + { EBADID, "Unknown transfer ID" }, + { EEXISTS, "File already exists" }, + { ENOUSER, "No such user" }, + { -1, 0 } +}; + +static char * +errtomsg(error) + int error; +{ + static char buf[20]; + register struct errmsg *pe; + if (error == 0) + return "success"; + for (pe = errmsgs; pe->e_code >= 0; pe++) + if (pe->e_code == error) + return pe->e_msg; + sprintf(buf, "error %d", error); + return buf; +} + +/* + * Send a nak packet (error message). + * Error code passed in is one of the + * standard TFTP codes, or a UNIX errno + * offset by 100. + */ +static void +nak(error) + int error; +{ + register struct tftphdr *tp; + int length; + register struct errmsg *pe; + + tp = (struct tftphdr *)buf; + tp->th_opcode = htons((u_short)ERROR); + tp->th_code = htons((u_short)error); + for (pe = errmsgs; pe->e_code >= 0; pe++) + if (pe->e_code == error) + break; + if (pe->e_code < 0) { + pe->e_msg = strerror(error - 100); + tp->th_code = EUNDEF; /* set 'undef' errorcode */ + } + strcpy(tp->th_msg, pe->e_msg); + length = strlen(pe->e_msg); + tp->th_msg[length] = '\0'; + length += 5; + if (send(peer, buf, length, 0) != length) + syslog(LOG_ERR, "nak: %m\n"); +} + +static char * +verifyhost(fromp) + struct sockaddr_in *fromp; +{ + struct hostent *hp; + + hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (fromp->sin_addr), + fromp->sin_family); + if (hp) + return hp->h_name; + else + return inet_ntoa(fromp->sin_addr); +} diff --git a/libexec/uucpd/Makefile b/libexec/uucpd/Makefile new file mode 100644 index 0000000..986c216 --- /dev/null +++ b/libexec/uucpd/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= uucpd +NOMAN= noman +LDADD= -lcrypt -lutil +DPADD= ${LIBCRYPT} ${LIBUTIL} +.include <bsd.prog.mk> diff --git a/libexec/uucpd/pathnames.h b/libexec/uucpd/pathnames.h new file mode 100644 index 0000000..46310a4 --- /dev/null +++ b/libexec/uucpd/pathnames.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + */ + +#include <paths.h> + +#define _PATH_UUCICO "/usr/libexec/uucp/uucico" diff --git a/libexec/uucpd/uucpd.c b/libexec/uucpd/uucpd.c new file mode 100644 index 0000000..f645eea --- /dev/null +++ b/libexec/uucpd/uucpd.c @@ -0,0 +1,264 @@ +/* + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Rick Adams. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1985, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)uucpd.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * 4.2BSD TCP/IP server for uucico + * uucico's TCP channel causes this server to be run at the remote end. + */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <signal.h> +#include <fcntl.h> +#include <time.h> +#include <pwd.h> +#include <unistd.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <utmp.h> +#include <syslog.h> +#include "pathnames.h" + +#define SCPYN(a, b) strncpy(a, b, sizeof (a)) + +struct sockaddr_in hisctladdr; +int hisaddrlen = sizeof hisctladdr; +struct sockaddr_in myctladdr; +int mypid; + +char Username[64], Logname[64]; +char *nenv[] = { + Username, + Logname, + NULL, +}; +extern char **environ; +extern void logwtmp(char *line, char *name, char *host); + +void doit(struct sockaddr_in *sinp); +void dologout(void); +int readline(char start[], int num, int passw); +void dologin(struct passwd *pw, struct sockaddr_in *sin); + +void main(int argc, char **argv) +{ + environ = nenv; + close(1); close(2); + dup(0); dup(0); + hisaddrlen = sizeof (hisctladdr); + openlog("uucpd", LOG_PID, LOG_DAEMON); + if (getpeername(0, (struct sockaddr *)&hisctladdr, &hisaddrlen) < 0) { + syslog(LOG_ERR, "getpeername: %m"); + _exit(1); + } + doit(&hisctladdr); + dologout(); + exit(0); +} + +void badlogin(char *name, struct sockaddr_in *sin) +{ + char remotehost[32]; + struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, + sizeof (struct in_addr), AF_INET); + + if (hp) { + strncpy(remotehost, hp->h_name, sizeof (remotehost)); + endhostent(); + } else + strncpy(remotehost, inet_ntoa(sin->sin_addr), + sizeof (remotehost)); + + syslog(LOG_NOTICE, "LOGIN FAILURE FROM %s", remotehost); + syslog(LOG_AUTHPRIV|LOG_NOTICE, + "LOGIN FAILURE FROM %s, %s", remotehost, name); + + fprintf(stderr, "Login incorrect.\n"); + exit(1); +} + +void login_incorrect(char *name, struct sockaddr_in *sinp) +{ + char passwd[64]; + + printf("Password: "); fflush(stdout); + if (readline(passwd, sizeof passwd, 1) < 0) { + syslog(LOG_WARNING, "passwd read: %m"); + _exit(1); + } + badlogin(name, sinp); +} + +void doit(struct sockaddr_in *sinp) +{ + char user[64], passwd[64]; + char *xpasswd, *crypt(); + struct passwd *pw; + pid_t s; + + alarm(60); + printf("login: "); fflush(stdout); + if (readline(user, sizeof user, 0) < 0) { + syslog(LOG_WARNING, "login read: %m"); + _exit(1); + } + /* truncate username to 8 characters */ + user[8] = '\0'; + pw = getpwnam(user); + if (pw == NULL) + login_incorrect(user, sinp); + if (strcmp(pw->pw_shell, _PATH_UUCICO)) + login_incorrect(user, sinp); + if (pw->pw_expire && time(NULL) >= pw->pw_expire) + login_incorrect(user, sinp); + if (pw->pw_passwd && *pw->pw_passwd != '\0') { + printf("Password: "); fflush(stdout); + if (readline(passwd, sizeof passwd, 1) < 0) { + syslog(LOG_WARNING, "passwd read: %m"); + _exit(1); + } + xpasswd = crypt(passwd, pw->pw_passwd); + if (strcmp(xpasswd, pw->pw_passwd)) + badlogin(user, sinp); + } + alarm(0); + sprintf(Username, "USER=%s", pw->pw_name); + sprintf(Logname, "LOGNAME=%s", pw->pw_name); + if ((s = fork()) < 0) { + syslog(LOG_ERR, "fork: %m"); + _exit(1); + } else if (s == 0) { + dologin(pw, sinp); + setgid(pw->pw_gid); + initgroups(pw->pw_name, pw->pw_gid); + chdir(pw->pw_dir); + setuid(pw->pw_uid); + execl(pw->pw_shell, "uucico", NULL); + syslog(LOG_ERR, "execl: %m"); + _exit(1); + } +} + +int readline(char start[], int num, int passw) +{ + char c; + register char *p = start; + register int n = num; + + while (n-- > 0) { + if (read(0, &c, 1) <= 0) + return(-1); + c &= 0177; + if (c == '\n' || c == '\r' || c == '\0') { + if (p == start && passw) { + n++; + continue; + } + *p = '\0'; + return(0); + } + if (c == 025) { + n = num; + p = start; + continue; + } + *p++ = c; + } + return(-1); +} + +void dologout(void) +{ + union wait status; + pid_t pid; + char line[32]; + + while ((pid=wait((int *)&status)) > 0) { + sprintf(line, "uucp%ld", pid); + logwtmp(line, "", ""); + } +} + +/* + * Record login in wtmp file. + */ +void dologin(struct passwd *pw, struct sockaddr_in *sin) +{ + char line[32]; + char remotehost[32]; + int f; + time_t cur_time; + struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, + sizeof (struct in_addr), AF_INET); + + if (hp) { + strncpy(remotehost, hp->h_name, sizeof (remotehost)); + endhostent(); + } else + strncpy(remotehost, inet_ntoa(sin->sin_addr), + sizeof (remotehost)); + /* hack, but must be unique and no tty line */ + sprintf(line, "uucp%ld", getpid()); + time(&cur_time); + if ((f = open(_PATH_LASTLOG, O_RDWR)) >= 0) { + struct lastlog ll; + + ll.ll_time = cur_time; + lseek(f, (off_t)pw->pw_uid * sizeof(struct lastlog), L_SET); + SCPYN(ll.ll_line, line); + SCPYN(ll.ll_host, remotehost); + (void) write(f, (char *) &ll, sizeof ll); + (void) close(f); + } + logwtmp(line, pw->pw_name, remotehost); +} diff --git a/libexec/xtend/Makefile b/libexec/xtend/Makefile new file mode 100644 index 0000000..75fedef --- /dev/null +++ b/libexec/xtend/Makefile @@ -0,0 +1,13 @@ +# Makefile for xtend (Stark) 10/30/93 +# $Id$ + +BINMODE= 555 + +PROG= xtend +SRCS= xtend.c status.c packet.c user.c +CFLAGS+=-I. -DXTENUNAME=\"xten\" -DXTENGNAME=\"xten\" +LDADD+= -lutil + +MAN8= xtend.8 + +.include <bsd.prog.mk> diff --git a/libexec/xtend/packet.c b/libexec/xtend/packet.c new file mode 100644 index 0000000..8dfad88 --- /dev/null +++ b/libexec/xtend/packet.c @@ -0,0 +1,317 @@ +/*- + * Copyright (c) 1992, 1993, 1995 Eugene W. Stark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Eugene W. Stark. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <sys/time.h> +#include "xtend.h" +#include "xten.h" + +char *X10housenames[] = { + "A", "B", "C", "D", "E", "F", "G", "H", + "I", "J", "K", "L", "M", "N", "O", "P", + NULL +}; + +char *X10cmdnames[] = { + "1", "2", "3", "4", "5", "6", "7", "8", + "9", "10", "11", "12", "13", "14", "15", "16", + "AllUnitsOff", "AllLightsOn", "On", "Off", "Dim", "Bright", "AllLightsOff", + "ExtendedCode", "HailRequest", "HailAcknowledge", "PreSetDim0", "PreSetDim1", + "ExtendedData", "StatusOn", "StatusOff", "StatusRequest", + NULL +}; + +/* + * Log a packet and update device status accordingly + */ + +logpacket(p) +unsigned char *p; +{ + fprintf(Log, "%s: %s %s ", thedate(), + X10housenames[p[1]], X10cmdnames[p[2]]); + if(p[0] & TW_RCV_LOCAL) fprintf(Log, "(loc,"); + else fprintf(Log, "(rem,"); + if(p[0] & TW_RCV_ERROR) fprintf(Log, "err)"); + else fprintf(Log, " ok)"); + fprintf(Log, "\n"); +} + +/* + * Process a received packet p, updating device status information both + * in core and on disk. + */ + +processpacket(p) +unsigned char *p; +{ + int i, j, h, k; + STATUS *s; + + /* + * If the packet had the error flag set, there is no other useful info. + */ + if(p[0] & TW_RCV_ERROR) return; + /* + * First update in-core status information for the device. + */ + h = p[1]; k = p[2]; + if(k < 16) { /* We received a unit code, to select a particular device */ + s = &Status[h][k]; + s->selected = SELECTED; + s->lastchange = time(NULL); + s->changed = 1; + } else { /* We received a key code, to execute some function */ + /* + * Change in status depends on the key code received + */ + if(k == DIM) { + /* + * We can't really track DIM/BRIGHT properly the way things are right + * now. The TW523 reports the first, fourth, seventh, etc. Dim packet. + * We don't really have any way to tell when gaps occur, to cancel + * selection. For now, we'll assume that successive sequences of + * Dim/Bright commands are never transmitted without some other + * intervening command, and we make a good guess about how many units of + * dim/bright are represented by each packet actually reported by the + * TW523. + */ + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + switch(s->selected) { + case SELECTED: /* Selected, but not being dimmed or brightened */ + if(s->onoff == 0) { + s->onoff = 1; + s->brightness = 15; + } + s->brightness -= 2; + if(s->brightness < 0) s->brightness = 0; + s->selected = DIMMING; + s->lastchange = time(NULL); + s->changed = 1; + break; + case DIMMING: /* Selected and being dimmed */ + s->brightness -=3; + if(s->brightness < 0) s->brightness = 0; + s->lastchange = time(NULL); + s->changed = 1; + break; + case BRIGHTENING: /* Selected and being brightened (an error) */ + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + break; + default: + break; + } + } + } else if(k == BRIGHT) { + /* + * Same problem here... + */ + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + switch(s->selected) { + case SELECTED: /* Selected, but not being dimmed or brightened */ + if(s->onoff == 0) { + s->onoff = 1; + s->brightness = 15; + } + s->brightness += 2; + if(s->brightness > 15) s->brightness = 15; + s->selected = BRIGHTENING; + s->lastchange = time(NULL); + s->changed = 1; + break; + case DIMMING: /* Selected and being dimmed (an error) */ + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + break; + case BRIGHTENING: /* Selected and being brightened */ + s->brightness +=3; + if(s->brightness > 15) s->brightness = 15; + s->lastchange = time(NULL); + s->changed = 1; + break; + default: + break; + } + } + } else { /* Other key codes besides Bright and Dim */ + /* + * We cancel brightening and dimming on ALL units on ALL house codes, + * because the arrival of a different packet indicates a gap that + * terminates any prior sequence of brightening and dimming + */ + for(j = 0; j < 16; j++) { + for(i = 0; i < 16; i++) { + s = &Status[j][i]; + if(s->selected == BRIGHTENING || s->selected == DIMMING) { + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + } + switch(k) { + case ALLUNITSOFF: + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + s->onoff = 0; + s->selected = IDLE; + s->brightness = 0; + s->lastchange = time(NULL); + s->changed = 1; + } + break; + case ALLLIGHTSON: + /* Does AllLightsOn cancel selectedness of non-lights? */ + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->devcap & ISLIGHT) { + s->onoff = 1; + s->selected = IDLE; + s->brightness = 15; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case UNITON: + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected == SELECTED) { + s->onoff = 1; + s->selected = IDLE; + s->brightness = 15; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case UNITOFF: + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected == SELECTED) { + s->onoff = 0; + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case ALLLIGHTSOFF: + /* Does AllLightsOff cancel selectedness of non-lights? */ + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->devcap & ISLIGHT) { + s->onoff = 0; + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case EXTENDEDCODE: + break; + case HAILREQUEST: + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected == SELECTED) { + s->selected = HAILED; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case HAILACKNOWLEDGE: + /* Do these commands cancel selection of devices not affected? */ + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected == HAILED) { + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case PRESETDIM0: + case PRESETDIM1: + /* I don't really understand these */ + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected == SELECTED) { + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case EXTENDEDDATA: + /* Who knows? The TW523 can't receive these anyway. */ + break; + case STATUSON: + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected == REQUESTED) { + s->onoff = 1; + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + case STATUSOFF: + for(i = 0; i < 16; i++) { + if(s->selected == REQUESTED) { + s = &Status[h][i]; + s->onoff = 0; + s->selected = IDLE; + s->brightness = 0; + s->lastchange = time(NULL); + s->changed = 1; + } + } + case STATUSREQUEST: + for(i = 0; i < 16; i++) { + s = &Status[h][i]; + if(s->selected) { + s->selected = REQUESTED; + s->lastchange = time(NULL); + s->changed = 1; + } + } + break; + } + } + } +} diff --git a/libexec/xtend/paths.h b/libexec/xtend/paths.h new file mode 100644 index 0000000..77c6ae9 --- /dev/null +++ b/libexec/xtend/paths.h @@ -0,0 +1,11 @@ +/* + * Pathnames for files used by xtend + */ + +#define X10DIR "/var/spool/xten" +#define X10LOGNAME "Log" +#define X10STATNAME "Status" +#define X10DUMPNAME "status.out" +#define TWPATH "/dev/tw0" +#define SOCKPATH "/var/run/tw523" +#define PIDPATH "/var/run/xtend.pid" diff --git a/libexec/xtend/status.c b/libexec/xtend/status.c new file mode 100644 index 0000000..2ee3812 --- /dev/null +++ b/libexec/xtend/status.c @@ -0,0 +1,109 @@ +/*- + * Copyright (c) 1992, 1993, 1995 Eugene W. Stark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Eugene W. Stark. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <unistd.h> +#include <sys/param.h> +#include <sys/stat.h> +#include "xtend.h" +#include "xten.h" +#include "paths.h" + +/* + * Initialize the status table from the status files + */ + +initstatus() +{ + int h, i; + + if(lseek(status, 0, SEEK_SET) != 0) { + fprintf(Log, "%s: Seek error on status file\n", thedate()); + return; + } + if(read(status, Status, 16*16*sizeof(STATUS)) != 16*16*sizeof(STATUS)) { + fprintf(Log, "%s: Read error on status file\n", thedate()); + return; + } +} + +/* + * Checkpoint status of any devices whose status has changed + * and notify anyone monitoring those devices. + */ + +checkpoint_status() +{ + int h, i, k, offset; + + offset = 0; + for(h = 0; h < 16; h++) { + for(i = 0; i < 16; i++) { + if(Status[h][i].changed) { + if(lseek(status, offset, SEEK_SET) != offset) { + fprintf(Log, "%s: Seek error on status file\n", thedate()); + } else { + if(write(status, &Status[h][i], sizeof(STATUS)) != sizeof(STATUS)) { + fprintf(Log, "%s: Write error on status file\n", thedate()); + } + } + Status[h][i].changed = 0; + for(k = 0; k < MAXMON; k++) { + if(Monitor[k].inuse + && Monitor[k].house == h && Monitor[k].unit == i) { + /* + * Arrange to catch SIGPIPE in case client has gone away. + */ + extern int client; + extern void clientgone(); + void (*prev)(); + + client = k; + prev = signal(SIGPIPE, clientgone); + printstatus(Monitor[k].user, &Status[h][i]); + fflush(Monitor[k].user); + signal(SIGPIPE, prev); + } + } + } + offset += sizeof(STATUS); + } + } +} + +int client; + +void clientgone() +{ + fprintf(Log, "%s: Deleting monitor table entry %d, client gone\n", thedate(), client); + fclose(Monitor[client].user); + Monitor[client].inuse = 0; +} diff --git a/libexec/xtend/user.c b/libexec/xtend/user.c new file mode 100644 index 0000000..f029f5f --- /dev/null +++ b/libexec/xtend/user.c @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 1992, 1993, 1995 Eugene W. Stark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Eugene W. Stark. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <sys/param.h> +#include <sys/time.h> +#include "xtend.h" +#include "xten.h" +#include "paths.h" + +MONENTRY Monitor[MAXMON]; + +/* + * Process a user command + */ + +user_command() +{ + char h; + int i, k, c, n, error; + char cmd[512], dumppath[MAXPATHLEN+1], pkt[3]; + FILE *dumpf; + + error = 0; + if(fgets(cmd, 512, User) != NULL) { + if(sscanf(cmd, "status %c %d", &h, &i) == 2 + && h >= 'A' && h <= 'P' && i >= 1 && i <= 16) { + h -= 'A'; + i--; + printstatus(User, &Status[h][i]); + } else if(sscanf(cmd, "send %c %s %d", &h, cmd, &n) == 3 + && h >= 'A' && h <= 'P' && (i = find(cmd, X10cmdnames)) >= 0) { + h -= 'A'; + pkt[0] = h; + pkt[1] = i; + pkt[2] = n; + if(write(tw523, pkt, 3) != 3) { + fprintf(Log, "%s: Transmission error (packet [%s %s]:%d).\n", + thedate(), X10housenames[h], X10cmdnames[i], n); + error++; + } else { + fprintf(User, "OK\n"); + } + } else if(!strcmp("dump\n", cmd)) { + strcpy(dumppath, X10DIR); + strcat(dumppath, X10DUMPNAME); + if((dumpf = fopen(dumppath, "w")) != NULL) { + for(h = 0; h < 16; h++) { + for(i = 0; i < 16; i++) { + if(Status[h][i].lastchange) { + fprintf(dumpf, "%s%d\t", X10housenames[h], i+1); + printstatus(dumpf, &Status[h][i]); + } + } + } + fclose(dumpf); + fprintf(User, "OK\n"); + } else { + error++; + } + } else if(sscanf(cmd, "monitor %c %d", &h, &i) == 2 + && h >= 'A' && h <= 'P' && i >= 1 && i <= 16) { + h -= 'A'; + i--; + for(k = 0; k < MAXMON; k++) { + if(!Monitor[k].inuse) break; + } + if(k == MAXMON) { + error++; + } else { + Monitor[k].house = h; + Monitor[k].unit = i; + Monitor[k].user = User; + Monitor[k].inuse = 1; + fprintf(Log, "%s: Adding %c %d to monitor list (entry %d)\n", + thedate(), h+'A', i+1, k); + fprintf(User, "OK\n"); + fflush(User); + User = NULL; + return(0); /* We don't want caller to close stream */ + } + } else if(!strcmp("done\n", cmd)) { + fprintf(User, "OK\n"); + fflush(User); + return(1); + } else { + if(feof(User)) { + return(1); + } else { + error++; + } + } + } else { + error++; + } + if(error) { + fprintf(User, "ERROR\n"); + } + fflush(User); + return(0); +} + +find(s, tab) +char *s; +char *tab[]; +{ + int i; + + for(i = 0; tab[i] != NULL; i++) { + if(strcmp(s, tab[i]) == 0) return(i); + } + return(-1); +} + +printstatus(f, s) +FILE *f; +STATUS *s; +{ + fprintf(f, "%s:%d", s->onoff ? "On" : "Off", s->brightness); + switch(s->selected) { + case IDLE: + fprintf(f, " (normal) "); break; + case SELECTED: + fprintf(f, " (selected) "); break; + case DIMMING: + fprintf(f, " (dimming) "); break; + case BRIGHTENING: + fprintf(f, " (brightening) "); break; + case REQUESTED: + fprintf(f, " (requested) "); break; + case HAILED: + fprintf(f, " (hailed) "); break; + default: + fprintf(f, " (bogus) "); break; + } + fprintf(f, "%s", ctime(&s->lastchange)); +} + diff --git a/libexec/xtend/xten.h b/libexec/xtend/xten.h new file mode 100644 index 0000000..e782f15 --- /dev/null +++ b/libexec/xtend/xten.h @@ -0,0 +1,58 @@ +/*- + * Copyright (c) 1992, 1993, 1995 Eugene W. Stark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Eugene W. Stark. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern char *X10housenames[]; +extern char *X10cmdnames[]; + +#define ALLUNITSOFF 16 +#define ALLLIGHTSON 17 +#define UNITON 18 +#define UNITOFF 19 +#define DIM 20 +#define BRIGHT 21 +#define ALLLIGHTSOFF 22 +#define EXTENDEDCODE 23 +#define HAILREQUEST 24 +#define HAILACKNOWLEDGE 25 +#define PRESETDIM0 26 +#define PRESETDIM1 27 +#define EXTENDEDDATA 28 +#define STATUSON 29 +#define STATUSOFF 30 +#define STATUSREQUEST 31 + +/* + * Flags for first byte of received packet + */ + +#define TW_RCV_LOCAL 1 /* The packet arrived during a local transmission */ +#define TW_RCV_ERROR 2 /* An invalid/corrupted packet was received */ + diff --git a/libexec/xtend/xtend.8 b/libexec/xtend/xtend.8 new file mode 100644 index 0000000..e6ac50a --- /dev/null +++ b/libexec/xtend/xtend.8 @@ -0,0 +1,174 @@ +.\" Copyright (c) 1992, 1993 Eugene W. Stark +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Eugene W. Stark. +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Th XTEND 8 "30 Oct 1993" +.Dd Oct 30, 1993 +.Dt XTEND 8 +.Os BSD FreeBSD +.Sh NAME +xtend \- X-10 daemon +.Sh SYNOPSIS +.Nm xtend +.Sh DESCRIPTION +.Nm Xtend +interfaces between user-level programs and the TW523 X-10 controller. +It logs all packets received from the TW523, attempts to track the +status of all X-10 devices, and accepts socket connections from user-level +client programs that need to manipulate X-10 devices. +.Pp +When +.Nm xtend +is started, it forks, releases the controlling terminal, then opens +its log file, where it subsequently records all X-10 activity and +diagnostic messages. It then begins processing packets received from +the TW523 and accepting connections one at a time from clients +wishing to issue X-10 commands. The usual place to start xtend would +be from the +.Pa /etc/rc.local +startup script. +.Pp +Sending +.Nm xtend +a SIGHUP causes it to close and reopen its log file. This is useful +in shell scripts that rotate the log files to keep them from growing +indefinitely. +If +.Nm xtend +receives a SIGTERM, it shuts down gracefully and exits. +A SIGPIPE causes +.Nm xtend +to abort the current client connection. +.Pp +.Nm Xtend +communicates with client processes by a simple protocol in which a one-line +command is sent by the client, and is acknowledged by a one-line response +from the daemon. +.Pp +.Nm Xtend +understands four types of commands. The command +.Bl -tag +.It status H U +.El +.Pp +where H is a single letter house code, and U is a numeric unit code, +causes +.Nm xtend +to respond with one line of status information about the specified device. +The command +.Bl -tag +.It send H U N +.El +.Pp +where H is a single-letter house code, U is either a numeric unit code +or a function code (see source file +.Pa xtend/packet.c +) for a list, and N is a number indicating the number of times (usually 2) +the packet is to be transmitted without gaps, +causes +.Nm xtend +to perform the specified X-10 transmission. If the transmission was apparently +successful, a single-line response containing +.B +OK +is issued, otherwise a single-line response containing +.B +ERROR +is produced. +The command +.Bl -tag +.It dump +.El +.Pp +causes +.Nm xtend +to dump the current status of all devices to an ASCII file in the spool +directory. The response +.B +OK +is issued, regardless of whether the status dump was successful. +The command +.Bl -tag +.It monitor H U +.El +.Pp +causes +.Nm xtend +to add the current client socket connection to a list of clients that are to +be notified about activity concerning the specified X-10 device. +The single-line acknowledgement +.B +OK +is returned if the maximum (currently 5) number of such clients was not +exceeded, otherwise +.B +ERROR +is returned. +.Nm Xtend +then returns to its normal mode of accepting connections from clients. +However, each subsequent change in the status of the specified device will +cause +.Nm xtend +to write one line of status information for the device (in the same +format as produced by the +.B +status +command) to the saved socket. This feature is useful for writing programs +that need to monitor the activity of devices, like motion detectors, that can +perform X-10 transmissions. +.Sh OPTIONS +None. +.Sh SEE ALSO +.Xr xten 1 +.Xr tw 4 +.Sh FILES +.Bl -tag -width /var/spool/xten/Status -compact +.It Pa /dev/tw0 +the TW523 special file +.It Pa /var/run/tw523 +socket for client connections +.It Pa /var/run/xtend.pid +pid file +.It Pa /var/spool/xten/Log +log file +.It Pa /var/spool/xten/Status +device status file (binary) +.It Pa /var/spool/status.out +ASCII dump of device status +.El +.Sh BUGS +There is currently no timeout on client socket connections, so a hung +client program can prevent other clients from accessing the daemon. +.Pp +.Nm Xtend +does the best it can at trying to track device status, but there is +usually no way it can tell when a device has been operated manually. +This is due to the fact that most X-10 devices are not able to +respond to queries about their status. +.Sh AUTHOR +Eugene W. Stark (stark@cs.sunysb.edu) diff --git a/libexec/xtend/xtend.c b/libexec/xtend/xtend.c new file mode 100644 index 0000000..609bc7f --- /dev/null +++ b/libexec/xtend/xtend.c @@ -0,0 +1,368 @@ +/*- + * Copyright (c) 1992, 1993, 1995 Eugene W. Stark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Eugene W. Stark. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * xtend - X-10 daemon + * Eugene W. Stark (stark@cs.sunysb.edu) + * January 14, 1993 + */ + +#include <stdio.h> +#include <time.h> +#include <setjmp.h> +#include <signal.h> +#include <pwd.h> +#include <grp.h> +#include <sys/param.h> +#include <sys/file.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include "xtend.h" +#include "xten.h" +#include "paths.h" + +FILE *Log; /* Log file */ +FILE *User; /* User connection */ +STATUS Status[16][16]; /* Device status table */ +int status; /* Status file descriptor */ +int tw523; /* tw523 controller */ +int sock; /* socket for user */ +jmp_buf mainloop; /* longjmp point after SIGHUP */ +void onhup(); /* SIGHUP handler */ +void onterm(); /* SIGTERM handler */ +void onpipe(); /* SIGPIPE handler */ + +main(argc, argv) +int argc; +char *argv[]; +{ + char *twpath = TWPATH; + char *sockpath = SOCKPATH; + char logpath[MAXPATHLEN+1]; + char statpath[MAXPATHLEN+1]; + struct sockaddr_un sa; + struct timeval tv; + struct passwd *pw; + struct group *gr; + struct stat sb; + int user; + int fd; + FILE *pidf; + + /* + * Make sure we start out running as root + */ + if(geteuid() != 0) { + fprintf(stderr, "You must be root to run %s\n", argv[0]); + exit(1); + } + + /* + * Find out what UID/GID we are to run as + */ + if((pw = getpwnam(XTENUNAME)) == NULL) { + fprintf(stderr, "%s: No such user '%s'\n", argv[0], XTENUNAME); + exit(1); + } + if((gr = getgrnam(XTENGNAME)) == NULL) { + fprintf(stderr, "%s: No such group '%s'\n", argv[0], XTENGNAME); + exit(1); + } + + /* + * Open the log file before doing anything else + */ + strcpy(logpath, X10DIR); + if(stat(logpath, &sb) == -1 && errno == ENOENT) { + if(mkdir(logpath, 0755) != -1) { + chown(logpath, pw->pw_uid, gr->gr_gid); + } else { + fprintf(stderr, "%s: Can't create directory '%s'\n", argv[0], logpath); + exit(1); + } + } + strcat(logpath, "/"); + strcat(logpath, X10LOGNAME); + if((Log = fopen(logpath, "a")) == NULL) { + fprintf(stderr, "Can't open log file %s\n", logpath); + exit(1); + } + chown(logpath, pw->pw_uid, gr->gr_gid); + + /* + * Become a daemon + */ + if(daemon(0, 0) == -1) { + fprintf(Log, "%s: Unable to become a daemon\n", thedate()); + fclose(Log); + exit(1); + } + fprintf(Log, "%s: %s [%d] started\n", thedate(), argv[0], getpid()); + + /* + * Get ahold of the TW523 device + */ + if((tw523 = open(twpath, O_RDWR)) < 0) { + fprintf(Log, "%s: Can't open %s\n", thedate(), twpath); + fclose(Log); + exit(1); + } + fprintf(Log, "%s: %s successfully opened\n", thedate(), twpath); + + /* + * Put our pid in a file so we can be signalled by shell scripts + */ + if((pidf = fopen(PIDPATH, "w")) == NULL) { + fprintf(Log, "%s: Error writing pid file: %s\n", thedate(), PIDPATH); + fclose(Log); + exit(1); + } + fprintf(pidf, "%d\n", getpid()); + fclose(pidf); + + /* + * Set up socket to accept user commands + */ + if((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + fprintf(Log, "%s: Can't create socket\n", thedate()); + fclose(Log); + exit(1); + } + strcpy(sa.sun_path, sockpath); + sa.sun_family = AF_UNIX; + unlink(sockpath); + if(bind(sock, (struct sockaddr *)(&sa), strlen(sa.sun_path) + 2) < 0) { + fprintf(Log, "%s: Can't bind socket to %s\n", thedate(), sockpath); + fclose(Log); + exit(1); + } + if(listen(sock, 5) < 0) { + fprintf(Log, "%s: Can't listen on socket\n", thedate()); + fclose(Log); + exit(1); + } + + /* + * Set proper ownership and permissions on the socket + */ + if(chown(sockpath, pw->pw_uid, gr->gr_gid) == -1 || + chmod(sockpath, 0660) == -1) { + fprintf(Log, "%s: Can't set owner/permissions on socket\n", thedate()); + fclose(Log); + exit(1); + } + + /* + * Give up root privileges + */ + setgid(pw->pw_gid); + setuid(pw->pw_uid); + + /* + * Initialize the status table + */ + strcpy(statpath, X10DIR); + strcat(statpath, "/"); + strcat(statpath, X10STATNAME); + if((status = open(statpath, O_RDWR)) < 0) { + if((status = open(statpath, O_RDWR | O_CREAT, 0666)) < 0) { + fprintf(Log, "%s: Can't open %s\n", thedate(), statpath); + fclose(Log); + exit(1); + } + if(write(status, Status, 16 * 16 * sizeof(STATUS)) + != 16 * 16 * sizeof(STATUS)) { + fprintf(Log, "%s: Error initializing status file\n", thedate()); + fclose(Log); + exit(1); + } + } + initstatus(); + + /* + * Return here on SIGHUP after closing and reopening log file. + * Also on SIGPIPE after closing user connection. + */ + signal(SIGTERM, onterm); + signal(SIGHUP, onhup); + signal(SIGPIPE, onpipe); + setjmp(mainloop); + + /* + * Now start the main processing loop. + */ + tv.tv_sec = 0; + tv.tv_usec = 250000; + while(1) { + fd_set fs; + unsigned char rpkt[3]; + int sel, h, k; + STATUS *s; + + FD_ZERO(&fs); + FD_SET(tw523, &fs); + if(User != NULL) FD_SET(user, &fs); + else FD_SET(sock, &fs); + sel = select(FD_SETSIZE, &fs, 0, 0, &tv); + if(sel == 0) { + /* + * Cancel brightening and dimming on ALL units on ALL house codes, + * because the fact that we haven't gotten a packet for awhile means + * that there was a gap in transmission. + */ + for(h = 0; h < 16; h++) { + for(k = 0; k < 16; k++) { + s = &Status[h][k]; + if(s->selected == BRIGHTENING || s->selected == DIMMING) { + s->selected = IDLE; + s->lastchange = time(NULL); + s->changed = 1; + } + } + } + fflush(Log); + checkpoint_status(); + /* + * Now that we've done this stuff, we'll set the timeout a little + * longer, so we don't keep looping too frequently. + */ + tv.tv_sec = 60; + tv.tv_usec = 0; + continue; + } + /* + * While there is stuff happening, we keep a short timeout, so we + * don't get stuck for some unknown reason, and so we can keep the + * brightening and dimming data up-to-date. + */ + tv.tv_sec = 0; + tv.tv_usec = 250000; + if(FD_ISSET(tw523, &fs)) { /* X10 data arriving from TW523 */ + if(read(tw523, rpkt, 3) < 3) { + fprintf(Log, "%s: Error reading from TW523\n", thedate()); + } else { + logpacket(rpkt); + processpacket(rpkt); + } + } else if(FD_ISSET(user, &fs)) { + if(User != NULL) { + if(user_command()) { + fprintf(Log, "%s: Closing user connection\n", thedate()); + fclose(User); + User = NULL; + } + } else { + /* "Can't" happen */ + } + } else if(FD_ISSET(sock, &fs)) { /* Accept a connection */ + if (User == NULL) { + int len = sizeof(struct sockaddr_un); + if((user = accept(sock, (struct sockaddr *)(&sa), &len)) >= 0) { + fprintf(Log, "%s: Accepting user connection\n", thedate()); + if((User = fdopen(user, "w+")) == NULL) { + fprintf(Log, "%s: Can't attach socket to stream\n", thedate()); + } + } else { + fprintf(Log, "%s: Failure in attempt to accept connection\n", thedate()); + } + } else { + /* "Can't happen */ + } + } + } + /* Not reached */ +} + +char *thedate() +{ + char *cp, *cp1; + time_t tod; + + tod = time(NULL); + cp = cp1 = ctime(&tod); + while(*cp1 != '\n') cp1++; + *cp1 = '\0'; + return(cp); +} + +/* + * When SIGHUP received, close and reopen the Log file + */ + +void onhup() +{ + char logpath[MAXPATHLEN+1]; + + fprintf(Log, "%s: SIGHUP received, reopening Log\n", thedate()); + fclose(Log); + strcpy(logpath, X10DIR); + strcat(logpath, "/"); + strcat(logpath, X10LOGNAME); + if((Log = fopen(logpath, "a")) == NULL) { + fprintf(stderr, "Can't open log file %s\n", logpath); + exit(1); + } + longjmp(mainloop, 1); + /* No return */ +} + +/* + * When SIGTERM received, just exit normally + */ + +void onterm() +{ + fprintf(Log, "%s: SIGTERM received, shutting down\n", thedate()); + fclose(Log); + exit(0); +} + +/* + * When SIGPIPE received, reset user connection + */ + +void onpipe() +{ + fprintf(Log, "%s: SIGPIPE received, resetting user connection\n", + thedate()); + if(User != NULL) { + fclose(User); + User = NULL; + } + longjmp(mainloop, 1); + /* No return */ +} diff --git a/libexec/xtend/xtend.h b/libexec/xtend/xtend.h new file mode 100644 index 0000000..1b8c706 --- /dev/null +++ b/libexec/xtend/xtend.h @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 1992, 1993, 1995 Eugene W. Stark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Eugene W. Stark. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Device capabilities + */ + +#define ISLIGHT 1 /* Is device a light? */ +#define CANQUERY 2 /* Responds to status query */ + +/* + * Device status + */ + +typedef enum { + IDLE, + SELECTED, + DIMMING, + BRIGHTENING, + REQUESTED, + HAILED + } SELECT; + +typedef struct { + unsigned int devcap; /* device capabilities */ + unsigned int changed; /* status changed since last checkpoint? */ + time_t lastchange; /* time status last changed */ + SELECT selected; /* select status of device */ + unsigned int onoff; /* nonzero if on */ + unsigned int brightness; /* value in range 0-15 */ +} STATUS; + +typedef struct { + int inuse; /* Is entry in use? */ + FILE *user; /* Socket to notify user */ + int house; /* House code of device to monitor */ + int unit; /* Unit code of device to monitor */ +} MONENTRY; + +#define MAXMON 5 /* Maximum number of monitor entries */ + +extern FILE *Log; /* Log file */ +extern FILE *User; /* User connection */ +extern STATUS Status[16][16]; /* Device status table */ +extern int status; /* Status file descriptor */ +extern int tw523; /* tw523 controller */ +extern MONENTRY Monitor[MAXMON];/* Monitor table */ + +extern char *thedate(); diff --git a/libexec/ypxfr/Makefile b/libexec/ypxfr/Makefile new file mode 100644 index 0000000..0339aee --- /dev/null +++ b/libexec/ypxfr/Makefile @@ -0,0 +1,26 @@ +# $Id: Makefile,v 1.6 1995/12/16 04:03:02 wpaul Exp $ + +PROG= ypxfr +SRCS= ypxfr_clnt.c yp_clnt.c ypxfr_getmap.c yp_dblookup.c \ + yp_error.c ypxfr_misc.c ypxfr_main.c yp_dbwrite.c + +.PATH: ${.CURDIR}/../../usr.sbin/ypserv + +MAN8= ypxfr.8 +CFLAGS+=-I. + +CLEANFILES= yp.h yp_clnt.c ypxfr_clnt.c + +RPCSRC= ${.DESTDIR}/usr/include/rpcsvc/yp.x +RPCGEN= rpcgen -I -C + +ypxfr_clnt.c: ${RPCSRC} yp.h + ${RPCGEN} -DYPPUSH_ONLY -l -o ${.TARGET} ${RPCSRC} + +yp_clnt.c: ${RPCSRC} yp.h + ${RPCGEN} -DYPSERV_ONLY -l -o ${.TARGET} ${RPCSRC} + +yp.h: ${RPCSRC} + ${RPCGEN} -h -o ${.TARGET} ${RPCSRC} + +.include <bsd.prog.mk> diff --git a/libexec/ypxfr/yp_dbwrite.c b/libexec/ypxfr/yp_dbwrite.c new file mode 100644 index 0000000..f874e9b --- /dev/null +++ b/libexec/ypxfr/yp_dbwrite.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: yp_dbwrite.c,v 1.7 1995/12/24 04:40:58 wpaul Exp $ + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <limits.h> +#include <unistd.h> +#include <db.h> +#include <sys/stat.h> +#include <errno.h> +#include <paths.h> +#include "yp.h" +#include "ypxfr_extern.h" + +#ifndef lint +static const char rcsid[] = "$Id: yp_dbwrite.c,v 1.7 1995/12/24 04:40:58 wpaul Exp $"; +#endif + +#define PERM_SECURE (S_IRUSR|S_IWUSR) + +/* + * Open a DB database read/write + */ +DB *yp_open_db_rw(domain, map) + const char *domain; + const char *map; +{ + DB *dbp; + char buf[1025]; + + + yp_errno = YP_TRUE; + + if (map[0] == '.' || strchr(map, '/')) { + yp_errno = YP_BADARGS; + return (NULL); + } + + snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map); + + dbp = dbopen(buf,O_RDWR|O_EXCL|O_CREAT, PERM_SECURE, DB_HASH, &openinfo); + + if (dbp == NULL) { + switch(errno) { + case ENOENT: + yp_errno = YP_NOMAP; + break; + case EFTYPE: + yp_errno = YP_BADDB; + break; + default: + yp_errno = YP_YPERR; + break; + } + } + + return (dbp); +} + +int yp_put_record(dbp,key,data) + DB *dbp; + DBT *key; + DBT *data; +{ + + if ((dbp->put)(dbp,key,data,0)) { + (void)(dbp->close)(dbp); + return(YP_BADDB); + } + + return(YP_TRUE); +} diff --git a/libexec/ypxfr/ypxfr.8 b/libexec/ypxfr/ypxfr.8 new file mode 100644 index 0000000..5e41b08 --- /dev/null +++ b/libexec/ypxfr/ypxfr.8 @@ -0,0 +1,207 @@ +.\" Copyright (c) 1995 +.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Bill Paul. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: ypxfr.8,v 1.2 1995/12/25 02:54:39 wpaul Exp $ +.\" +.Dd February 5, 1995 +.Dt YPXFR 8 +.Os +.Sh NAME +.Nm ypxfr +.Nd "transfer NIS database from remote server to local host" +.Sh SYNOPSIS +.Nm ypxfr +.Op Fl f +.Op Fl c +.Op Fl d Ar target domain +.Op Fl h Ar source host +.Op Fl s Ar source domain +.Op Fl p Ar path +.Op Fl C Ar taskid program-number ipaddr port +.Ar mapname +.Sh DESCRIPTION +.Nm ypxfr +copies an NIS database (or +.Pa map ) +from one NIS server to another using NIS services. In FreeBSD, +.Nm ypxfr +is generally invoked by +.Xr ypserv 8 +when it receives a map transfer request from +.Xr yppush 8 . +.Nm ypxfr +is used primarily in environments where several NIS servers +are in use in a single domain. One server, the NIS master, maintains +the canonical copies of all NIS maps, and all the other servers, +the NIS slaves, copy new versions of the maps from the master whenever +any updates are made (i.e. when a user updates their password via +.Xr yppasswd 1 +). +.Pp +When run, +.Nm ypxfr +creates a temporary database file in +.Pa /var/yp/[domainmame] , +and fills it with the contents of +.Ar mapname +as supplied by the specified +.Ar source host . +When the entire map has been transfered, +.Nm ypxfr +deletes the original copy of +.Ar mapname +and moves the temporary copy into its place. When the transfer is +complete, +.Nm ypxfr +will attempt to send a 'clear current map' request to the local +.Xr ypserv 8 +process to clear any possible references it may still have to the +stale map. +.Pp +Note that all files created by +.Nm ypxfr +are owner readable and writable only for security reasons. Since the +NIS maps and the directory in which they reside are normally owned by +root, this prevents non-privleged users from making unauthorized +modifications. +.Pp +In order to maintain consistency across all NIS servers, +.Nm ypxfr +can be run periodically in a +.Xr cron 8 +job. Maps which change infrequently +need only be updated once a day (preferably late at night when system +usage is lowest), whereas those that are subject to frequent changes +(such a +.Pa passwd.byname +and +.Pa passwd.byuid ) +should be updated perhaps once every hour. Using +.Xr cron 8 +to automatically +update the NIS maps is not strictly mandatory since all updates should +be propagated by +.Xr yppush 8 +when +.Pa /var/yp/Makefile +is run on the NIS master server, however it is good practice +on large networks where possible outages could cause NIS servers to +fall out of sync with each other. +.Pp +When +.Nm ypxfr +is invoked without a controlling terminal, e.g. from inside +.Xr ypserv 8 , +it logs all its output using the +.Xr syslog 3 +facility. +.Sh OPTIONS +The following options and flags are supported by +.Nm ypxfr : +.Bl -tag -width flag +.It Fl f +Force a map transfer. Normally, +.Nm ypxfr +will not transfer a map if it determines that the NIS master's copy +is not newer than the existing copy already on the local host: the +.Fl f +flag forces a transfer regardless of which server's version is more recent. +.It Fl c +Do not send a 'clear current map' request to the +.Xr ypserv 8 +process running on the local host. This flag is normally used when +invoking +.Nm ypxfr +manually on a machine that is not yet running +.Xr ypserv 8 . +Without this flag, failure to contact the local NIS server will cause +.Nm ypxfr +to abort the transfer. +.It Fl d Ar target domain +Specify a target domain other than the current NIS domain. +.It Fl h Ar source host +Specify the name of the host from which to copy the NIS maps. This option +is used to insure that +.Nm ypxfr +only copies maps from the NIS master server. +.It Fl s Ar source domain +Specify the domain from which to transfer a map, in the event that +the transfer is being done across two different NIS domains. +.It Fl p Ar path +Specify the top level directory containing the NIS maps. By +default, this path is +.Pa /var/yp . +The +.Fl p +flag allows you to specify an alternate path should you wish to +store your NIS maps in a different part of the filesystem. The +NIS server, +.Xr ypserv 8 , +passes this flag to +.Nm ypxfr +if it too has been told to use an alternate path. +.It Fl C Ar taskid program-number ipaddr port +These options are used only when +.Nm ypxfr +is invoked by +.Xr ypserv 8 +in response to a map transfer request initiated by +.Xr yppush 8 . +In this instance, +.Nm ypxfr +needs to 'callback' to the +.Xr yppush 8 +process and interact with it, so +.Xr yppush 8 +passes to it an IP address +.Ar ipaddr , +port number +.Ar port , +registered program number +.Ar program-number +and a transaction ID +.Ar taskid +that it can use to contact the waiting +.Xr yppush 8 +process on the master server. +.It Ar mapname +The name of the map to transfer. +.El +.Sh FILES +.Bl -tag -width Pa -compact +.It Pa /var/yp/[domainname]/[maps] +The NIS maps for a particular NIS domain. +.El +.Sh SEE ALSO +.Xr ypserv 8 , +.Xr yppush 8 , +.Xr yp 4 +.Sh AUTHOR +Bill Paul <wpaul@ctr.columbia.edu> diff --git a/libexec/ypxfr/ypxfr_extern.h b/libexec/ypxfr/ypxfr_extern.h new file mode 100644 index 0000000..b2eda88 --- /dev/null +++ b/libexec/ypxfr/ypxfr_extern.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ypxfr_extern.h,v 1.5 1995/12/24 04:37:22 wpaul Exp $ + */ +#include <sys/types.h> +#include <limits.h> +#include <db.h> +#include <paths.h> + +#ifndef _PATH_YP +#define _PATH_YP "/var/yp/" +#endif + +extern char *yp_dir; +extern int debug; +extern HASHINFO openinfo; +extern int yp_errno; +extern void yp_error __P(( const char *, ... )); +extern int _yp_check __P(( char ** )); +extern char *ypxfrerr_string __P(( ypxfrstat )); +extern DB *yp_open_db_rw __P(( const char *, const char *)); +extern int yp_put_record __P(( DB *, DBT *, DBT * )); +extern int yp_get_record __P(( const char *, const char *, const DBT *, DBT *, int )); +extern int ypxfr_get_map __P(( char *, char *, char *, int (*)() )); +extern char *ypxfr_get_master __P(( char *, char *, char *, const int )); +extern unsigned long ypxfr_get_order __P(( char *, char *, char *, const int )); +extern char *ypxfxerr_string __P(( ypxfrstat )); +extern int callrpc __P(( char *, int, int, int, xdrproc_t, char *, xdrproc_t, char *)); diff --git a/libexec/ypxfr/ypxfr_getmap.c b/libexec/ypxfr/ypxfr_getmap.c new file mode 100644 index 0000000..702b5cb --- /dev/null +++ b/libexec/ypxfr/ypxfr_getmap.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ypxfr_getmap.c,v 1.6 1995/12/24 04:39:04 wpaul Exp $ + */ +#include <stdio.h> +#include <sys/types.h> +#include <time.h> +#include <rpc/rpc.h> +#include <rpc/xdr.h> +#include <rpcsvc/yp.h> +#include "ypxfr_extern.h" + +#ifndef lint +static const char rcsid[] = "$Id: ypxfr_getmap.c,v 1.6 1995/12/24 04:39:04 wpaul Exp $"; +#endif + +extern bool_t xdr_ypresp_all_seq __P(( XDR *, unsigned long * )); + +int (*ypresp_allfn)(); +void *ypresp_data; +extern DB *specdbp; +extern int yp_errno; + +/* + * This is largely the same as yp_all() except we do the transfer + * from a specific server without the aid of ypbind(8). We need to + * be able to specify the source host explicitly since ypxfr may + * only transfer maps from the NIS master server for any given domain. + * However, if we use the libc version of yp_all(), we could end up + * talking to one of the slaves instead. We do need to dig into libc + * a little though, since it contains the magic XDR function we need. + */ +int ypxfr_get_map(map, domain, host, callback) + char *map; + char *domain; + char *host; + int (*callback)(); +{ + CLIENT *clnt; + ypreq_nokey req; + unsigned long status; + struct timeval timeout; + + timeout.tv_usec = 0; + timeout.tv_sec = 10; + + /* YPPROC_ALL is a TCP service */ + if ((clnt = clnt_create(host, YPPROG, YPVERS, "tcp")) == NULL) { + yp_error("%s", clnt_spcreateerror("failed to tcp handle")); + yp_errno = YPXFR_YPERR; + return(1); + } + + req.domain = domain; + req.map = map; + ypresp_allfn = callback; + ypresp_data = NULL; + + (void)clnt_call(clnt, YPPROC_ALL, xdr_ypreq_nokey, &req, + xdr_ypresp_all_seq, &status, timeout); + + clnt_destroy(clnt); + + if (status != YP_TRUE) { + yp_errno = YPXFR_YPERR; + return(1); + } + + return(0); +} diff --git a/libexec/ypxfr/ypxfr_main.c b/libexec/ypxfr/ypxfr_main.c new file mode 100644 index 0000000..e9bddb4 --- /dev/null +++ b/libexec/ypxfr/ypxfr_main.c @@ -0,0 +1,490 @@ +/* + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ypxfr_main.c,v 1.11 1995/12/25 02:53:33 wpaul Exp $ + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <syslog.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <rpc/rpc.h> +#include <rpcsvc/yp.h> +struct dom_binding {}; +#include <rpcsvc/ypclnt.h> +#include "ypxfr_extern.h" + +#ifndef lint +static const char rcsid[] = "$Id: ypxfr_main.c,v 1.11 1995/12/25 02:53:33 wpaul Exp $"; +#endif + +char *progname = "ypxfr"; +char *yp_dir = _PATH_YP; +int _rpcpmstart = 0; +int ypxfr_use_yplib = 0; /* Assume the worst. */ +int ypxfr_clear = 1; +int ypxfr_prognum = 0; +struct sockaddr_in ypxfr_callback_addr; +struct yppushresp_xfr ypxfr_resp; +DB *dbp; + +static void ypxfr_exit(retval, temp) + ypxfrstat retval; + char *temp; +{ + CLIENT *clnt; + int sock = RPC_ANYSOCK; + struct timeval timeout; + + /* Clean up no matter what happened previously. */ + if (temp != NULL) { + (void)(dbp->close)(dbp); + if (unlink(temp) == -1) { + yp_error("failed to unlink %s",strerror(errno)); + } + } + + if (_rpcpmstart) { + timeout.tv_sec = 20; + timeout.tv_usec = 0; + + if ((clnt = clntudp_create(&ypxfr_callback_addr, ypxfr_prognum, + 1, timeout, &sock)) == NULL) { + yp_error("%s", clnt_spcreateerror("failed to establish callback handle")); + exit(1); + } + + ypxfr_resp.status = retval; + + if (yppushproc_xfrresp_1(&ypxfr_resp, clnt) == NULL) { + yp_error("%s", clnt_sperror(clnt, "callback failed")); + clnt_destroy(clnt); + exit(1); + } + clnt_destroy(clnt); + } else { + yp_error("Exiting: %s", ypxfrerr_string(retval)); + } + + exit(0); +} + +static void usage() +{ + if (_rpcpmstart) { + ypxfr_exit(YPXFR_BADARGS,NULL); + } else { + fprintf(stderr,"usage: %s [-f] [-c] [-d target domain] \ +[-h source host] [-s source domain]\n", progname); + fprintf(stderr,"\t [-p path] [-C taskid program-number \ +ipaddr port] mapname\n"); + exit(1); + } +} + +int ypxfr_foreach(status, key, keylen, val, vallen, data) + int status; + char *key; + int keylen; + char *val; + int vallen; + char *data; +{ + DBT dbkey, dbval; + + if (status != YP_TRUE) + return (status); + + dbkey.data = key; + dbkey.size = keylen; + dbval.data = val; + dbval.size = vallen; + + if (yp_put_record(dbp, &dbkey, &dbval) != YP_TRUE) + return(yp_errno); + + return (0); +} + +main(argc,argv) + int argc; + char *argv[]; +{ + int ch; + int ypxfr_force = 0; + char *ypxfr_dest_domain = NULL; + char *ypxfr_source_host = NULL; + char *ypxfr_source_domain = NULL; + char *ypxfr_local_domain = NULL; + char *ypxfr_master = NULL; + unsigned long ypxfr_order = -1, ypxfr_skew_check = -1; + char *ypxfr_mapname = NULL; + int ypxfr_args = 0; + char ypxfr_temp_map[MAXPATHLEN + 2]; + char tempmap[MAXPATHLEN + 2]; + char buf[MAXPATHLEN + 2]; + DBT key, data; + + debug = 1; + + if (!isatty(fileno(stderr))) { + openlog(progname, LOG_PID, LOG_DAEMON); + _rpcpmstart = 1; + } + + if (argc < 2) + usage(); + + while ((ch = getopt(argc, argv, "fcd:h:s:p:C:")) != EOF) { + int my_optind; + switch(ch) { + case 'f': + ypxfr_force++; + ypxfr_args++; + break; + case 'c': + ypxfr_clear = 0; + ypxfr_args++; + break; + case 'd': + ypxfr_dest_domain = optarg; + ypxfr_args += 2; + break; + case 'h': + ypxfr_source_host = optarg; + ypxfr_args += 2; + break; + case 's': + ypxfr_source_domain = optarg; + ypxfr_args += 2; + break; + case 'p': + yp_dir = optarg; + ypxfr_args += 2; + break; + case 'C': + /* + * Whoever decided that the -C flag should take + * four arguments is a twit. + */ + my_optind = optind - 1; + if (argv[my_optind] == NULL || !strlen(argv[my_optind])) { + yp_error("transaction ID not specified"); + usage(); + } + ypxfr_resp.transid = atol(argv[my_optind]); + my_optind++; + if (argv[my_optind] == NULL || !strlen(argv[my_optind])) { + yp_error("RPC program number not specified"); + usage(); + } + ypxfr_prognum = atol(argv[my_optind]); + my_optind++; + if (argv[my_optind] == NULL || !strlen(argv[my_optind])) { + yp_error("address not specified"); + usage(); + } + if (!inet_aton(argv[my_optind], &ypxfr_callback_addr.sin_addr)) { + yp_error("failed to convert '%s' to IP addr", + argv[my_optind]); + exit(1); + } + my_optind++; + if (argv[my_optind] == NULL || !strlen(argv[my_optind])) { + yp_error("port not specified"); + usage(); + } + ypxfr_callback_addr.sin_port = htons((u_short)atoi(argv[my_optind])); + ypxfr_args += 5; + break; + default: + usage(); + break; + } + } + + ypxfr_mapname = argv[ypxfr_args + 1]; + + if (ypxfr_mapname == NULL) { + yp_error("no map name specified"); + usage(); + } + + /* Always the case. */ + ypxfr_callback_addr.sin_family = AF_INET; + + /* Determine if local NIS client facilities are turned on. */ + if (!yp_get_default_domain(&ypxfr_local_domain) && + _yp_check(&ypxfr_local_domain)) + ypxfr_use_yplib = 1; + + /* + * If no destination domain is specified, assume that the + * local default domain is to be used and try to obtain it. + * Fails if NIS client facilities are turned off. + */ + if (ypxfr_dest_domain == NULL) { + if (ypxfr_use_yplib) { + yp_get_default_domain(&ypxfr_dest_domain); + } else { + yp_error("no destination domain specified and \ +the local domain name isn't set"); + ypxfr_exit(YPXFR_BADARGS,NULL); + } + } + + /* + * If a source domain is not specified, assume it to + * be the same as the destination domain. + */ + if (ypxfr_source_domain == NULL) { + ypxfr_source_domain = ypxfr_dest_domain; + } + + /* + * If the source host is not specified, assume it to be the + * master for the specified map. If local NIS client facilities + * are turned on, we can figure this out using yp_master(). + * If not, we have to see if a local copy of the map exists + * and extract its YP_MASTER_NAME record. If _that_ fails, + * we are stuck and must ask the user for more information. + */ + if (ypxfr_source_host == NULL) { + if (!ypxfr_use_yplib) { + /* + * Double whammy: NIS isn't turned on and the user + * didn't specify a source host. + */ + char *dptr; + key.data = "YP_MASTER_NAME"; + key.size = sizeof("YP_MASTER_NAME") - 1; + + if (yp_get_record(ypxfr_dest_domain, ypxfr_mapname, + &key, &data, 1) != YP_TRUE) { + yp_error("no source host specified"); + ypxfr_exit(YPXFR_BADARGS,NULL); + } + dptr = data.data; + dptr[data.size] = '\0'; + ypxfr_master = ypxfr_source_host = strdup(dptr); + } + } else { + if (ypxfr_use_yplib) + ypxfr_use_yplib = 0; + } + + if (ypxfr_master == NULL) { + if ((ypxfr_master = ypxfr_get_master(ypxfr_source_domain, + ypxfr_mapname, + ypxfr_source_host, + ypxfr_use_yplib)) == NULL) { + yp_error("failed to find master of %s in domain %s", + ypxfr_mapname, ypxfr_source_domain); + ypxfr_exit(YPXFR_MADDR,NULL); + } + } + + /* + * If we got here and ypxfr_source_host is still undefined, + * it means we had to resort to using yp_master() to find the + * master server for the map. The source host and master should + * be identical. + */ + if (ypxfr_source_host == NULL) + ypxfr_source_host = ypxfr_master; + + if ((ypxfr_order = ypxfr_get_order(ypxfr_source_domain, + ypxfr_mapname, + ypxfr_master, 0)) == 0) { + yp_error("failed to get order number of %s", + ypxfr_mapname); + ypxfr_exit(YPXFR_YPERR,NULL); + } + + key.data = "YP_LAST_MODIFIED"; + key.size = sizeof("YP_LAST_MODIFIED") - 1; + + /* The order number is immaterial when the 'force' flag is set. */ + + if (!ypxfr_force) { + int ignore = 0; + if (yp_get_record(ypxfr_dest_domain,ypxfr_mapname,&key,&data,1) != YP_TRUE) { + switch(yp_errno) { + case YP_NOKEY: + ypxfr_exit(YPXFR_FORCE,NULL); + break; + case YP_NOMAP: + /* + * If the map doesn't exist, we're + * creating it. Ignore the error. + */ + ignore++; + break; + case YP_BADDB: + default: + ypxfr_exit(YPXFR_DBM,NULL); + break; + } + } + if (!ignore && ypxfr_order <= atoi(data.data)) + ypxfr_exit(YPXFR_AGE, NULL); + + } + + /* Construct a temporary map file name */ + snprintf(tempmap, sizeof(tempmap), "%s.%d",ypxfr_mapname, getpid()); + snprintf(ypxfr_temp_map, sizeof(ypxfr_temp_map), "%s/%s/%s", yp_dir, + ypxfr_dest_domain, tempmap); + + /* Open the temporary map read/write. */ + if ((dbp = yp_open_db_rw(ypxfr_dest_domain, tempmap)) == NULL) { + yp_error("failed to open temporary map file"); + ypxfr_exit(YPXFR_DBM,NULL); + } + + /* + * Fill in the keys we already know, such as the order number, + * master name, input file name (we actually make up a bogus + * name for that) and output file name. + */ + snprintf(buf, sizeof(buf), "%d", ypxfr_order); + data.data = buf; + data.size = strlen(buf); + + if (yp_put_record(dbp, &key, &data) != YP_TRUE) { + yp_error("failed to write order number to database"); + ypxfr_exit(YPXFR_DBM,&ypxfr_temp_map); + } + + key.data = "YP_MASTER_NAME"; + key.size = sizeof("YP_MASTER_NAME") - 1; + data.data = ypxfr_master; + data.size = strlen(ypxfr_master); + + if (yp_put_record(dbp, &key, &data) != YP_TRUE) { + yp_error("failed to write master name to database"); + ypxfr_exit(YPXFR_DBM,&ypxfr_temp_map); + } + + key.data = "YP_DOMAIN_NAME"; + key.size = sizeof("YP_DOMAIN_NAME") - 1; + data.data = ypxfr_dest_domain; + data.size = strlen(ypxfr_dest_domain); + + if (yp_put_record(dbp, &key, &data) != YP_TRUE) { + yp_error("failed to write domain name to database"); + ypxfr_exit(YPXFR_DBM,&ypxfr_temp_map); + } + + snprintf (buf, sizeof(buf), "%s:%s", ypxfr_source_host, ypxfr_mapname); + + key.data = "YP_INPUT_NAME"; + key.size = sizeof("YP_INPUT_NAME") - 1; + data.data = &buf; + data.size = strlen(buf); + + if (yp_put_record(dbp, &key, &data) != YP_TRUE) { + yp_error("failed to write input name to database"); + ypxfr_exit(YPXFR_DBM,&ypxfr_temp_map); + + } + + snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, ypxfr_dest_domain, + ypxfr_mapname); + + key.data = "YP_OUTPUT_NAME"; + key.size = sizeof("YP_OUTPUT_NAME") - 1; + data.data = &buf; + data.size = strlen(buf); + + if (yp_put_record(dbp, &key, &data) != YP_TRUE) { + yp_error("failed to write output name to database"); + ypxfr_exit(YPXFR_DBM,&ypxfr_temp_map); + } + + /* Now suck over the contents of the map from the master. */ + + if (ypxfr_get_map(ypxfr_mapname,ypxfr_source_domain, + ypxfr_source_host, ypxfr_foreach)){ + yp_error("failed to retrieve map from source host"); + ypxfr_exit(YPXFR_YPERR,&ypxfr_temp_map); + } + + (void)(dbp->close)(dbp); + + /* Peek at the order number again and check for skew. */ + if ((ypxfr_skew_check = ypxfr_get_order(ypxfr_source_domain, + ypxfr_mapname, + ypxfr_master, 0)) == 0) { + yp_error("failed to get order number of %s", + ypxfr_mapname); + ypxfr_exit(YPXFR_YPERR,&ypxfr_temp_map); + } + + if (ypxfr_order != ypxfr_skew_check) + ypxfr_exit(YPXFR_SKEW,&ypxfr_temp_map); + + /* + * Send a YPPROC_CLEAR to the local ypserv. + * The FreeBSD ypserv doesn't really need this, but we send it + * here anyway for the sake of consistency. + */ + if (!ypxfr_clear) { + char in = 0; + char *out = NULL; + if (callrpc("localhost",YPPROG,YPVERS,YPPROC_CLEAR, xdr_void, + (void *)&in, xdr_void, (void *)out) == NULL) { + yp_error("failed to send 'clear' to local ypserv"); + ypxfr_exit(YPXFR_YPERR, &ypxfr_temp_map); + } + } + + if (unlink(buf) == -1 && errno != ENOENT) { + yp_error("unlink(%s) failed: %s", buf, strerror(errno)); + ypxfr_exit(YPXFR_FILE,NULL); + } + + if (rename(ypxfr_temp_map, buf) == -1) { + yp_error("rename(%s,%s) failed: %s", ypxfr_temp_map, buf, + strerror(errno)); + ypxfr_exit(YPXFR_FILE,NULL); + } + + ypxfr_exit(YPXFR_SUCC,NULL); + + return(1); +} diff --git a/libexec/ypxfr/ypxfr_misc.c b/libexec/ypxfr/ypxfr_misc.c new file mode 100644 index 0000000..4093dae --- /dev/null +++ b/libexec/ypxfr/ypxfr_misc.c @@ -0,0 +1,240 @@ +/* + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: ypxfr_misc.c,v 1.6 1995/12/24 04:39:55 wpaul Exp $ + */ +#include <rpc/rpc.h> +#include <rpcsvc/yp.h> +struct dom_binding {}; +#include <rpcsvc/ypclnt.h> +#include "ypxfr_extern.h" + +#ifndef lint +static const char rcsid[] = "$Id: ypxfr_misc.c,v 1.6 1995/12/24 04:39:55 wpaul Exp $"; +#endif + +char *ypxfrerr_string(code) + ypxfrstat code; +{ + switch(code) { + case YPXFR_SUCC: + return ("Map successfully transfered"); + break; + case YPXFR_AGE: + return ("Master's version not newer"); + break; + case YPXFR_NOMAP: + return ("No such map in server's domain"); + break; + case YPXFR_NODOM: + return ("Domain not supported by server"); + break; + case YPXFR_RSRC: + return ("Local resource allocation failure"); + break; + case YPXFR_RPC: + return ("RPC failure talking to server"); + break; + case YPXFR_MADDR: + return ("Could not get master server address"); + break; + case YPXFR_YPERR: + return ("NIS server/map database error"); + break; + case YPXFR_BADARGS: + return ("Request arguments bad"); + break; + case YPXFR_DBM: + return ("Local database operation failed"); + break; + case YPXFR_FILE: + return ("Local file I/O operation failed"); + break; + case YPXFR_SKEW: + return ("Map version skew during transfer"); + break; + case YPXFR_CLEAR: + return ("Couldn't send \"clear\" request to local ypserv"); + break; + case YPXFR_FORCE: + return ("No local order number in map -- use -f flag"); + break; + case YPXFR_XFRERR: + return ("General ypxfr error"); + break; + case YPXFR_REFUSED: + return ("Transfer request refused by ypserv"); + break; + default: + return ("Unknown error code"); + break; + } +} + +/* + * These are wrappers for the usual yp_master() and yp_order() functions. + * They can use either local yplib functions (the real yp_master() and + * yp_order()) or do direct RPCs to a specified server. The latter is + * necessary if ypxfr is run on a machine that isn't configured as an + * NIS client (this can happen very easily: a given machine need not be + * an NIS client in order to be an NIS server). + */ + +char *ypxfr_get_master(domain,map,source,yplib) + char *domain; + char *map; + char *source; + const int yplib; +{ + if (yplib) { + int res; + char *master; + if ((res = yp_master(domain, map, &master))) { + switch (res) { + case YPERR_DOMAIN: + yp_errno = YPXFR_NODOM; + break; + case YPERR_MAP: + yp_errno = YPXFR_NOMAP; + break; + case YPERR_YPERR: + default: + yp_errno = YPXFR_YPERR; + break; + } + return(NULL); + } else + return(master); + } else { + CLIENT *clnt; + ypresp_master *resp; + ypreq_nokey req; + + if ((clnt = clnt_create(source,YPPROG,YPVERS,"udp")) == NULL) { + yp_error("%s",clnt_spcreateerror("failed to \ +create udp handle to ypserv")); + yp_errno = YPXFR_RPC; + return(NULL); + } + + req.map = map; + req.domain = domain; + if ((resp = ypproc_master_2(&req, clnt)) == NULL) { + yp_error("%s",clnt_sperror(clnt,"YPPROC_MASTER \ +failed")); + clnt_destroy(clnt); + yp_errno = YPXFR_RPC; + return(NULL); + } + clnt_destroy(clnt); + if (resp->stat != YP_TRUE) { + switch (resp->stat) { + case YP_NODOM: + yp_errno = YPXFR_NODOM; + break; + case YP_NOMAP: + yp_errno = YPXFR_NOMAP; + break; + case YP_YPERR: + default: + yp_errno = YPXFR_YPERR; + break; + } + return(NULL); + } + return(resp->peer); + } +} + +unsigned long ypxfr_get_order(domain, map, source, yplib) + char *domain; + char *map; + char *source; + const int yplib; +{ + if (yplib) { + unsigned long order; + int res; + if ((res = yp_order(domain, map, (int *)&order))) { + switch (res) { + case (YPERR_DOMAIN): + yp_errno = YPXFR_NODOM; + break; + case (YPERR_MAP): + yp_errno = YPXFR_NOMAP; + break; + case (YPERR_YPERR): + default: + yp_errno = YPXFR_YPERR; + break; + } + return(0); + } else + return(order); + } else { + CLIENT *clnt; + ypresp_order *resp; + ypreq_nokey req; + + if ((clnt = clnt_create(source,YPPROG,YPVERS,"udp")) == NULL) { + yp_error("%s",clnt_spcreateerror("couldn't create \ +udp handle to ypserv")); + yp_errno = YPXFR_RPC; + return(0); + } + req.map = map; + req.domain = domain; + if ((resp = ypproc_order_2(&req, clnt)) == NULL) { + yp_error("%s", clnt_sperror(clnt, "YPPROC_ORDER \ +failed")); + clnt_destroy(clnt); + yp_errno = YPXFR_RPC; + return(0); + } + clnt_destroy(clnt); + if (resp->stat != YP_TRUE) { + switch (resp->stat) { + case (YP_NODOM): + yp_errno = YPXFR_NODOM; + break; + case (YP_NOMAP): + yp_errno = YPXFR_NOMAP; + break; + case (YP_YPERR): + default: + yp_errno = YPXFR_YPERR; + break; + } + return(0); + } + return(resp->ordernum); + } +} |