various fixes suggested by Robert Elz:

* implement closedataconn() and use appropriately (including in mlsd())
* only put leading space in front of MLST output (not MLSD output)
* MLSD: only output pdir and cdir entries when the type fact is requested.
* change error code for giving MLSD a non-directory from 550 to 501
* remove MLSx Type fact support for UNIX.* for now; it's not standardised yet.
* do a check_login when MLSD and MLST are given no args
* detect & complain about null facts in OPTS MLST
* cache getgroups() at login instead of calling each time in fact_perm()

other mods:
* implement cprintf(); as per fprintf() but increments total_bytes{,_out}
* implement CPUTC(); as per putc() but increments total_bytes{,_out}
* implement base64_encode()
* fact_unique() display base64 encoding of dev_t and ino_t rather than
  hex output; should scale if size of those changes
* change reply() so that a negative code acts as the initial line in a reply,
  code == 0 prefixes the line with 4 spaces, and code > 0 works as before.
  deprecate lreply(code, ) and lreply(0, ) in favour of reply(-code, ) and
  reply(0, ) respectively.
* use cprintf() and CPUTC() appropriately (often instead of printf(),
  lreply(-2, ) or lreply(-1, ).
  now we actually account for the data sent by MLST and MLSD.
* remove DEBUG support for sending MLSD output to control connection instead
  of data connection (my ftp client now supports MLSD :-)
This commit is contained in:
lukem 2000-06-19 15:15:03 +00:00
parent 4f82723a61
commit 73f082e2ea
7 changed files with 302 additions and 320 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: cmds.c,v 1.2 2000/06/16 23:17:41 explorer Exp $ */
/* $NetBSD: cmds.c,v 1.3 2000/06/19 15:15:03 lukem Exp $ */
/*
* Copyright (c) 1999-2000 The NetBSD Foundation, Inc.
@ -101,7 +101,7 @@
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: cmds.c,v 1.2 2000/06/16 23:17:41 explorer Exp $");
__RCSID("$NetBSD: cmds.c,v 1.3 2000/06/19 15:15:03 lukem Exp $");
#endif /* not lint */
#include <sys/param.h>
@ -133,15 +133,13 @@ typedef struct {
} factelem;
static void ack(const char *);
static void base64_encode(const char *, size_t, char *, int);
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 int matchgroup(gid_t);
static void mlsname(FILE *, factelem *);
static void replydirname(const char *, const char *);
@ -154,13 +152,11 @@ struct ftpfact {
struct ftpfact facttab[] = {
{ "Type", 1, fact_type },
#define FACT_TYPE 0
{ "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" */
@ -199,16 +195,16 @@ feat(void)
{
int i;
lreply(211, "Features supported");
lreply(-1, " MDTM");
lreply(-2, " MLST ");
reply(-211, "Features supported");
cprintf(stdout, " MDTM\r\n");
cprintf(stdout, " MLST ");
for (i = 0; facttab[i].name; i++)
lreply(-2, "%s%s;", facttab[i].name,
cprintf(stdout, "%s%s;", facttab[i].name,
facttab[i].enabled ? "*" : "");
lreply(-1, "");
lreply(-1, " REST STREAM");
lreply(-1, " SIZE");
lreply(-1, " TVFS");
cprintf(stdout, "\r\n");
cprintf(stdout, " REST STREAM\r\n");
cprintf(stdout, " SIZE\r\n");
cprintf(stdout, " TVFS\r\n");
reply(211, "End");
}
@ -228,13 +224,15 @@ makedir(const char *name)
void
mlsd(const char *path)
{
FILE *dout;
DIR *dirp;
struct dirent *dp;
struct stat sb, pdirstat;
char name[MAXPATHLEN];
struct dirent *dp;
struct stat sb, pdirstat;
factelem f;
FILE *dout;
DIR *dirp;
char name[MAXPATHLEN];
int hastypefact;
hastypefact = facttab[FACT_TYPE].enabled;
if (path == NULL)
path = ".";
if (stat(path, &pdirstat) == -1) {
@ -244,53 +242,48 @@ mlsd(const char *path)
}
if (! S_ISDIR(pdirstat.st_mode)) {
errno = ENOTDIR;
goto mlsdperror;
perror_reply(501, path);
return;
}
#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 (ISDOTDIR(dp->d_name)) { /* special case curdir: */
if (! hastypefact)
continue;
f.pdirstat = NULL; /* require stat of parent */
f.display = path; /* set name to real name */
f.iscurdir = 1; /* flag name is curdir */
} else {
if (ISDOTDOTDIR(dp->d_name)) {
if (! hastypefact)
continue;
f.pdirstat = NULL;
} else
f.pdirstat = &pdirstat; /* cache parent stat */
f.display = dp->d_name;
f.iscurdir = 0;
}
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);
closedataconn(dout);
total_xfers_out++;
total_xfers++;
#endif
}
void
@ -305,12 +298,13 @@ mlst(const char *path)
perror_reply(550, path);
return;
}
lreply(250, "MLST %s", path);
reply(-250, "MLST %s", path);
f.path = path;
f.display = path;
f.stat = &sb;
f.pdirstat = NULL;
f.iscurdir = 0;
CPUTC(' ', stdout);
mlsname(stdout, &f);
reply(250, "End");
}
@ -341,37 +335,47 @@ opts(const char *command)
/* special case: MLST */
if (strcasecmp(command, "MLST") == 0) {
int enabled[sizeof(facttab) / sizeof(struct ftpfact)];
int i, onedone;
size_t len;
char *p;
for (i = 0; facttab[i].name; i++)
facttab[i].enabled = 0;
for (i = 0; i < sizeof(enabled) / sizeof(int); i++)
enabled[i] = 0;
if (ep == NULL || *ep == '\0')
goto displaymlstopts;
/* don't like spaces, and need trailing ; */
if (strchr(ep, ' ') != NULL || ep[strlen(ep) - 1] != ';') {
len = strlen(ep);
if (strchr(ep, ' ') != NULL || ep[len - 1] != ';') {
badmlstopt:
reply(501, "Invalid MLST options");
return;
}
ep[len - 1] = '\0';
while ((p = strsep(&ep, ";")) != NULL) {
if (*p == '\0')
goto badmlstopt;
for (i = 0; facttab[i].name; i++)
if (strcasecmp(p, facttab[i].name) == 0) {
facttab[i].enabled = 1;
enabled[i] = 1;
break;
}
}
displaymlstopts:
lreply(-2, "200 MLST OPTS");
for (i = 0; facttab[i].name; i++)
facttab[i].enabled = enabled[i];
cprintf(stdout, "200 MLST OPTS");
for (i = onedone = 0; facttab[i].name; i++) {
if (facttab[i].enabled) {
lreply(-2, "%s%s;", onedone ? "" : " ",
cprintf(stdout, "%s%s;", onedone ? "" : " ",
facttab[i].name);
onedone++;
}
}
lreply(-1, "");
cprintf(stdout, "\r\n");
fflush(stdout);
return;
}
@ -488,7 +492,8 @@ statfilecmd(const char *filename)
argv[2] = (char *)filename;
fin = ftpd_popen(argv, "r", STDOUT_FILENO);
lreply(211, "status of %s:", filename);
reply(-211, "status of %s:", filename);
/* XXX: use fgetln() or fparseln() here? */
while ((c = getc(fin)) != EOF) {
if (c == '\n') {
if (ferror(stdout)){
@ -502,13 +507,9 @@ statfilecmd(const char *filename)
(void) ftpd_pclose(fin);
return;
}
(void) putchar('\r');
total_bytes++;
total_bytes_out++;
CPUTC('\r', stdout);
}
(void) putchar(c);
total_bytes++;
total_bytes_out++;
CPUTC(c, stdout);
}
(void) ftpd_pclose(fin);
reply(211, "End of Status");
@ -523,13 +524,40 @@ ack(const char *s)
reply(250, "%s command successful.", s);
}
/*
* Encode len bytes starting at clear using base64 encoding into encoded,
* which should be at least ((len + 2) * 4 / 3 + 1) in size.
* If nulterm is non-zero, terminate with \0 else pad to len with `='.
*/
static void
base64_encode(const char *clear, size_t len, char *encoded, int nulterm)
{
static const char enc[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char *cp;
int i;
cp = encoded;
for (i = 0; i < len; i += 3) {
*(cp++) = enc[((clear[i + 0] >> 2))];
*(cp++) = enc[((clear[i + 0] << 4) & 0x30)
| ((clear[i + 1] >> 4) & 0x0f)];
*(cp++) = enc[((clear[i + 1] << 2) & 0x3c)
| ((clear[i + 2] >> 6) & 0x03)];
*(cp++) = enc[((clear[i + 2] ) & 0x3f)];
}
*cp = '\0';
while (i-- > len)
*(--cp) = nulterm ? '\0' : '=';
}
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,
cprintf(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);
@ -538,16 +566,14 @@ fact_modify(const char *fact, FILE *fd, factelem *fe)
static void
fact_perm(const char *fact, FILE *fd, factelem *fe)
{
int rok, wok, xok, pdirwok, ngid;
int rok, wok, xok, pdirwok;
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)) {
} else if (matchgroup(fe->stat->st_gid)) {
rok = ((fe->stat->st_mode & S_IRGRP) != 0);
wok = ((fe->stat->st_mode & S_IWGRP) != 0);
xok = ((fe->stat->st_mode & S_IXGRP) != 0);
@ -557,7 +583,7 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
xok = ((fe->stat->st_mode & S_IXOTH) != 0);
}
fprintf(fd, "%s=", fact);
cprintf(fd, "%s=", fact);
/*
* if parent info not provided, look it up, but
@ -571,7 +597,6 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
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));
@ -585,7 +610,6 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
} else
strlcpy(realdir, "..", sizeof(realdir));
}
/* printf("[real=%s]", realdir); */
if (stat(realdir, &dir) == 0)
pdir = &dir;
}
@ -594,22 +618,19 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
if (pdir != NULL) {
if (pdir->st_uid == geteuid())
pdirwok = ((pdir->st_mode & S_IWUSR) != 0);
else if (matchgroup(pdir->st_gid, gids, ngid))
else if (matchgroup(pdir->st_gid))
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);
CPUTC('a', fd);
/* 'c': can create or append to files in directory */
if (wok && curclass.modify && S_ISDIR(fe->stat->st_mode))
fputs("c", fd);
CPUTC('c', fd);
/* 'd': can delete file or directory */
if (pdirwok && curclass.modify) {
@ -634,38 +655,38 @@ fact_perm(const char *fact, FILE *fd, factelem *fe)
}
}
if (candel)
fputs("d", fd);
CPUTC('d', fd);
}
/* 'e': can enter directory */
if (xok && S_ISDIR(fe->stat->st_mode))
fputs("e", fd);
CPUTC('e', fd);
/* 'f': can rename file or directory */
if (pdirwok && curclass.modify)
fputs("f", fd);
CPUTC('f', fd);
/* 'l': can list directory */
if (rok && xok && S_ISDIR(fe->stat->st_mode))
fputs("l", fd);
CPUTC('l', fd);
/* 'm': can create directory */
if (wok && curclass.modify && S_ISDIR(fe->stat->st_mode))
fputs("m", fd);
CPUTC('m', fd);
/* 'p': can remove files in directory */
if (wok && curclass.modify && S_ISDIR(fe->stat->st_mode))
fputs("p", fd);
CPUTC('p', fd);
/* 'r': can RETR file */
if (rok && S_ISREG(fe->stat->st_mode))
fputs("r", fd);
CPUTC('r', fd);
/* 'w': can STOR file */
if (wok && curclass.upload && S_ISREG(fe->stat->st_mode))
fputs("w", fd);
CPUTC('w', fd);
fputc(';', fd);
CPUTC(';', fd);
}
static void
@ -673,82 +694,65 @@ 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);
cprintf(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);
cprintf(fd, "%s=", fact);
switch (fe->stat->st_mode & S_IFMT) {
case S_IFDIR:
if (fe->iscurdir || ISDOTDIR(fe->display))
fputs("cdir", fd);
cprintf(fd, "cdir");
else if (ISDOTDOTDIR(fe->display))
fputs("pdir", fd);
cprintf(fd, "pdir");
else
fputs("dir", fd);
cprintf(fd, "dir");
break;
case S_IFREG:
fputs("file", fd);
cprintf(fd, "file");
break;
case S_IFIFO:
fputs("OS.unix=fifo", fd);
cprintf(fd, "OS.unix=fifo");
break;
case S_IFLNK:
fputs("OS.unix=slink", fd);
case S_IFLNK: /* XXX: probably a NO-OP with stat() */
cprintf(fd, "OS.unix=slink");
break;
case S_IFSOCK:
fputs("OS.unix=socket", fd);
cprintf(fd, "OS.unix=socket");
break;
case S_IFBLK:
case S_IFCHR:
fprintf(fd, "OS.unix=%s-%d/%d",
cprintf(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);
cprintf(fd, "OS.unix=UNKNOWN(0%o)", fe->stat->st_mode & S_IFMT);
break;
}
fputc(';', fd);
CPUTC(';', fd);
}
static void
fact_unique(const char *fact, FILE *fd, factelem *fe)
{
char obuf[(MAX(sizeof(dev_t),sizeof(ino_t)) + 2) * 4 / 3 + 2];
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);
base64_encode((char *)&(fe->stat->st_dev), sizeof(dev_t), obuf, 1);
cprintf(fd, "%s=%s", fact, obuf);
base64_encode((char *)&(fe->stat->st_ino), sizeof(ino_t), obuf, 1);
cprintf(fd, "%s;", obuf);
}
static int
matchgroup(gid_t gid, gid_t *gidlist, int ngids)
matchgroup(gid_t gid)
{
int i;
for (i = 0; i < ngids; i++)
for (i = 0; i < gidcount; i++)
if (gid == gidlist[i])
return(1);
return (0);
@ -759,12 +763,11 @@ 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);
cprintf(fp, " %s\r\n", fe->display);
}
static void

