major overhaul (just before netbsd 1.5 :-):

* implement draft-ietf-ftpext-mlst-10 commands, especially MLST and MLSD.
  we already supported SIZE and MDTM. add the appropriate FEAT output lines.

* migrate a lot of the command code from ftpcmd.y and ftpd.c to cmds.c

* make dataconn(), feat(), lookup(), opts() and sizecmd() public

* modify struct tab so that it has a `flags' instead of `implemented' element,
  and remove the `hasopts' element.  If flags == 1, the command is implemented.
  if flags == 2, the command is implemented and takes options

* add macros ISDOTDIR(x) (is x ".") and ISDOTDOTDIR(x) (is x "..")

* modify lreply() so that lreply(-2, ...) just outputs the given info without
  a prefix or trailing \r\n. this saves doing b = printf(); total_* += b;

* enhance statcmd(). still needs work in the LPRT status stuff.

* crank version
This commit is contained in:
lukem 2000-06-14 13:44:21 +00:00
parent 7c77443b0b
commit a26448af43
7 changed files with 990 additions and 425 deletions

View File

@ -1,12 +1,13 @@
# $NetBSD: Makefile,v 1.40 2000/03/05 06:12:19 lukem Exp $
# $NetBSD: Makefile,v 1.41 2000/06/14 13:44:21 lukem Exp $
# @(#)Makefile 8.2 (Berkeley) 4/4/94
SRCTOP= ../..
.include <bsd.crypto.mk>
PROG= ftpd
SRCS= conf.c ftpd.c ftpcmd.y logutmp.c logwtmp.c popen.c
SRCS= cmds.c conf.c ftpd.c ftpcmd.y logutmp.c logwtmp.c popen.c
CPPFLAGS+=-DHASSETPROCTITLE
# CPPFLAGS+=-DDEBUG # XXX for lukem testing
DPADD+= ${LIBCRYPT} ${LIBUTIL}
LDADD+= -lcrypt -lutil
MAN= ftpd.conf.5 ftpusers.5 ftpd.8

779
libexec/ftpd/cmds.c Normal file
View File

@ -0,0 +1,779 @@
/* $NetBSD: cmds.c,v 1.1 2000/06/14 13:44:22 lukem Exp $ */
/*
* Copyright (c) 1999-2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Luke Mewburn.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* 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.
*/
/*
* Copyright (C) 1997 and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: cmds.c,v 1.1 2000/06/14 13:44:22 lukem Exp $");
#endif /* not lint */
#include <sys/param.h>
#include <sys/stat.h>
#include <arpa/ftp.h>
#include <dirent.h>
#include <errno.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tzfile.h>
#include <unistd.h>
#include "extern.h"
typedef struct {
const char *path; /* full pathname */
const char *display; /* name to display */
struct stat *stat; /* stat of path */
struct stat *pdirstat; /* stat of path's parent dir */
int iscurdir; /* nonzero if name is the current dir */
} factelem;
static void ack(const char *);
static void fact_type(const char *, FILE *, factelem *);
static void fact_size(const char *, FILE *, factelem *);
static void fact_modify(const char *, FILE *, factelem *);
static void fact_perm(const char *, FILE *, factelem *);
static void fact_unique(const char *, FILE *, factelem *);
static void fact_unix_group(const char *, FILE *, factelem *);
static void fact_unix_mode(const char *, FILE *, factelem *);
static void fact_unix_owner(const char *, FILE *, factelem *);
static int matchgroup(gid_t, gid_t *, int);
static void mlsname(FILE *, factelem *);
static void replydirname(const char *, const char *);
struct ftpfact {
const char *name; /* name of fact */
int enabled; /* if fact is enabled */
void (*display)(const char *, FILE *, factelem *);
/* function to display fact */
};
struct ftpfact facttab[] = {
{ "Type", 1, fact_type },
{ "Size", 1, fact_size },
{ "Modify", 1, fact_modify },
{ "Perm", 1, fact_perm },
{ "Unique", 1, fact_unique },
{ "UNIX.mode", 1, fact_unix_mode },
{ "UNIX.owner", 0, fact_unix_owner },
{ "UNIX.group", 0, fact_unix_group },
/* "Create" */
/* "Lang" */
/* "Media-Type" */
/* "CharSet" */
{ NULL, NULL, },
};
void
cwd(const char *path)
{
if (chdir(path) < 0)
perror_reply(550, path);
else {
show_chdir_messages(250);
ack("CWD");
}
}
void
delete(const char *name)
{
char *p = NULL;
if (remove(name) < 0) {
p = strerror(errno);
perror_reply(550, name);
} else
ack("DELE");
logcmd("delete", -1, name, NULL, NULL, p);
}
void
feat(void)
{
int i;
lreply(211, "Features supported");
lreply(-1, " MDTM");
lreply(-2, " MLST ");
for (i = 0; facttab[i].name; i++)
lreply(-2, "%s%s;", facttab[i].name,
facttab[i].enabled ? "*" : "");
lreply(-1, "");
lreply(-1, " REST STREAM");
lreply(-1, " SIZE");
lreply(-1, " TVFS");
reply(211, "End");
}
void
makedir(const char *name)
{
char *p = NULL;
if (mkdir(name, 0777) < 0) {
p = strerror(errno);
perror_reply(550, name);
} else
replydirname(name, "directory created.");
logcmd("mkdir", -1, name, NULL, NULL, p);
}
void
mlsd(const char *path)
{
FILE *dout;
DIR *dirp;
struct dirent *dp;
struct stat sb, pdirstat;
char name[MAXPATHLEN];
factelem f;
if (path == NULL)
path = ".";
if (stat(path, &pdirstat) == -1) {
mlsdperror:
perror_reply(550, path);
return;
}
if (! S_ISDIR(pdirstat.st_mode)) {
errno = ENOTDIR;
goto mlsdperror;
}
#ifdef DEBUG /* send mlsd to ctrl connection not data connection */
dout = stdout;
lreply(250, "MLSD %s", path);
#else
dout = dataconn("MLSD", (off_t)-1, "w");
if (dout == NULL)
return;
#endif
if ((dirp = opendir(path)) == NULL)
goto mlsdperror;
f.stat = &sb;
while ((dp = readdir(dirp)) != NULL) {
snprintf(name, sizeof(name), "%s/%s", path, dp->d_name);
/* printf("got >%s< >%s<\n", dp->d_name, name); */
if (stat(name, &sb) == -1)
continue;
f.path = name;
if (ISDOTDIR(dp->d_name)) { /* special case curdir: */
f.display = path; /* set name to real name */
f.iscurdir = 1; /* flag name is curdir */
f.pdirstat = NULL; /* require stat of parent */
} else {
f.display = dp->d_name;
f.iscurdir = 0;
if (ISDOTDOTDIR(dp->d_name))
f.pdirstat = NULL;
else
f.pdirstat = &pdirstat; /* cache parent stat */
}
mlsname(dout, &f);
}
(void)closedir(dirp);
#ifdef DEBUG
reply(250, "End");
#else
if (ferror(dout) != 0)
perror_reply(550, "Data connection");
else
reply(226, "MLSD complete.");
(void) fclose(dout);
total_xfers_out++;
total_xfers++;
#endif
}
void
mlst(const char *path)
{
struct stat sb;
factelem f;
if (path == NULL)
path = ".";
if (stat(path, &sb) == -1) {
perror_reply(550, path);
return;
}
lreply(250, "MLST %s", path);
f.path = path;
f.display = path;
f.stat = &sb;
f.pdirstat = NULL;
f.iscurdir = 0;
mlsname(stdout, &f);
reply(250, "End");
}
void
opts(const char *command)
{
struct tab *c;
char *ep;
if ((ep = strchr(command, ' ')) != NULL)
*ep++ = '\0';
c = lookup(cmdtab, command);
if (c == NULL) {
reply(502, "Unknown command %s.", command);
return;
}
if (! CMD_IMPLEMENTED(c)) {
reply(501, "%s command not implemented.", c->name);
return;
}
if (! CMD_HAS_OPTIONS(c)) {
reply(501, "%s command does not support persistent options.",
c->name);
return;
}
/* special case: MLST */
if (strcasecmp(command, "MLST") == 0) {
int i, onedone;
char *p;
for (i = 0; facttab[i].name; i++)
facttab[i].enabled = 0;
if (ep == NULL || *ep == '\0')
goto displaymlstopts;
/* don't like spaces, and need trailing ; */
if (strchr(ep, ' ') != NULL || ep[strlen(ep) - 1] != ';') {
reply(501, "Invalid MLST options");
return;
}
while ((p = strsep(&ep, ";")) != NULL) {
for (i = 0; facttab[i].name; i++)
if (strcasecmp(p, facttab[i].name) == 0) {
facttab[i].enabled = 1;
break;
}
}
displaymlstopts:
lreply(-2, "200 MLST OPTS");
for (i = onedone = 0; facttab[i].name; i++) {
if (facttab[i].enabled) {
lreply(-2, "%s%s;", onedone ? "" : " ",
facttab[i].name);
onedone++;
}
}
lreply(-1, "");
return;
}
/* default cases */
if (ep != NULL && *ep != '\0')
REASSIGN(c->options, xstrdup(ep));
if (c->options != NULL)
reply(200, "Options for %s are '%s'.", c->name,
c->options);
else
reply(200, "No options defined for %s.", c->name);
}
void
pwd(void)
{
char path[MAXPATHLEN];
if (getcwd(path, sizeof(path) - 1) == NULL)
reply(550, "Can't get the current directory: %s.",
strerror(errno));
else
replydirname(path, "is the current directory.");
}
void
removedir(const char *name)
{
char *p = NULL;
if (rmdir(name) < 0) {
p = strerror(errno);
perror_reply(550, name);
} else
ack("RMD");
logcmd("rmdir", -1, name, NULL, NULL, p);
}
char *
renamefrom(const char *name)
{
struct stat st;
if (stat(name, &st) < 0) {
perror_reply(550, name);
return (NULL);
}
reply(350, "File exists, ready for destination name");
return (xstrdup(name));
}
void
renamecmd(const char *from, const char *to)
{
char *p = NULL;
if (rename(from, to) < 0) {
p = strerror(errno);
perror_reply(550, "rename");
} else
ack("RNTO");
logcmd("rename", -1, from, to, NULL, p);
}
void
sizecmd(const 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", (qufmt_t)stbuf.st_size);
break; }
case TYPE_A: {
FILE *fin;
int c;
off_t count;
struct stat stbuf;
fin = fopen(filename, "r");
if (fin == NULL) {
perror_reply(550, filename);
return;
}
if (fstat(fileno(fin), &stbuf) < 0 || !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", (qdfmt_t)count);
break; }
default:
reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
}
}
void
statfilecmd(const char *filename)
{
FILE *fin;
int c;
char *argv[] = { INTERNAL_LS, "-lgA", "", NULL };
argv[2] = (char *)filename;
fin = ftpd_popen(argv, "r", STDOUT_FILENO);
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) putchar('\r');
total_bytes++;
total_bytes_out++;
}
(void) putchar(c);
total_bytes++;
total_bytes_out++;
}
(void) ftpd_pclose(fin);
reply(211, "End of Status");
}
/* -- */
static void
ack(const char *s)
{
reply(250, "%s command successful.", s);
}
static void
fact_modify(const char *fact, FILE *fd, factelem *fe)
{
struct tm *t;
t = gmtime(&(fe->stat->st_mtime));
fprintf(fd, "%s=%04d%02d%02d%02d%02d%02d;", fact,
TM_YEAR_BASE + t->tm_year,
t->tm_mon+1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec);
}
static void
fact_perm(const char *fact, FILE *fd, factelem *fe)
{
int rok, wok, xok, pdirwok, ngid;
struct stat *pdir;
gid_t gids[NGROUPS_MAX];
ngid = getgroups(sizeof(gids), gids);
if (fe->stat->st_uid == geteuid()) {
rok = ((fe->stat->st_mode & S_IRUSR) != 0);
wok = ((fe->stat->st_mode & S_IWUSR) != 0);
xok = ((fe->stat->st_mode & S_IXUSR) != 0);
} else if (matchgroup(fe->stat->st_gid, gids, ngid)) {
rok = ((fe->stat->st_mode & S_IRGRP) != 0);
wok = ((fe->stat->st_mode & S_IWGRP) != 0);
xok = ((fe->stat->st_mode & S_IXGRP) != 0);
} else {
rok = ((fe->stat->st_mode & S_IROTH) != 0);
wok = ((fe->stat->st_mode & S_IWOTH) != 0);
xok = ((fe->stat->st_mode & S_IXOTH) != 0);
}
fprintf(fd, "%s=", fact);
/*
* if parent info not provided, look it up, but
* only if the current class has modify rights,
* since we only need this info in such a case.
*/
pdir = fe->pdirstat;
if (pdir == NULL && curclass.modify) {
size_t len;
char realdir[MAXPATHLEN], *p;
struct stat dir;
len = strlcpy(realdir, fe->path, sizeof(realdir));
/* printf("[path=%s]", fe->path); */
if (len < sizeof(realdir) - 4) {
if (S_ISDIR(fe->stat->st_mode))
strlcat(realdir, "/..", sizeof(realdir));
else {
/* if has a /, move back to it */
/* otherwise use '..' */
if ((p = strrchr(realdir, '/')) != NULL) {
if (p == realdir)
p++;
*p = '\0';
} else
strlcpy(realdir, "..", sizeof(realdir));
}
/* printf("[real=%s]", realdir); */
if (stat(realdir, &dir) == 0)
pdir = &dir;
}
}
pdirwok = 0;
if (pdir != NULL) {
if (pdir->st_uid == geteuid())
pdirwok = ((pdir->st_mode & S_IWUSR) != 0);
else if (matchgroup(pdir->st_gid, gids, ngid))
pdirwok = ((pdir->st_mode & S_IWGRP) != 0);
else
pdirwok = ((pdir->st_mode & S_IWOTH) != 0);
}
/* printf("[euid=%d,r%d,w%d,x%d,pw%d]", geteuid(), rok, wok, xok, pdirwok);
*/
/* 'a': can APPE to file */
if (wok && curclass.upload && S_ISREG(fe->stat->st_mode))
fputs("a", fd);
/* 'c': can create or append to files in directory */
if (wok && curclass.modify && S_ISDIR(fe->stat->st_mode))
fputs("c", fd);
/* 'd': can delete file or directory */
if (pdirwok && curclass.modify) {
int candel;
candel = 1;
if (S_ISDIR(fe->stat->st_mode)) {
DIR *dirp;
struct dirent *dp;
if ((dirp = opendir(fe->display)) == NULL)
candel = 0;
else {
while ((dp = readdir(dirp)) != NULL) {
if (ISDOTDIR(dp->d_name) ||
ISDOTDOTDIR(dp->d_name))
continue;
candel = 0;
break;
}
closedir(dirp);
}
}
if (candel)
fputs("d", fd);
}
/* 'e': can enter directory */
if (xok && S_ISDIR(fe->stat->st_mode))
fputs("e", fd);
/* 'f': can rename file or directory */
if (pdirwok && curclass.modify)
fputs("f", fd);
/* 'l': can list directory */
if (rok && xok && S_ISDIR(fe->stat->st_mode))
fputs("l", fd);
/* 'm': can create directory */
if (wok && curclass.modify && S_ISDIR(fe->stat->st_mode))
fputs("m", fd);
/* 'p': can remove files in directory */
if (wok && curclass.modify && S_ISDIR(fe->stat->st_mode))
fputs("p", fd);
/* 'r': can RETR file */
if (rok && S_ISREG(fe->stat->st_mode))
fputs("r", fd);
/* 'w': can STOR file */
if (wok && curclass.upload && S_ISREG(fe->stat->st_mode))
fputs("w", fd);
fputc(';', fd);
}
static void
fact_size(const char *fact, FILE *fd, factelem *fe)
{
if (S_ISREG(fe->stat->st_mode))
fprintf(fd, "%s=%lld;", fact, (long long)fe->stat->st_size);
}
static void
fact_type(const char *fact, FILE *fd, factelem *fe)
{
fprintf(fd, "%s=", fact);
switch (fe->stat->st_mode & S_IFMT) {
case S_IFDIR:
if (fe->iscurdir || ISDOTDIR(fe->display))
fputs("cdir", fd);
else if (ISDOTDOTDIR(fe->display))
fputs("pdir", fd);
else
fputs("dir", fd);
break;
case S_IFREG:
fputs("file", fd);
break;
case S_IFIFO:
fputs("OS.unix=fifo", fd);
break;
case S_IFLNK:
fputs("OS.unix=slink", fd);
break;
case S_IFSOCK:
fputs("OS.unix=socket", fd);
break;
case S_IFBLK:
case S_IFCHR:
fprintf(fd, "OS.unix=%s-%d/%d",
S_ISBLK(fe->stat->st_mode) ? "blk" : "chr",
major(fe->stat->st_rdev), minor(fe->stat->st_rdev));
break;
default:
fprintf(fd, "OS.unix=UNKNOWN(0%o)", fe->stat->st_mode & S_IFMT);
break;
}
fputc(';', fd);
}
static void
fact_unique(const char *fact, FILE *fd, factelem *fe)
{
fprintf(fd, "%s=%04x%08x;", fact, fe->stat->st_dev, fe->stat->st_ino);
}
static void
fact_unix_mode(const char *fact, FILE *fd, factelem *fe)
{
fprintf(fd, "%s=%03o;", fact, fe->stat->st_mode & ACCESSPERMS);
}
static void
fact_unix_owner(const char *fact, FILE *fd, factelem *fe)
{
fprintf(fd, "%s=%d;", fact, (int)fe->stat->st_uid);
}
static void
fact_unix_group(const char *fact, FILE *fd, factelem *fe)
{
fprintf(fd, "%s=%d;", fact, (int)fe->stat->st_gid);
}
static int
matchgroup(gid_t gid, gid_t *gidlist, int ngids)
{
int i;
for (i = 0; i < ngids; i++)
if (gid == gidlist[i])
return(1);
return (0);
}
static void
mlsname(FILE *fp, factelem *fe)
{
int i;
fputs(" ", fp);
for (i = 0; facttab[i].name; i++) {
if (facttab[i].enabled)
(facttab[i].display)(facttab[i].name, fp, fe);
}
fprintf(fp, " %s\r\n", fe->display);
}
static void
replydirname(const char *name, const char *message)
{
char npath[MAXPATHLEN];
int i;
for (i = 0; *name != '\0' && i < sizeof(npath) - 1; i++, name++) {
npath[i] = *name;
if (*name == '"')
npath[++i] = '"';
}
npath[i] = '\0';
reply(257, "\"%s\" %s", npath, message);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: extern.h,v 1.26 2000/05/20 02:20:18 lukem Exp $ */
/* $NetBSD: extern.h,v 1.27 2000/06/14 13:44:22 lukem Exp $ */
/*-
* Copyright (c) 1992, 1993
@ -105,10 +105,12 @@ char *conffilename(const char *);
char **copyblk(char **);
void count_users(void);
void cwd(const char *);
FILE *dataconn(const char *, off_t, const char *);
void delete(const char *);
char **do_conversion(const char *);
void dologout(int);
void fatal(const char *);
void feat(void);
int format_file(const char *, int);
int ftpd_pclose(FILE *);
FILE *ftpd_popen(char *[], const char *, int);
@ -117,8 +119,12 @@ void init_curclass(void);
void logcmd(const char *, off_t, const char *, const char *,
const struct timeval *, const char *);
void logwtmp(const char *, const char *, const char *);
struct tab *lookup(struct tab *, const char *);
void lreply(int, const char *, ...);
void makedir(const char *);
void mlsd(const char *);
void mlst(const char *);
void opts(const char *);
void parse_conf(const char *);
void pass(const char *);
void passive(void);
@ -132,6 +138,7 @@ void reply(int, const char *, ...);
void retrieve(char *[], const char *);
void send_file_list(const char *);
void show_chdir_messages(int);
void sizecmd(const char *);
void statcmd(void);
void statfilecmd(const char *);
void store(const char *, const char *, int);
@ -147,7 +154,16 @@ typedef enum {
CLASS_GUEST,
CLASS_CHROOT,
CLASS_REAL
} class_t;
} class_ft;
struct tab {
char *name;
short token;
short state;
short flags; /* 1 if command implemented, 2 if has options */
char *help;
char *options;
};
struct ftpconv {
struct ftpconv *next;
@ -177,7 +193,7 @@ struct ftpclass {
int rateget; /* Get (RETR) transfer rate throttle */
int rateput; /* Put (STOR) transfer rate throttle */
unsigned int timeout; /* Default timeout */
class_t type; /* Class type */
class_ft type; /* Class type */
mode_t umask; /* Umask to use */
int upload; /* As per modify, but also allow
APPE, STOR, STOU */
@ -244,6 +260,21 @@ GLOBAL off_t total_bytes_in, total_bytes_out, total_bytes;
/* total number of xfers */
GLOBAL off_t total_xfers_in, total_xfers_out, total_xfers;
extern struct tab cmdtab[];
#define INTERNAL_LS "/bin/ls"
#define CMD_IMPLEMENTED(x) ((x)->flags != 0)
#define CMD_HAS_OPTIONS(x) ((x)->flags == 2)
#define CURCLASSTYPE curclass.type == CLASS_GUEST ? "GUEST" : \
curclass.type == CLASS_CHROOT ? "CHROOT" : \
curclass.type == CLASS_REAL ? "REAL" : \
"<unknown>"
#define ISDOTDIR(x) (x[0] == '.' && x[1] == '\0')
#define ISDOTDOTDIR(x) (x[0] == '.' && x[1] == '.' && x[2] == '\0')
#define EMPTYSTR(p) ((p) == NULL || *(p) == '\0')
#define NEXTWORD(P, W) do { \
@ -251,5 +282,3 @@ GLOBAL off_t total_xfers_in, total_xfers_out, total_xfers;
} while ((W) != NULL && *(W) == '\0')
#define PLURAL(s) ((s) == 1 ? "" : "s")
#define REASSIGN(X,Y) do { if (X) free(X); (X)=(Y); } while (/*CONSTCOND*/0)
#define INTERNAL_LS "/bin/ls"

View File

@ -1,7 +1,7 @@
/* $NetBSD: ftpcmd.y,v 1.46 2000/05/20 02:20:18 lukem Exp $ */
/* $NetBSD: ftpcmd.y,v 1.47 2000/06/14 13:44:23 lukem Exp $ */
/*-
* Copyright (c) 1997-1999 The NetBSD Foundation, Inc.
* Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@ -83,7 +83,7 @@
#if 0
static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94";
#else
__RCSID("$NetBSD: ftpcmd.y,v 1.46 2000/05/20 02:20:18 lukem Exp $");
__RCSID("$NetBSD: ftpcmd.y,v 1.47 2000/06/14 13:44:23 lukem Exp $");
#endif
#endif /* not lint */
@ -150,7 +150,7 @@ char *fromname;
FEAT OPTS
SIZE MDTM
SIZE MDTM MLST MLSD
LPRT LPSV EPRT EPSV
@ -952,11 +952,8 @@ cmd
/* RFC 2389 */
| FEAT CRLF
{
lreply(211, "Features supported");
lreply(-1, " MDTM");
lreply(-1, " REST STREAM");
lreply(-1, " SIZE");
reply(211, "End");
feat();
}
| OPTS SP STRING CRLF
@ -967,12 +964,9 @@ cmd
}
/* BSD extensions */
/* extensions from draft-ietf-ftpext-mlst-10 */
/*
* SIZE is not in RFC 959, 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).
*/
@ -985,9 +979,6 @@ cmd
}
/*
* MDTM is not in RFC 959, 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,
@ -1003,6 +994,7 @@ cmd
reply(550, "%s: not a plain file.", $4);
} else {
struct tm *t;
t = gmtime(&stbuf.st_mtime);
reply(213,
"%04d%02d%02d%02d%02d%02d",
@ -1015,6 +1007,32 @@ cmd
free($4);
}
| MLST check_login SP pathname CRLF
{
if ($2 && $4 != NULL)
mlst($4);
if ($4 != NULL)
free($4);
}
| MLST CRLF
{
mlst(NULL);
}
| MLSD check_login SP pathname CRLF
{
if ($2 && $4 != NULL)
mlsd($4);
if ($4 != NULL)
free($4);
}
| MLSD CRLF
{
mlsd(NULL);
}
| error CRLF
{
yyerrok;
@ -1373,111 +1391,100 @@ check_upload
#define NSTR 8 /* Number followed by a string */
#define NOARGS 9 /* No arguments allowed */
struct tab {
char *name;
short token;
short state;
short implemented; /* 1 if command is implemented */
short hasopts; /* 1 if command takes options */
char *help;
char *options;
};
struct tab cmdtab[] = {
/* From RFC 959, in order defined (5.3.1) */
{ "USER", USER, STR1, 1, 0, "<sp> username" },
{ "PASS", PASS, ZSTR1, 1, 0, "<sp> password" },
{ "ACCT", ACCT, STR1, 0, 0, "(specify account)" },
{ "CWD", CWD, OSTR, 1, 0, "[ <sp> directory-name ]" },
{ "CDUP", CDUP, NOARGS, 1, 0, "(change to parent directory)" },
{ "SMNT", SMNT, ARGS, 0, 0, "(structure mount)" },
{ "QUIT", QUIT, NOARGS, 1, 0, "(terminate service)" },
{ "REIN", REIN, NOARGS, 0, 0, "(reinitialize server state)" },
{ "PORT", PORT, ARGS, 1, 0, "<sp> b0, b1, b2, b3, b4" },
{ "LPRT", LPRT, ARGS, 1, 0, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
{ "EPRT", EPRT, STR1, 1, 0, "<sp> |af|addr|port|" },
{ "PASV", PASV, NOARGS, 1, 0, "(set server in passive mode)" },
{ "LPSV", LPSV, ARGS, 1, 0, "(set server in passive mode)" },
{ "EPSV", EPSV, ARGS, 1, 0, "[<sp> af|ALL]" },
{ "TYPE", TYPE, ARGS, 1, 0, "<sp> [ A | E | I | L ]" },
{ "STRU", STRU, ARGS, 1, 0, "(specify file structure)" },
{ "MODE", MODE, ARGS, 1, 0, "(specify transfer mode)" },
{ "RETR", RETR, STR1, 1, 0, "<sp> file-name" },
{ "STOR", STOR, STR1, 1, 0, "<sp> file-name" },
{ "STOU", STOU, STR1, 1, 0, "<sp> file-name" },
{ "APPE", APPE, STR1, 1, 0, "<sp> file-name" },
{ "ALLO", ALLO, ARGS, 1, 0, "allocate storage (vacuously)" },
{ "REST", REST, ARGS, 1, 0, "<sp> offset (restart command)" },
{ "RNFR", RNFR, STR1, 1, 0, "<sp> file-name" },
{ "RNTO", RNTO, STR1, 1, 0, "<sp> file-name" },
{ "ABOR", ABOR, NOARGS, 1, 0, "(abort operation)" },
{ "DELE", DELE, STR1, 1, 0, "<sp> file-name" },
{ "RMD", RMD, STR1, 1, 0, "<sp> path-name" },
{ "MKD", MKD, STR1, 1, 0, "<sp> path-name" },
{ "PWD", PWD, NOARGS, 1, 0, "(return current directory)" },
{ "LIST", LIST, OSTR, 1, 0, "[ <sp> path-name ]" },
{ "NLST", NLST, OSTR, 1, 0, "[ <sp> path-name ]" },
{ "SITE", SITE, SITECMD, 1, 0, "site-cmd [ <sp> arguments ]" },
{ "SYST", SYST, NOARGS, 1, 0, "(get type of operating system)" },
{ "STAT", STAT, OSTR, 1, 0, "[ <sp> path-name ]" },
{ "HELP", HELP, OSTR, 1, 0, "[ <sp> <string> ]" },
{ "NOOP", NOOP, NOARGS, 1, 1, "" },
{ "USER", USER, STR1, 1, "<sp> username" },
{ "PASS", PASS, ZSTR1, 1, "<sp> password" },
{ "ACCT", ACCT, STR1, 0, "(specify account)" },
{ "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
{ "CDUP", CDUP, NOARGS, 1, "(change to parent directory)" },
{ "SMNT", SMNT, ARGS, 0, "(structure mount)" },
{ "QUIT", QUIT, NOARGS, 1, "(terminate service)" },
{ "REIN", REIN, NOARGS, 0, "(reinitialize server state)" },
{ "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
{ "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
{ "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
{ "PASV", PASV, NOARGS, 1, "(set server in passive mode)" },
{ "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
{ "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
{ "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
{ "STRU", STRU, ARGS, 1, "(specify file structure)" },
{ "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
{ "RETR", RETR, STR1, 1, "<sp> file-name" },
{ "STOR", STOR, STR1, 1, "<sp> file-name" },
{ "STOU", STOU, STR1, 1, "<sp> file-name" },
{ "APPE", APPE, STR1, 1, "<sp> file-name" },
{ "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, NOARGS, 1, "(abort operation)" },
{ "DELE", DELE, STR1, 1, "<sp> file-name" },
{ "RMD", RMD, STR1, 1, "<sp> path-name" },
{ "MKD", MKD, STR1, 1, "<sp> path-name" },
{ "PWD", PWD, NOARGS, 1, "(return current directory)" },
{ "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
{ "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
{ "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
{ "SYST", SYST, NOARGS, 1, "(get type of operating system)" },
{ "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
{ "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
{ "NOOP", NOOP, NOARGS, 2, "" },
/* From RFC 2228, in order defined */
{ "AUTH", AUTH, STR1, 1, 0, "<sp> mechanism-name" },
{ "ADAT", ADAT, STR1, 1, 0, "<sp> base-64-data" },
{ "PROT", PROT, STR1, 1, 0, "<sp> prot-code" },
{ "PBSZ", PBSZ, ARGS, 1, 0, "<sp> decimal-integer" },
{ "CCC", CCC, NOARGS, 1, 0, "(Disable data protection)" },
{ "MIC", MIC, STR1, 1, 0, "<sp> base64data" },
{ "CONF", CONF, STR1, 1, 0, "<sp> base64data" },
{ "ENC", ENC, STR1, 1, 0, "<sp> base64data" },
{ "AUTH", AUTH, STR1, 1, "<sp> mechanism-name" },
{ "ADAT", ADAT, STR1, 1, "<sp> base-64-data" },
{ "PROT", PROT, STR1, 1, "<sp> prot-code" },
{ "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer" },
{ "CCC", CCC, NOARGS, 1, "(Disable data protection)" },
{ "MIC", MIC, STR1, 1, "<sp> base64data" },
{ "CONF", CONF, STR1, 1, "<sp> base64data" },
{ "ENC", ENC, STR1, 1, "<sp> base64data" },
/* From RFC 2389, in order defined */
{ "FEAT", FEAT, NOARGS, 1, 0, "(display extended features)" },
{ "OPTS", OPTS, STR1, 1, 0, "<sp> command [ <sp> options ]" },
{ "FEAT", FEAT, NOARGS, 1, "(display extended features)" },
{ "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]" },
/* Non standardized extensions */
{ "SIZE", SIZE, OSTR, 1, 0, "<sp> path-name" },
{ "MDTM", MDTM, OSTR, 1, 0, "<sp> path-name" },
/* from draft-ietf-ftpext-mlst-10 */
{ "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
{ "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
{ "MLST", MLST, OSTR, 2, "[ <sp> path-name ]" },
{ "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]" },
/* obsolete commands */
{ "MAIL", MAIL, OSTR, 0, 0, "(mail to user)" },
{ "MLFL", MLFL, OSTR, 0, 0, "(mail file)" },
{ "MRCP", MRCP, STR1, 0, 0, "(mail recipient)" },
{ "MRSQ", MRSQ, OSTR, 0, 0, "(mail recipient scheme question)" },
{ "MSAM", MSAM, OSTR, 0, 0, "(mail send to terminal and mailbox)" },
{ "MSND", MSND, OSTR, 0, 0, "(mail send to terminal)" },
{ "MSOM", MSOM, OSTR, 0, 0, "(mail send to terminal or mailbox)" },
{ "XCUP", CDUP, NOARGS, 1, 0, "(change to parent directory)" },
{ "XCWD", CWD, OSTR, 1, 0, "[ <sp> directory-name ]" },
{ "XMKD", MKD, STR1, 1, 0, "<sp> path-name" },
{ "XPWD", PWD, NOARGS, 1, 0, "(return current directory)" },
{ "XRMD", RMD, STR1, 1, 0, "<sp> path-name" },
{ "MAIL", MAIL, OSTR, 0, "(mail to user)" },
{ "MLFL", MLFL, OSTR, 0, "(mail file)" },
{ "MRCP", MRCP, STR1, 0, "(mail recipient)" },
{ "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
{ "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
{ "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
{ "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
{ "XCUP", CDUP, NOARGS, 1, "(change to parent directory)" },
{ "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
{ "XMKD", MKD, STR1, 1, "<sp> path-name" },
{ "XPWD", PWD, NOARGS, 1, "(return current directory)" },
{ "XRMD", RMD, STR1, 1, "<sp> path-name" },
{ NULL, 0, 0, 0, 0, 0 }
{ NULL, 0, 0, 0, 0 }
};
struct tab sitetab[] = {
{ "CHMOD", CHMOD, NSTR, 1, 0, "<sp> mode <sp> file-name" },
{ "HELP", HELP, OSTR, 1, 0, "[ <sp> <string> ]" },
{ "IDLE", IDLE, ARGS, 1, 0, "[ <sp> maximum-idle-time ]" },
{ "RATEGET", RATEGET, OSTR, 1,0,"[ <sp> get-throttle-rate ]" },
{ "RATEPUT", RATEPUT, OSTR, 1,0,"[ <sp> put-throttle-rate ]" },
{ "UMASK", UMASK, ARGS, 1, 0, "[ <sp> umask ]" },
{ NULL, 0, 0, 0, 0, 0 }
{ "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
{ "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
{ "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
{ "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]" },
{ "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]" },
{ "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
{ NULL, 0, 0, 0, NULL }
};
static void help(struct tab *, const char *);
static struct tab *lookup(struct tab *, const char *);
static void opts(const char *);
static void sizecmd(char *);
static void help(struct tab *, const char *);
static void toolong(int);
static int yylex(void);
extern int epsvall;
static struct tab *
struct tab *
lookup(struct tab *p, const char *cmd)
{
@ -1630,7 +1637,7 @@ yylex(void)
p = lookup(cmdtab, cbuf);
cbuf[cpos] = c;
if (p != NULL) {
if (p->implemented == 0) {
if (! CMD_IMPLEMENTED(p)) {
reply(502, "%s command not implemented.",
p->name);
hasyyerrored = 1;
@ -1655,7 +1662,7 @@ yylex(void)
p = lookup(sitetab, cp);
cbuf[cpos] = c;
if (p != NULL) {
if (p->implemented == 0) {
if (!CMD_IMPLEMENTED(p)) {
reply(502, "SITE %s command not implemented.",
p->name);
hasyyerrored = 1;
@ -1846,7 +1853,6 @@ help(struct tab *ctab, const char *s)
{
struct tab *c;
int width, NCMDS;
off_t b;
char *type;
if (ctab == sitetab)
@ -1874,22 +1880,18 @@ help(struct tab *ctab, const char *s)
columns = 1;
lines = (NCMDS + columns - 1) / columns;
for (i = 0; i < lines; i++) {
b = printf(" ");
total_bytes += b;
total_bytes_out += b;
lreply(-2, " ");
for (j = 0; j < columns; j++) {
c = ctab + j * lines + i;
b = printf("%s", c->name);
total_bytes += b;
total_bytes_out += b;
lreply(-2, "%s", c->name);
w = strlen(c->name);
if (! c->implemented) {
if (! CMD_IMPLEMENTED(c)) {
putchar('-');
total_bytes++;
total_bytes_out++;
w++;
}
if (c->hasopts) {
if (CMD_HAS_OPTIONS(c)) {
putchar('+');
total_bytes++;
total_bytes_out++;
@ -1904,9 +1906,7 @@ help(struct tab *ctab, const char *s)
w++;
}
}
b = printf("\r\n");
total_bytes += b;
total_bytes_out += b;
lreply(-2, "\r\n");
}
(void) fflush(stdout);
reply(214, "Direct comments to ftp-bugs@%s.", hostname);
@ -1917,83 +1917,9 @@ help(struct tab *ctab, const char *s)
reply(502, "Unknown command %s.", s);
return;
}
if (c->implemented)
if (CMD_IMPLEMENTED(c))
reply(214, "Syntax: %s%s %s", type, c->name, c->help);
else
reply(214, "%s%-*s\t%s; not implemented.", type, width,
c->name, c->help);
}
static void
sizecmd(char *filename)
{
switch (type) {
case TYPE_L:
case TYPE_I: {
struct stat stbuf;
if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
reply(550, "%s: not a plain file.", filename);
else
reply(213, "%qu", (qufmt_t)stbuf.st_size);
break; }
case TYPE_A: {
FILE *fin;
int c;
off_t count;
struct stat stbuf;
fin = fopen(filename, "r");
if (fin == NULL) {
perror_reply(550, filename);
return;
}
if (fstat(fileno(fin), &stbuf) < 0 || !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", (qdfmt_t)count);
break; }
default:
reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
}
}
static void
opts(const char *command)
{
struct tab *c;
char *ep;
if ((ep = strchr(command, ' ')) != NULL)
*ep++ = '\0';
c = lookup(cmdtab, command);
if (c == NULL) {
reply(502, "Unknown command %s.", command);
return;
}
if (c->implemented == 0) {
reply(502, "%s command not implemented.", c->name);
return;
}
if (c->hasopts == 0) {
reply(501, "%s command does not support persistent options.",
c->name);
return;
}
if (ep != NULL && *ep != '\0')
REASSIGN(c->options, xstrdup(ep));
if (c->options != NULL)
reply(200, "Options for %s are '%s'.", c->name, c->options);
else
reply(200, "No options defined for %s.", c->name);
}

View File

@ -1,4 +1,4 @@
.\" $NetBSD: ftpd.8,v 1.51 2000/05/16 00:59:12 itojun Exp $
.\" $NetBSD: ftpd.8,v 1.52 2000/06/14 13:44:24 lukem Exp $
.\"
.\" Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
.\" All rights reserved.
@ -67,7 +67,7 @@
.\"
.\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94
.\"
.Dd January 13, 2000
.Dd June 14, 2000
.Dt FTPD 8
.Os
.Sh NAME
@ -543,7 +543,10 @@ recognizes all commands in
.Cm RFC 2228
(although they are not supported yet),
and supports the extensions from
.Cm RFC 2389.
.Cm RFC 2389 ,
.Cm RFC 2428
and
.Cm draft-ietf-ftpext-mlst-10 .
.Sh HISTORY
The
.Nm

View File

@ -1,4 +1,4 @@
/* $NetBSD: ftpd.c,v 1.92 2000/06/02 14:47:19 explorer Exp $ */
/* $NetBSD: ftpd.c,v 1.93 2000/06/14 13:44:24 lukem Exp $ */
/*
* Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
@ -109,7 +109,7 @@ __COPYRIGHT(
#if 0
static char sccsid[] = "@(#)ftpd.c 8.5 (Berkeley) 4/28/95";
#else
__RCSID("$NetBSD: ftpd.c,v 1.92 2000/06/02 14:47:19 explorer Exp $");
__RCSID("$NetBSD: ftpd.c,v 1.93 2000/06/14 13:44:24 lukem Exp $");
#endif
#endif /* not lint */
@ -144,6 +144,7 @@ __RCSID("$NetBSD: ftpd.c,v 1.92 2000/06/02 14:47:19 explorer Exp $");
#include <pwd.h>
#include <setjmp.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -165,8 +166,6 @@ __RCSID("$NetBSD: ftpd.c,v 1.92 2000/06/02 14:47:19 explorer Exp $");
#include "pathnames.h"
#include "version.h"
#include <stdarg.h>
int data;
jmp_buf urgcatch;
struct passwd *pw;
@ -204,16 +203,9 @@ int epsvall = 0;
int swaitmax = SWAITMAX;
int swaitint = SWAITINT;
#define CURCLASSTYPE curclass.type == CLASS_GUEST ? "GUEST" : \
curclass.type == CLASS_CHROOT ? "CHROOT" : \
curclass.type == CLASS_REAL ? "REAL" : \
"<unknown>"
static void ack(const char *);
static int bind_pasv_addr(void);
static int checkuser(const char *, const char *, int, int, char **);
static int checkaccess(const char *);
static FILE *dataconn(const char *, off_t, const char *);
static void dolog(struct sockaddr *);
static void end_login(void);
static FILE *getdatasock(const char *);
@ -221,7 +213,6 @@ static char *gunique(const char *);
static void lostconn(int);
static void myoob(int);
static int receive_data(FILE *, FILE *);
static void replydirname(const char *, const char *);
static int send_data(FILE *, FILE *, off_t, int);
static struct passwd *sgetpwnam(const char *);
@ -487,9 +478,9 @@ sgetpwnam(const char *name)
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 */
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.
@ -1259,7 +1250,7 @@ getdatasock(const char *mode)
return (NULL);
}
static FILE *
FILE *
dataconn(const char *name, off_t size, const char *mode)
{
char sizebuf[32];
@ -1647,41 +1638,6 @@ receive_data(FILE *instr, FILE *outstr)
return (rval);
}
void
statfilecmd(const char *filename)
{
FILE *fin;
int c;
char *argv[] = { INTERNAL_LS, "-lgA", "", NULL };
argv[2] = (char *)filename;
fin = ftpd_popen(argv, "r", STDOUT_FILENO);
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) putchar('\r');
total_bytes++;
total_bytes_out++;
}
(void) putchar(c);
total_bytes++;
total_bytes_out++;
}
(void) ftpd_pclose(fin);
reply(211, "End of Status");
}
void
statcmd(void)
{
@ -1689,7 +1645,7 @@ statcmd(void)
static char ntop_buf[INET6_ADDRSTRLEN];
u_char *a, *p;
int ispassive, af;
off_t b, otbi, otbo, otb;
off_t otbi, otbo, otb;
a = p = (u_char *)NULL;
@ -1712,69 +1668,49 @@ statcmd(void)
lreply(0, "Waiting for password");
else
lreply(0, "Waiting for user name");
b = printf(" TYPE: %s", typenames[type]);
total_bytes += b;
total_bytes_out += b;
if (type == TYPE_A || type == TYPE_E) {
b = printf(", FORM: %s", formnames[form]);
total_bytes += b;
total_bytes_out += b;
}
lreply(-2, " TYPE: %s", typenames[type]);
if (type == TYPE_A || type == TYPE_E)
lreply(-2, ", FORM: %s", formnames[form]);
if (type == TYPE_L) {
#if NBBY == 8
b = printf(" %d", NBBY);
lreply(-2, " %d", NBBY);
#else
/* XXX: `bytesize' needs to be defined in this case */
b = printf(" %d", bytesize);
lreply(-2, " %d", bytesize);
#endif
total_bytes += b;
total_bytes_out += b;
}
b = printf("; STRUcture: %s; transfer MODE: %s\r\n",
lreply(-1, "; STRUcture: %s; transfer MODE: %s",
strunames[stru], modenames[mode]);
total_bytes += b;
total_bytes_out += b;
ispassive = 0;
if (data != -1) {
lreply(0, "Data connection open");
su = NULL;
} else if (pdata != -1) {
b = printf(" in Passive mode");
total_bytes += b;
total_bytes_out += b;
lreply(0, "in Passive mode");
su = (union sockunion *)&pasv_addr;
ispassive = 1;
goto printaddr;
} else if (usedefault == 0) {
if (epsvall) {
b = printf("211- EPSV only mode (EPSV ALL )\r\n");
total_bytes += b;
total_bytes_out += b;
lreply(0, "EPSV only mode (EPSV ALL)");
goto epsvonly;
}
b = printf("211- %s",
(data_dest.su_family == AF_INET) ? "PORT" : "LPRT");
total_bytes += b;
total_bytes_out += b;
su = (union sockunion *)&data_dest;
printaddr:
/* PASV/PORT */
/* PASV/PORT */
if (su->su_family == AF_INET) {
if (ispassive)
b = printf("211- PASV ");
else
b = printf("211- PORT ");
a = (u_char *) &su->su_sin.sin_addr;
p = (u_char *) &su->su_sin.sin_port;
#define UC(b) (((int) b) & 0xff)
b += printf("(%d,%d,%d,%d,%d,%d)\r\n",
lreply(0, "%s (%d,%d,%d,%d,%d,%d)",
ispassive ? "PASV" : "PORT" ,
UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
UC(p[0]), UC(p[1]));
total_bytes += b;
total_bytes_out += b;
}
/* LPSV/LPRT */
/* XXXLUKEM this code doesn't look right; speak to itojun */
/* LPSV/LPRT */
{
int alen, af, i;
@ -1797,16 +1733,11 @@ statcmd(void)
break;
}
if (af) {
if (ispassive)
b = printf("211- LPSV ");
else
b = printf("211- LPRT ");
printf("(%d,%d", af, alen);
lreply(-2, " %s (%d,%d",
ispassive ? "LPSV" : "LPRT", af, alen);
for (i = 0; i < alen; i++)
b += printf("%d,", UC(a[alen]));
b += printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1]));
total_bytes += b;
total_bytes_out += b;
lreply(-2, "%d,", UC(a[alen]));
lreply(-1, "%d,%d,%d)", 2, UC(p[0]), UC(p[1]));
#undef UC
}
}
@ -1826,16 +1757,11 @@ statcmd(void)
}
if (af) {
if (getnameinfo((struct sockaddr *)su, su->su_len,
ntop_buf, sizeof(ntop_buf), NULL, 0,
ntop_buf, sizeof(ntop_buf), NULL, 0,
NI_NUMERICHOST) == 0) {
if (ispassive)
b = printf("211 - EPSV ");
else
b = printf("211 - EPRT ");
b += printf("(|%d|%s|%d|)\r\n",
af, ntop_buf, ntohs(su->su_port));
total_bytes += b;
total_bytes_out += b;
lreply(0, "%s (|%d|%s|%d|)",
ispassive ? "EPSV" : "EPRT",
af, ntop_buf, ntohs(su->su_port));
}
}
} else
@ -1932,6 +1858,11 @@ fatal(const char *s)
/* NOTREACHED */
}
/*
* reply() --
* display the final line of a reply, with a trailing CRLF
* i.e, n + " " + fmt + CRLF
*/
void
reply(int n, const char *fmt, ...)
{
@ -1952,6 +1883,14 @@ reply(int n, const char *fmt, ...)
}
}
/*
* lreply() --
* depending on the value of n, display a line with a trailing CRLF:
* n == -2 freeform text, but no trailing CRLF
* n == -1 freeform text
* n == 0 prefix the message with 4 spaces
* n > 0 prefix the message with n + "-"
*/
void
lreply(int n, const char *fmt, ...)
{
@ -1964,133 +1903,25 @@ lreply(int n, const char *fmt, ...)
case 0:
b += printf(" ");
case -1:
case -2:
break;
default:
b += printf("%d-", n);
break;
}
b += vprintf(fmt, ap);
b += printf("\r\n");
if (n != -2)
b += printf("\r\n");
total_bytes += b;
total_bytes_out += b;
(void)fflush(stdout);
if (debug) {
if (n != -2)
(void)fflush(stdout);
if (debug && n >= 0) {
syslog(LOG_DEBUG, "<--- %d- ", n);
vsyslog(LOG_DEBUG, fmt, ap);
}
}
static void
ack(const char *s)
{
reply(250, "%s command successful.", s);
}
void
delete(const char *name)
{
char *p = NULL;
if (remove(name) < 0) {
p = strerror(errno);
perror_reply(550, name);
} else
ack("DELE");
logcmd("delete", -1, name, NULL, NULL, p);
}
void
cwd(const char *path)
{
if (chdir(path) < 0)
perror_reply(550, path);
else {
show_chdir_messages(250);
ack("CWD");
}
}
static void
replydirname(const char *name, const char *message)
{
char npath[MAXPATHLEN];
int i;
for (i = 0; *name != '\0' && i < sizeof(npath) - 1; i++, name++) {
npath[i] = *name;
if (*name == '"')
npath[++i] = '"';
}
npath[i] = '\0';
reply(257, "\"%s\" %s", npath, message);
}
void
makedir(const char *name)
{
char *p = NULL;
if (mkdir(name, 0777) < 0) {
p = strerror(errno);
perror_reply(550, name);
} else
replydirname(name, "directory created.");
logcmd("mkdir", -1, name, NULL, NULL, p);
}
void
removedir(const char *name)
{
char *p = NULL;
if (rmdir(name) < 0) {
p = strerror(errno);
perror_reply(550, name);
} else
ack("RMD");
logcmd("rmdir", -1, name, NULL, NULL, p);
}
void
pwd(void)
{
char path[MAXPATHLEN];
if (getcwd(path, sizeof(path) - 1) == NULL)
reply(550, "Can't get the current directory: %s.",
strerror(errno));
else
replydirname(path, "is the current directory.");
}
char *
renamefrom(const char *name)
{
struct stat st;
if (stat(name, &st) < 0) {
perror_reply(550, name);
return (NULL);
}
reply(350, "File exists, ready for destination name");
return (xstrdup(name));
}
void
renamecmd(const char *from, const char *to)
{
char *p = NULL;
if (rename(from, to) < 0) {
p = strerror(errno);
perror_reply(550, "rename");
} else
ack("RNTO");
logcmd("rename", -1, from, to, NULL, p);
}
static void
dolog(struct sockaddr *who)
{
@ -2497,10 +2328,7 @@ send_file_list(const char *whichf)
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)
if (ISDOTDIR(dir->d_name) || ISDOTDOTDIR(dir->d_name))
continue;
(void)snprintf(nbuf, sizeof(nbuf), "%s%s%s", dirname,
@ -2579,7 +2407,6 @@ conffilename(const char *s)
* if elapsed != NULL, append "in xxx.yyy seconds"
* if error != NULL, append ": " + error
*/
void
logcmd(const char *command, off_t bytes, const char *file1, const char *file2,
const struct timeval *elapsed, const char *error)

View File

@ -1,4 +1,4 @@
/* $NetBSD: version.h,v 1.10 2000/05/20 23:34:55 lukem Exp $ */
/* $NetBSD: version.h,v 1.11 2000/06/14 13:44:25 lukem Exp $ */
/*-
* Copyright (c) 1999, 2000 The NetBSD Foundation, Inc.
* All rights reserved.
@ -36,5 +36,5 @@
*/
#ifndef FTPD_VERSION
#define FTPD_VERSION "NetBSD-ftpd 20000521"
#define FTPD_VERSION "NetBSD-ftpd 20000614"
#endif