|
|
Power 6/32 Unix version 1.21
#ifndef lint
static char sccsid[] = "@(#)ftp.c 4.11 (Berkeley) 7/26/83";
#endif
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/ftp.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <netdb.h>
#include "ftp_var.h"
struct sockaddr_in hisctladdr;
struct sockaddr_in data_addr;
int data = -1;
int connected;
struct sockaddr_in myctladdr;
FILE *cin, *cout;
FILE *dataconn();
/*
Changes made to this file :
(1) recvrequest() :
a. Move the logic to check if the local file is successfully
opened before sending the RETR cmd to remote server. Just
to make sure user ftp is ready to accept data from server
before the RETR cmd is sent.
b. Add logic to report actual no of bytes received from server
and actual size of new file. They are different because ftp
user program strips '\n' characters.
c. Fix for 1st char. lost when print out error message to
indicate local cannot be opened.
d. If you try to retrieve something into a local file, and you
don't have the write permission on the file, it should test
whether you have write permission on the directory contain-
ing the file. But, previously, if the local file is a path
name containing slashes, it does a test on a null pathname
rather than on the containing directory. This is wrong.
(2) empty(): Peter P. 07/02/85
@ removed because no routine uses it. Peter P. 07/02/85
(3) getreply(): Peter P. 07/02/85
a. Prevent the reply getter to get more replies than necessary.
only one reply is gotten per call to this routine.
*/
struct hostent *
hookup(host, port)
char *host;
int port;
{
register struct hostent *hp;
int s, len;
bzero((char *)&hisctladdr, sizeof (hisctladdr));
hp = gethostbyname(host);
if (hp == NULL) {
static struct hostent def;
static struct in_addr defaddr;
static char namebuf[128];
int inet_addr();
defaddr.s_addr = inet_addr(host);
if (defaddr.s_addr == -1) {
fprintf(stderr, "%s: Unknown host.\n", host);
return (0);
}
strcpy(namebuf, host);
def.h_name = namebuf;
hostname = namebuf;
def.h_addr = (char *)&defaddr;
def.h_length = sizeof (struct in_addr);
def.h_addrtype = AF_INET;
def.h_aliases = 0;
hp = &def;
}
hostname = hp->h_name;
hisctladdr.sin_family = hp->h_addrtype;
s = socket(hp->h_addrtype, SOCK_STREAM, 0, 0);
if (s < 0) {
perror("ftp: socket");
return (0);
}
if (bind(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) {
perror("ftp: bind");
goto bad;
}
bcopy(hp->h_addr, (char *)&hisctladdr.sin_addr, hp->h_length);
hisctladdr.sin_port = port;
if (connect(s, (char *)&hisctladdr, sizeof (hisctladdr), 0) < 0) {
perror("ftp: connect");
goto bad;
}
len = sizeof (myctladdr);
if (getsockname(s, (char *)&myctladdr, &len) < 0) {
perror("ftp: getsockname");
goto bad;
}
cin = fdopen(s, "r");
cout = fdopen(s, "w");
if (cin == NULL || cout == NULL) {
fprintf(stderr, "ftp: fdopen failed.\n");
if (cin)
fclose(cin);
if (cout)
fclose(cout);
goto bad;
}
if (verbose)
printf("Connected to %s.\n", hp->h_name);
(void) getreply(0); /* read startup message from server */
return (hp);
bad:
close(s);
return ((struct hostent *)0);
}
login(hp)
struct hostent *hp;
{
char acct[80];
char *user, *pass;
int n;
user = pass = 0;
ruserpass(hp->h_name, &user, &pass);
n = command("USER %s", user);
if (n == CONTINUE)
n = command("PASS %s", pass);
if (n == CONTINUE) {
printf("Account: "); (void) fflush(stdout);
(void) fgets(acct, sizeof(acct) - 1, stdin);
acct[strlen(acct) - 1] = '\0';
n = command("ACCT %s", acct);
}
if (n != COMPLETE) {
fprintf(stderr, "Login failed.\n");
return (0);
}
return (1);
}
/*VARARGS 1*/
command(fmt, args)
char *fmt;
{
if (debug) {
printf("---> ");
_doprnt(fmt, &args, stdout);
printf("\n");
(void) fflush(stdout);
}
if (cout == NULL) {
perror ("No control connection for command");
return (0);
}
_doprnt(fmt, &args, cout);
fprintf(cout, "\r\n");
(void) fflush(cout);
return (getreply(!strcmp(fmt, "QUIT")));
}
#include <ctype.h>
getreply(expecteof)
int expecteof;
{
register int n;
register char c;
register int code, dig;
int originalcode = 0, continuation = 0;
for (;;) {
dig = n = code = 0;
while ((c = getc(cin)) != '\n') {
dig++;
if (c == EOF) {
if (expecteof)
return (0);
lostpeer();
exit(1);
}
if (verbose && c != '\r' ||
(n == '5' && dig > 4))
putchar(c);
if (dig < 4 && isdigit(c))
code = code * 10 + (c - '0');
if (dig == 4 && c == '-')
continuation++;
if (n == 0)
n = c;
}
if (verbose || n == '5') {
putchar(c);
(void) fflush (stdout);
}
if (continuation && code != originalcode) {
if (originalcode == 0)
originalcode = code;
continue;
}
/* if (expecteof || empty(cin)) Peter 07/02/85 */
return (n - '0');
}
}
/* not called by anyone anymore... Peter P. 07/02/85
empty(f)
FILE *f;
{
long mask;
struct timeval t;
if (f->_cnt > 0)
return (0);
mask = (1 << fileno(f));
t.tv_sec = t.tv_usec = 0;
(void) select(20, &mask, 0, 0, &t);
return (mask == 0);
}
*/
jmp_buf sendabort;
abortsend()
{
longjmp(sendabort, 1);
}
sendrequest(cmd, local, remote)
char *cmd, *local, *remote;
{
FILE *fin, *dout, *popen();
int (*closefunc)(), pclose(), fclose(), (*oldintr)();
char buf[BUFSIZ];
long bytes = 0, hashbytes = sizeof (buf);
long wscnt = 0;
register int c, d;
struct stat st;
struct timeval start, stop;
closefunc = NULL;
if (setjmp(sendabort))
goto bad;
oldintr = signal(SIGINT, abortsend);
if (strcmp(local, "-") == 0)
fin = stdin;
else if (*local == '|') {
fin = popen(local + 1, "r");
if (fin == NULL) {
perror(local + 1);
goto bad;
}
closefunc = pclose;
} else {
fin = fopen(local, "r");
if (fin == NULL) {
perror(local);
goto bad;
}
closefunc = fclose;
if (fstat(fileno(fin), &st) < 0 ||
(st.st_mode&S_IFMT) != S_IFREG) {
fprintf(stderr, "%s: not a plain file.", local);
goto bad;
}
}
if (initconn())
goto bad;
if (remote) {
if (command("%s %s", cmd, remote) != PRELIM)
goto bad;
} else
if (command("%s", cmd) != PRELIM)
goto bad;
dout = dataconn("w");
if (dout == NULL)
goto bad;
gettimeofday(&start, (struct timezone *)0);
switch (type) {
case TYPE_I:
case TYPE_L:
errno = d = 0;
while ((c = read(fileno (fin), buf, sizeof (buf))) > 0) {
if ((d = write(fileno (dout), buf, c)) < 0)
break;
bytes += c;
if (hash) {
putchar('#');
fflush(stdout);
}
}
wscnt = bytes;
if (hash && bytes > 0) {
putchar('\n');
fflush(stdout);
}
if (c < 0)
perror(local);
if (d < 0)
perror("netout");
break;
case TYPE_A:
while ((c = getc(fin)) != EOF) {
wscnt++;
if (c == '\n') {
while (hash && (bytes >= hashbytes)) {
putchar('#');
fflush(stdout);
hashbytes += sizeof (buf);
}
if (ferror(dout))
break;
putc('\r', dout);
bytes++;
}
putc(c, dout);
bytes++;
if (c == '\r') {
putc('\0', dout);
bytes++;
}
}
if (hash) {
if (bytes < hashbytes)
putchar('#');
putchar('\n');
fflush(stdout);
}
if (ferror(fin))
perror(local);
if (ferror(dout))
perror("netout");
break;
}
if (closefunc != NULL)
(*closefunc)(fin);
(void) fclose(dout);
(void) getreply(0);
done:
gettimeofday(&stop, (struct timezone *)0);
signal(SIGINT, oldintr);
if (bytes > 0 && verbose) {
if (fin != stdin)
printf("Actual size of %s is %d bytes\n", local, wscnt);
ptransfer("sent", bytes, &start, &stop);
}
return;
bad:
if (data >= 0)
(void) close(data), data = -1;
if (closefunc != NULL && fin != NULL)
(*closefunc)(fin);
goto done;
}
jmp_buf recvabort;
abortrecv()
{
longjmp(recvabort, 1);
}
recvrequest(cmd, local, remote, mode)
char *cmd, *local, *remote, *mode;
{
FILE *fout, *din, *popen();
int (*closefunc)(), pclose(), fclose(), (*oldintr)();
char buf[BUFSIZ];
long wrcnt = 0;
long bytes = 0, hashbytes = sizeof (buf);
register int c, d;
struct timeval start, stop;
din = NULL;
closefunc = NULL;
if (setjmp(recvabort))
goto bad;
oldintr = signal(SIGINT, abortrecv);
if (strcmp(local, "-") && *local != '|')
if (access(local, 2) < 0) {
if (errno == ENOENT) {
char *dir = rindex(local, '/');
if (dir != NULL)
*dir = 0;
if (access(dir ? local : ".", 2) < 0) {
perror(local);
goto bad;
}
if (dir != NULL)
*dir = '/';
}
else {
perror(local);
goto bad;
}
}
if (strcmp(local, "-") == 0)
fout = stdout;
else if (*local == '|') {
fout = popen(local + 1, "w");
closefunc = pclose;
} else {
fout = fopen(local, mode);
closefunc = fclose;
}
if (fout == NULL) {
if (*local == '|') perror(local + 1);
else perror(local);
goto bad;
}
if (initconn())
goto bad;
if (remote) {
if (command("%s %s", cmd, remote) != PRELIM)
goto bad;
} else
if (command("%s", cmd) != PRELIM)
goto bad;
din = dataconn("r");
if (din == NULL)
goto bad;
gettimeofday(&start, (struct timezone *)0);
switch (type) {
case TYPE_I:
case TYPE_L:
errno = d = 0;
while ((c = read(fileno(din), buf, sizeof (buf))) > 0) {
if ((d = write(fileno(fout), buf, c)) < 0)
break;
bytes += d;
if (hash) {
putchar('#');
fflush(stdout);
}
}
wrcnt = bytes;
if (hash && bytes > 0) {
putchar('\n');
fflush(stdout);
}
if (c < 0)
perror("netin");
if (d < 0)
perror(local);
break;
case TYPE_A:
while ((c = getc(din)) != EOF) {
bytes++;
if (c == '\r') {
while (hash && (bytes >= hashbytes)) {
putchar('#');
fflush(stdout);
hashbytes += sizeof (buf);
}
c = getc(din);
bytes++;
if (c != '\n') {
if (ferror (fout))
break;
putc ('\r', fout);
wrcnt++;
}
if (c == '\0') {
continue;
}
}
putc (c, fout);
wrcnt++;
}
if (hash) {
if (bytes < hashbytes)
putchar('#');
putchar('\n');
fflush(stdout);
}
if (ferror (din))
perror ("netin");
if (ferror (fout))
perror (local);
break;
}
(void) fclose(din);
if (closefunc != NULL)
(*closefunc)(fout);
(void) getreply(0);
done:
gettimeofday(&stop, (struct timezone *)0);
signal(SIGINT, oldintr);
if (bytes > 0 && verbose) {
ptransfer("received", bytes, &start, &stop);
if (fout != stdout)
printf("Actual size of %s is %d bytes\n", local, wrcnt);
}
return;
bad:
if (data >= 0) {
/*
/* if (din != NULL) {
/* printf("\nstart drainning..\n");
/* if (!empty(din))
/* for(;;) { c=getc(din);
/* if (c == EOF) break;
/* }
/* }
/* (void) getreply(0);
/**/
(void) close(data), data = -1;
}
if (closefunc != NULL && fout != NULL)
(*closefunc)(fout);
goto done;
}
/*
* Need to start a listen on the data channel
* before we send the command, otherwise the
* server's connect may fail.
*/
static int sendport = -1;
initconn()
{
register char *p, *a;
int result, len;
noport:
data_addr = myctladdr;
if (sendport)
data_addr.sin_port = 0; /* let system pick one */
if (data != -1)
(void) close (data);
data = socket(AF_INET, SOCK_STREAM, 0, 0);
if (data < 0) {
perror("ftp: socket");
return (1);
}
if (!sendport)
if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) {
perror("ftp: setsockopt (resuse address)");
goto bad;
}
if (bind(data, (char *)&data_addr, sizeof (data_addr), 0) < 0) {
perror("ftp: bind");
goto bad;
}
if (options & SO_DEBUG &&
setsockopt(data, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
perror("ftp: setsockopt (ignored)");
len = sizeof (data_addr);
if (getsockname(data, (char *)&data_addr, &len) < 0) {
perror("ftp: getsockname");
goto bad;
}
if (listen(data, 1) < 0) {
perror("ftp: listen");
goto bad;
}
if (sendport) {
a = (char *)&data_addr.sin_addr;
p = (char *)&data_addr.sin_port;
#define UC(b) (((int)b)&0xff)
result =
command("PORT %d,%d,%d,%d,%d,%d",
UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
UC(p[0]), UC(p[1]));
if (result == ERROR && sendport == -1) {
sendport = 0;
goto noport;
}
return (result != COMPLETE);
}
return (0);
bad:
(void) close(data), data = -1;
return (1);
}
FILE *
dataconn(mode)
char *mode;
{
struct sockaddr_in from;
int s, fromlen = sizeof (from);
s = accept(data, &from, &fromlen, 0);
if (s < 0) {
perror("ftp: accept");
(void) close(data), data = -1;
return (NULL);
}
(void) close(data);
data = s;
return (fdopen(data, mode));
}
ptransfer(direction, bytes, t0, t1)
char *direction;
long bytes;
struct timeval *t0, *t1;
{
struct timeval td;
long ms;
float bs;
tvsub(&td, t1, t0);
ms = (td.tv_sec * 1000) + (td.tv_usec / 1000);
#define nz(x) ((x) == 0 ? 1 : (x))
bs = ((bytes * NBBY * 1000) / (float) nz(ms)) / NBBY;
printf("%ld bytes %s in %d.%02d seconds (%.2g Kbytes/s)\n",
bytes, direction, td.tv_sec, td.tv_usec / 10000, bs / 1024.);
}
tvadd(tsum, t0)
struct timeval *tsum, *t0;
{
tsum->tv_sec += t0->tv_sec;
tsum->tv_usec += t0->tv_usec;
if (tsum->tv_usec > 1000000)
tsum->tv_sec++, tsum->tv_usec -= 1000000;
}
tvsub(tdiff, t1, t0)
struct timeval *tdiff, *t1, *t0;
{
tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
if (tdiff->tv_usec < 0)
tdiff->tv_sec--, tdiff->tv_usec += 1000000;
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.