View File

@ -1,4 +1,4 @@
/* $NetBSD: conf.c,v 1.30 2000/05/20 02:20:18 lukem Exp $ */
/* $NetBSD: conf.c,v 1.31 2000/06/19 15:15:03 lukem Exp $ */
/*-
* Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
@ -38,7 +38,7 @@
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: conf.c,v 1.30 2000/05/20 02:20:18 lukem Exp $");
__RCSID("$NetBSD: conf.c,v 1.31 2000/06/19 15:15:03 lukem Exp $");
#endif /* not lint */
#include <sys/types.h>
@ -510,15 +510,15 @@ show_chdir_messages(int code)
continue;
then = st.st_mtime;
if (code != 0) {
lreply(code, "");
reply(-code, "");
code = 0;
}
lreply(code, "Please read the file %s", *rlist);
reply(-code, "Please read the file %s", *rlist);
t = localtime(&now);
age = 365 * t->tm_year + t->tm_yday;
t = localtime(&then);
age -= 365 * t->tm_year + t->tm_yday;
lreply(code, " it was last modified on %.24s - %d day%s ago",
reply(-code, " it was last modified on %.24s - %d day%s ago",
ctime(&then), age, PLURAL(age));
}
globfree(&gl);
@ -530,27 +530,23 @@ format_file(const char *file, int code)
FILE *f;
char *buf, *p, *cwd;
size_t len;
off_t b;
time_t now;
if (quietmessages)
return (0);
#define PUTC(x) putchar(x), b++
if (EMPTYSTR(file))
return(0);
if ((f = fopen(file, "r")) == NULL)
return (0);
lreply(code, "");
reply(-code, "");
b = 0;
for (;
(buf = fparseln(f, &len, NULL, "\0\0\0", 0)) != NULL; free(buf)) {
if (len > 0)
if (buf[len - 1] == '\n')
buf[--len] = '\0';
b += printf(" ");
cprintf(stdout, " ");
for (p = buf; *p; p++) {
if (*p == '%') {
@ -558,7 +554,7 @@ format_file(const char *file, int code)
switch (*p) {
case 'c':
b += printf("%s",
cprintf(stdout, "%s",
curclass.classname ?
curclass.classname : "<unknown>");
break;
@ -570,7 +566,7 @@ format_file(const char *file, int code)
strerror(errno));
continue;
}
b += printf("%s", cwd);
cprintf(stdout, "%s", cwd);
break;
case 'E':
@ -578,51 +574,48 @@ format_file(const char *file, int code)
break;
case 'L':
b += printf("%s", hostname);
cprintf(stdout, "%s", hostname);
break;
case 'M':
if (curclass.limit == -1)
b += printf("unlimited");
cprintf(stdout, "unlimited");
else
b += printf("%d",
cprintf(stdout, "%d",
curclass.limit);
break;
case 'N':
if (connections > 0)
b += printf("%d", connections);
cprintf(stdout, "%d",
connections);
break;
case 'R':
b += printf("%s", remotehost);
cprintf(stdout, "%s", remotehost);
break;
case 'T':
now = time(NULL);
b += printf("%.24s", ctime(&now));
cprintf(stdout, "%.24s", ctime(&now));
break;
case 'U':
b += printf("%s",
cprintf(stdout, "%s",
pw ? pw->pw_name : "<unknown>");
break;
case '%':
PUTC('%');
CPUTC('%', stdout);
break;
}
} else {
PUTC(*p);
}
} else
CPUTC(*p, stdout);
}
PUTC('\r');
PUTC('\n');
cprintf(stdout, "\r\n");
}
total_bytes += b;
total_bytes_out += b;
(void)fflush(stdout);
(void)fclose(f);
return (1);

View File

@ -1,4 +1,4 @@
/* $NetBSD: extern.h,v 1.27 2000/06/14 13:44:22 lukem Exp $ */
/* $NetBSD: extern.h,v 1.28 2000/06/19 15:15:03 lukem Exp $ */
/*-
* Copyright (c) 1992, 1993
@ -101,9 +101,11 @@
*/
void blkfree(char **);
void closedataconn(FILE *);
char *conffilename(const char *);
char **copyblk(char **);
void count_users(void);
void cprintf(FILE *, const char *, ...);
void cwd(const char *);
FILE *dataconn(const char *, off_t, const char *);
void delete(const char *);
@ -120,7 +122,6 @@ 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 *);
@ -231,6 +232,8 @@ GLOBAL struct ftpclass curclass;
GLOBAL int debug;
GLOBAL jmp_buf errcatch;
GLOBAL int form;
GLOBAL int gidcount; /* number of entries in gidlist[] */
GLOBAL gid_t gidlist[NGROUPS_MAX];
GLOBAL int hasyyerrored;
GLOBAL char hostname[MAXHOSTNAMELEN+1];
#ifdef KERBEROS5
@ -268,6 +271,10 @@ extern struct tab cmdtab[];
#define CMD_IMPLEMENTED(x) ((x)->flags != 0)
#define CMD_HAS_OPTIONS(x) ((x)->flags == 2)
#define CPUTC(c, f) do { \
putc(c, f); total_bytes++; total_bytes_out++; \
} while (0);
#define CURCLASSTYPE curclass.type == CLASS_GUEST ? "GUEST" : \
curclass.type == CLASS_CHROOT ? "CHROOT" : \
curclass.type == CLASS_REAL ? "REAL" : \

View File

@ -1,4 +1,4 @@
/* $NetBSD: ftpcmd.y,v 1.47 2000/06/14 13:44:23 lukem Exp $ */
/* $NetBSD: ftpcmd.y,v 1.48 2000/06/19 15:15:03 lukem Exp $ */
/*-
* Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
@ -83,7 +83,7 @@
#if 0
static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94";
#else
__RCSID("$NetBSD: ftpcmd.y,v 1.47 2000/06/14 13:44:23 lukem Exp $");
__RCSID("$NetBSD: ftpcmd.y,v 1.48 2000/06/19 15:15:03 lukem Exp $");
#endif
#endif /* not lint */
@ -225,12 +225,12 @@ cmd
| QUIT CRLF
{
if (logged_in) {
lreply(221, "");
lreply(0,
reply(-221, "");
reply(0,
"Data traffic for this session was %qd byte%s in %qd file%s.",
(qdfmt_t)total_data, PLURAL(total_data),
(qdfmt_t)total_files, PLURAL(total_files));
lreply(0,
reply(0,
"Total traffic for this session was %qd byte%s in %qd transfer%s.",
(qdfmt_t)total_bytes, PLURAL(total_bytes),
(qdfmt_t)total_xfers, PLURAL(total_xfers));
@ -1015,7 +1015,7 @@ cmd
free($4);
}
| MLST CRLF
| MLST check_login CRLF
{
mlst(NULL);
}
@ -1028,7 +1028,7 @@ cmd
free($4);
}
| MLSD CRLF
| MLSD check_login CRLF
{
mlsd(NULL);
}
@ -1502,7 +1502,6 @@ lookup(struct tab *p, const char *cmd)
char *
getline(char *s, int n, FILE *iop)
{
off_t b;
int c;
char *cs;
@ -1535,9 +1534,7 @@ getline(char *s, int n, FILE *iop)
c = getc(iop);
total_bytes++;
total_bytes_in++;
b = printf("%c%c%c", IAC, DONT, 0377&c);
total_bytes += b;
total_bytes_out += b;
cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c);
(void) fflush(stdout);
continue;
case DO:
@ -1545,9 +1542,7 @@ getline(char *s, int n, FILE *iop)
c = getc(iop);
total_bytes++;
total_bytes_in++;
b = printf("%c%c%c", IAC, WONT, 0377&c);
total_bytes += b;
total_bytes_out += b;
cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c);
(void) fflush(stdout);
continue;
case IAC:
@ -1872,41 +1867,35 @@ help(struct tab *ctab, const char *s)
int i, j, w;
int columns, lines;
lreply(214, "");
lreply(0, "The following %scommands are recognized.", type);
lreply(0, "(`-' = not implemented, `+' = supports options)");
reply(-214, "");
reply(0, "The following %scommands are recognized.", type);
reply(0, "(`-' = not implemented, `+' = supports options)");
columns = 76 / width;
if (columns == 0)
columns = 1;
lines = (NCMDS + columns - 1) / columns;
for (i = 0; i < lines; i++) {
lreply(-2, " ");
cprintf(stdout, " ");
for (j = 0; j < columns; j++) {
c = ctab + j * lines + i;
lreply(-2, "%s", c->name);
cprintf(stdout, "%s", c->name);
w = strlen(c->name);
if (! CMD_IMPLEMENTED(c)) {
putchar('-');
total_bytes++;
total_bytes_out++;
CPUTC('-', stdout);
w++;
}
if (CMD_HAS_OPTIONS(c)) {
putchar('+');
total_bytes++;
total_bytes_out++;
CPUTC('+', stdout);
w++;
}
if (c + lines >= &ctab[NCMDS])
break;
while (w < width) {
putchar(' ');
total_bytes++;
total_bytes_out++;
CPUTC(' ', stdout);
w++;
}
}
lreply(-2, "\r\n");
cprintf(stdout, "\r\n");
}
(void) fflush(stdout);
reply(214, "Direct comments to ftp-bugs@%s.", hostname);

View File

@ -1,4 +1,4 @@
.\" $NetBSD: ftpd.8,v 1.52 2000/06/14 13:44:24 lukem Exp $
.\" $NetBSD: ftpd.8,v 1.53 2000/06/19 15:15:04 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 June 14, 2000
.Dd June 20, 2000
.Dt FTPD 8
.Os
.Sh NAME
@ -197,9 +197,11 @@ The case of the requests is ignored.
.It EPRT Ta "specify data connection port"
.It FEAT Ta "list extra features that are not defined in" Cm "RFC 959"
.It HELP Ta "give help information"
.It LIST Ta "give list files in a directory" Pq Dq Li "ls -lgA"
.It LIST Ta "give list files in a directory" Pq Dq Li "ls -lA"
.It LPSV Ta "prepare for server-to-server transfer"
.It LPRT Ta "specify data connection port"
.It MLSD Ta "list contents of directory in a machine-processable form"
.It MLST Ta "show a pathname in a machine-processable form"
.It MKD Ta "make a directory"
.It MDTM Ta "show last modification time of file"
.It MODE Ta "specify data transfer" Em mode
@ -553,11 +555,15 @@ The
command appeared in
.Bx 4.2 .
.Pp
The
Various features such as the
.Xr ftpd.conf 5
functionality was implemented in
functionality,
.Cm RFC 2389 ,
and
.Cm draft-ietf-ftpext-mlst-10
support was implemented in
.Nx 1.3
and later releases by Luke Mewburn, based on work by Simon Burge.
and later releases by Luke Mewburn <lukem@netbsd.org>.
.Sh BUGS
The server must run as the super-user to create sockets with
privileged port numbers.

View File

@ -1,4 +1,4 @@
/* $NetBSD: ftpd.c,v 1.94 2000/06/14 13:55:15 itojun Exp $ */
/* $NetBSD: ftpd.c,v 1.95 2000/06/19 15:15:04 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.94 2000/06/14 13:55:15 itojun Exp $");
__RCSID("$NetBSD: ftpd.c,v 1.95 2000/06/19 15:15:04 lukem Exp $");
#endif
#endif /* not lint */
@ -243,6 +243,7 @@ main(int argc, char *argv[])
usedefault = 1;
(void)strcpy(confdir, _DEFAULT_CONFDIR);
hostname[0] = '\0';
gidcount = 0;
while ((ch = getopt(argc, argv, "a:c:C:dh:lst:T:u:Uv")) != -1) {
switch (ch) {
@ -343,7 +344,7 @@ main(int argc, char *argv[])
while (fgets(line, sizeof(line), fd) != NULL) {
if ((cp = strchr(line, '\n')) != NULL)
*cp = '\0';
lreply(530, "%s", line);
reply(-530, "%s", line);
}
(void) fflush(stdout);
(void) fclose(fd);
@ -734,6 +735,7 @@ end_login(void)
pw = NULL;
logged_in = 0;
quietmessages = 0;
gidcount = 0;
curclass.type = CLASS_REAL;
}
@ -847,6 +849,7 @@ pass(const char *passwd)
goto bad;
}
(void) initgroups(pw->pw_name, pw->pw_gid);
gidcount = getgroups(sizeof(gidlist), gidlist);
/* open wtmp before chroot */
logwtmp(ttyline, pw->pw_name, remotehost);
@ -932,7 +935,7 @@ pass(const char *passwd)
pw->pw_name, pw->pw_dir);
goto bad;
} else
lreply(230,
reply(-230,
"No directory! Logging in with home=/");
} else
home = pw->pw_dir;
@ -993,7 +996,7 @@ pass(const char *passwd)
void
retrieve(char *argv[], const char *name)
{
FILE *fin = NULL, *dout;
FILE *fin, *dout;
struct stat st;
int (*closefunc)(FILE *) = NULL;
int log, sendrv, closerv, stderrfd, isconversion, isdata, isls;
@ -1004,6 +1007,7 @@ retrieve(char *argv[], const char *name)
isconversion = isdata = isls = log = 0;
tdp = NULL;
dispname = name;
fin = dout = NULL;
if (argv == NULL) {
log = 1;
isdata = 1;
@ -1075,10 +1079,10 @@ retrieve(char *argv[], const char *name)
(void)gettimeofday(&start, NULL);
sendrv = send_data(fin, dout, st.st_blksize, isdata);
(void)gettimeofday(&finish, NULL);
(void) fclose(dout);
(void) fclose(dout); /* close now to affect timing stats */
dout = NULL;
timersub(&finish, &start, &td);
tdp = &td;
data = -1;
done:
if (log)
logcmd("get", byte_count, name, NULL, tdp, NULL);
@ -1088,7 +1092,7 @@ retrieve(char *argv[], const char *name)
struct stat sb;
if (!isls && argv != NULL && closerv != 0) {
lreply(226,
reply(-226,
"Command returned an exit status of %d",
closerv);
if (isconversion)
@ -1101,12 +1105,12 @@ retrieve(char *argv[], const char *name)
((err = fdopen(stderrfd, "r")) != NULL)) {
char *cp, line[LINE_MAX];
lreply(226, "Command error messages:");
reply(-226, "Command error messages:");
rewind(err);
while (fgets(line, sizeof(line), err) != NULL) {
if ((cp = strchr(line, '\n')) != NULL)
*cp = '\0';
lreply(0, " %s", line);
reply(0, " %s", line);
}
(void) fflush(stdout);
(void) fclose(err);
@ -1115,9 +1119,7 @@ retrieve(char *argv[], const char *name)
reply(226, "Transfer complete.");
}
cleanupretrieve:
if (pdata >= 0)
(void)close(pdata);
pdata = -1;
closedataconn(dout);
if (stderrfd != -1)
(void)close(stderrfd);
if (isconversion)
@ -1133,6 +1135,7 @@ store(const char *name, const char *mode, int unique)
struct timeval start, finish, td, *tdp;
char *desc;
din = NULL;
desc = (*mode == 'w') ? "put" : "append";
if (unique && stat(name, &st) == 0 &&
(name = gunique(name)) == NULL) {
@ -1190,17 +1193,15 @@ store(const char *name, const char *mode, int unique)
reply(226, "Transfer complete.");
}
(void)gettimeofday(&finish, NULL);
(void) fclose(din);
(void) fclose(din); /* close now to affect timing stats */
din = NULL;
timersub(&finish, &start, &td);
tdp = &td;
data = -1;
done:
logcmd(desc, byte_count, name, NULL, tdp, NULL);
(*closefunc)(fout);
cleanupstore:
if (pdata >= 0)
(void)close(pdata);
pdata = -1;
closedataconn(din);
}
static FILE *
@ -1338,6 +1339,18 @@ dataconn(const char *name, off_t size, const char *mode)
return (file);
}
void
closedataconn(FILE *fd)
{
if (fd != NULL)
(void)fclose(fd);
data = -1;
if (pdata >= 0)
(void)close(pdata);
pdata = -1;
}
/*
* Tranfer the contents of "instr" to "outstr" peer using the appropriate
* encapsulation of the data subject * to Mode, Structure, and Type.
@ -1605,10 +1618,10 @@ receive_data(FILE *instr, FILE *outstr)
if (ferror(outstr))
goto file_err;
if (bare_lfs) {
lreply(226,
reply(-226,
"WARNING! %d bare linefeeds received in ASCII mode",
bare_lfs);
lreply(0, "File may not have transferred correctly.");
reply(0, "File may not have transferred correctly.");
}
rval = 0;
goto cleanup_recv_data;
@ -1649,50 +1662,50 @@ statcmd(void)
a = p = (u_char *)NULL;
lreply(211, "%s FTP server status:", hostname);
lreply(0, "Version: %s", FTPD_VERSION);
reply(-211, "%s FTP server status:", hostname);
reply(0, "Version: %s", FTPD_VERSION);
ntop_buf[0] = '\0';
if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
ntop_buf, sizeof(ntop_buf), NULL, 0, NI_NUMERICHOST)
&& strcmp(remotehost, ntop_buf) != 0) {
lreply(0, "Connected to %s (%s)", remotehost, ntop_buf);
reply(0, "Connected to %s (%s)", remotehost, ntop_buf);
} else
lreply(0, "Connected to %s", remotehost);
reply(0, "Connected to %s", remotehost);
if (logged_in) {
if (curclass.type == CLASS_GUEST)
lreply(0, "Logged in anonymously");
reply(0, "Logged in anonymously");
else
lreply(0, "Logged in as %s%s", pw->pw_name,
reply(0, "Logged in as %s%s", pw->pw_name,
curclass.type == CLASS_CHROOT ? " (chroot)" : "");
} else if (askpasswd)
lreply(0, "Waiting for password");
reply(0, "Waiting for password");
else
lreply(0, "Waiting for user name");
lreply(-2, " TYPE: %s", typenames[type]);
reply(0, "Waiting for user name");
cprintf(stdout, " TYPE: %s", typenames[type]);
if (type == TYPE_A || type == TYPE_E)
lreply(-2, ", FORM: %s", formnames[form]);
cprintf(stdout, ", FORM: %s", formnames[form]);
if (type == TYPE_L) {
#if NBBY == 8
lreply(-2, " %d", NBBY);
cprintf(stdout, " %d", NBBY);
#else
/* XXX: `bytesize' needs to be defined in this case */
lreply(-2, " %d", bytesize);
cprintf(stdout, " %d", bytesize);
#endif
}
lreply(-1, "; STRUcture: %s; transfer MODE: %s",
cprintf(stdout, "; STRUcture: %s; transfer MODE: %s\r\n",
strunames[stru], modenames[mode]);
ispassive = 0;
if (data != -1) {
lreply(0, "Data connection open");
reply(0, "Data connection open");
su = NULL;
} else if (pdata != -1) {
lreply(0, "in Passive mode");
reply(0, "in Passive mode");
su = (union sockunion *)&pasv_addr;
ispassive = 1;
goto printaddr;
} else if (usedefault == 0) {
if (epsvall) {
lreply(0, "EPSV only mode (EPSV ALL)");
reply(0, "EPSV only mode (EPSV ALL)");
goto epsvonly;
}
su = (union sockunion *)&data_dest;
@ -1702,7 +1715,7 @@ statcmd(void)
a = (u_char *) &su->su_sin.sin_addr;
p = (u_char *) &su->su_sin.sin_port;
#define UC(b) (((int) b) & 0xff)
lreply(0, "%s (%d,%d,%d,%d,%d,%d)",
reply(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]));
@ -1731,11 +1744,11 @@ statcmd(void)
break;
}
if (af) {
lreply(-2, " %s (%d,%d",
cprintf(stdout, " %s (%d,%d",
ispassive ? "LPSV" : "LPRT", af, alen);
for (i = 0; i < alen; i++)
lreply(-2, ",%d", UC(a[i]));
lreply(-1, ",%d,%d,%d)", 2, UC(p[0]), UC(p[1]));
cprintf(stdout, ",%d", UC(a[i]));
cprintf(stdout, ",%d,%d,%d)", 2, UC(p[0]), UC(p[1]));
#undef UC
}
}
@ -1757,88 +1770,86 @@ statcmd(void)
if (getnameinfo((struct sockaddr *)su, su->su_len,
ntop_buf, sizeof(ntop_buf), NULL, 0,
NI_NUMERICHOST) == 0) {
lreply(0, "%s (|%d|%s|%d|)",
reply(0, "%s (|%d|%s|%d|)",
ispassive ? "EPSV" : "EPRT",
af, ntop_buf, ntohs(su->su_port));
}
}
} else
lreply(0, "No data connection");
reply(0, "No data connection");
if (logged_in) {
lreply(0, "Data sent: %qd byte%s in %qd file%s",
reply(0, "Data sent: %qd byte%s in %qd file%s",
(qdfmt_t)total_data_out, PLURAL(total_data_out),
(qdfmt_t)total_files_out, PLURAL(total_files_out));
lreply(0, "Data received: %qd byte%s in %qd file%s",
reply(0, "Data received: %qd byte%s in %qd file%s",
(qdfmt_t)total_data_in, PLURAL(total_data_in),
(qdfmt_t)total_files_in, PLURAL(total_files_in));
lreply(0, "Total data: %qd byte%s in %qd file%s",
reply(0, "Total data: %qd byte%s in %qd file%s",
(qdfmt_t)total_data, PLURAL(total_data),
(qdfmt_t)total_files, PLURAL(total_files));
}
otbi = total_bytes_in;
otbo = total_bytes_out;
otb = total_bytes;
lreply(0, "Traffic sent: %qd byte%s in %qd transfer%s",
reply(0, "Traffic sent: %qd byte%s in %qd transfer%s",
(qdfmt_t)otbo, PLURAL(otbo),
(qdfmt_t)total_xfers_out, PLURAL(total_xfers_out));
lreply(0, "Traffic received: %qd byte%s in %qd transfer%s",
reply(0, "Traffic received: %qd byte%s in %qd transfer%s",
(qdfmt_t)otbi, PLURAL(otbi),
(qdfmt_t)total_xfers_in, PLURAL(total_xfers_in));
lreply(0, "Total traffic: %qd byte%s in %qd transfer%s",
reply(0, "Total traffic: %qd byte%s in %qd transfer%s",
(qdfmt_t)otb, PLURAL(otb),
(qdfmt_t)total_xfers, PLURAL(total_xfers));
if (logged_in) {
struct ftpconv *cp;
lreply(0, "");
lreply(0, "Class: %s, type: %s",
reply(0, "");
reply(0, "Class: %s, type: %s",
curclass.classname, CURCLASSTYPE);
lreply(0, "Check PORT/LPRT commands: %sabled",
reply(0, "Check PORT/LPRT commands: %sabled",
curclass.checkportcmd ? "en" : "dis");
if (curclass.display != NULL)
lreply(0, "Display file: %s", curclass.display);
reply(0, "Display file: %s", curclass.display);
if (curclass.notify != NULL)
lreply(0, "Notify fileglob: %s", curclass.notify);
lreply(0, "Idle timeout: %d, maximum timeout: %d",
reply(0, "Notify fileglob: %s", curclass.notify);
reply(0, "Idle timeout: %d, maximum timeout: %d",
curclass.timeout, curclass.maxtimeout);
lreply(0, "Current connections: %d", connections);
reply(0, "Current connections: %d", connections);
if (curclass.limit == -1)
lreply(0, "Maximum connections: unlimited");
reply(0, "Maximum connections: unlimited");
else
lreply(0, "Maximum connections: %d", curclass.limit);
reply(0, "Maximum connections: %d", curclass.limit);
if (curclass.limitfile)
lreply(0, "Connection limit exceeded file: %s",
reply(0, "Connection limit exceeded file: %s",
curclass.limitfile);
if (curclass.motd != NULL)
lreply(0, "MotD file: %s", curclass.motd);
lreply(0,
reply(0, "MotD file: %s", curclass.motd);
reply(0,
"Modify commands (CHMOD, DELE, MKD, RMD, RNFR, UMASK): %sabled",
curclass.modify ? "en" : "dis");
lreply(0,
"Upload commands (APPE, STOR, STOU): %sabled",
reply(0, "Upload commands (APPE, STOR, STOU): %sabled",
curclass.upload ? "en" : "dis");
if (curclass.portmin && curclass.portmax)
lreply(0, "PASV port range: %d - %d",
reply(0, "PASV port range: %d - %d",
curclass.portmin, curclass.portmax);
if (curclass.rateget)
lreply(0, "Rate get limit: %d bytes/sec",
reply(0, "Rate get limit: %d bytes/sec",
curclass.rateget);
else
lreply(0, "Rate get limit: disabled");
reply(0, "Rate get limit: disabled");
if (curclass.rateput)
lreply(0, "Rate put limit: %d bytes/sec",
reply(0, "Rate put limit: %d bytes/sec",
curclass.rateput);
else
lreply(0, "Rate put limit: disabled");
lreply(0, "Umask: %.04o", curclass.umask);
reply(0, "Rate put limit: disabled");
reply(0, "Umask: %.04o", curclass.umask);
for (cp = curclass.conversions; cp != NULL; cp=cp->next) {
if (cp->suffix == NULL || cp->types == NULL ||
cp->command == NULL)
continue;
lreply(0,
"Conversion: %s [%s] disable: %s, command: %s",
reply(0, "Conversion: %s [%s] disable: %s, command: %s",
cp->suffix, cp->types, cp->disable, cp->command);
}
}
@ -1858,8 +1869,11 @@ fatal(const char *s)
/*
* reply() --
* display the final line of a reply, with a trailing CRLF
* i.e, n + " " + fmt + CRLF
* depending on the value of n, display fmt with a trailing CRLF and
* prefix of:
* n < -1 prefix the message with abs(n) + "-" (initial line)
* n == 0 prefix the message with 4 spaces (middle lines)
* n > 0 prefix the message with n + " " (final line)
*/
void
reply(int n, const char *fmt, ...)
@ -1869,53 +1883,19 @@ reply(int n, const char *fmt, ...)
va_start(ap, fmt);
b = 0;
b += printf("%d ", n);
b += vprintf(fmt, ap);
b += printf("\r\n");
if (n == 0)
cprintf(stdout, " ");
else if (n < 0)
cprintf(stdout, "%d-", -n);
else
cprintf(stdout, "%d ", n);
b = vprintf(fmt, ap);
total_bytes += b;
total_bytes_out += b;
cprintf(stdout, "\r\n");
(void)fflush(stdout);
if (debug) {
syslog(LOG_DEBUG, "<--- %d ", n);
vsyslog(LOG_DEBUG, fmt, ap);
}
}
/*
* 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, ...)
{
off_t b;
va_list ap;
va_start(ap, fmt);
b = 0;
switch (n) {
case 0:
b += printf(" ");
case -1:
case -2:
break;
default:
b += printf("%d-", n);
break;
}
b += vprintf(fmt, ap);
if (n != -2)
b += printf("\r\n");
total_bytes += b;
total_bytes_out += b;
if (n != -2)
(void)fflush(stdout);
if (debug && n >= 0) {
syslog(LOG_DEBUG, "<--- %d- ", n);
syslog(LOG_DEBUG, "<--- %d%c", abs(n), (n < 0) ? '-' : ' ');
vsyslog(LOG_DEBUG, fmt, ap);
}
}
@ -2290,15 +2270,7 @@ send_file_list(const char *whichf)
goto out;
}
perror_reply(550, whichf);
if (dout != NULL) {
(void) fclose(dout);
transflag = 0;
data = -1;
if (pdata >= 0)
(void)close(pdata);
pdata = -1;
}
goto out;
goto cleanup_send_file_list;
}
if (S_ISREG(st.st_mode)) {
@ -2368,13 +2340,9 @@ send_file_list(const char *whichf)
else
reply(226, "Transfer complete.");
cleanup_send_file_list:
transflag = 0;
if (dout != NULL)
(void) fclose(dout);
else if (pdata >= 0)
(void)close(pdata);
data = -1;
pdata = -1;
closedataconn(dout);
out:
total_xfers++;
total_xfers_out++;
@ -2461,3 +2429,19 @@ xstrdup(const char *s)
/* NOTREACHED */
return (new);
}
/*
* As per fprintf(), but increment total_bytes and total_bytes_out,
* by the appropriate amount.
*/
void
cprintf(FILE *fd, const char *fmt, ...)
{
off_t b;
va_list ap;
va_start(ap, fmt);
b = vfprintf(fd, fmt, ap);
total_bytes += b;
total_bytes_out += b;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: version.h,v 1.11 2000/06/14 13:44:25 lukem Exp $ */
/* $NetBSD: version.h,v 1.12 2000/06/19 15:15:04 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 20000614"
#define FTPD_VERSION "NetBSD-ftpd 20000620"
#endif