xref: /csrg-svn/libexec/ftpd/ftpd.c (revision 34769)
122499Sdist /*
226044Sminshall  * Copyright (c) 1985 Regents of the University of California.
333738Sbostic  * All rights reserved.
433738Sbostic  *
533738Sbostic  * Redistribution and use in source and binary forms are permitted
6*34769Sbostic  * provided that the above copyright notice and this paragraph are
7*34769Sbostic  * duplicated in all such forms and that any documentation,
8*34769Sbostic  * advertising materials, and other materials related to such
9*34769Sbostic  * distribution and use acknowledge that the software was developed
10*34769Sbostic  * by the University of California, Berkeley.  The name of the
11*34769Sbostic  * University may not be used to endorse or promote products derived
12*34769Sbostic  * from this software without specific prior written permission.
13*34769Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*34769Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*34769Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1622499Sdist  */
1722499Sdist 
1810275Ssam #ifndef lint
1922499Sdist char copyright[] =
2026044Sminshall "@(#) Copyright (c) 1985 Regents of the University of California.\n\
2122499Sdist  All rights reserved.\n";
2233738Sbostic #endif /* not lint */
2310275Ssam 
2422499Sdist #ifndef lint
25*34769Sbostic static char sccsid[] = "@(#)ftpd.c	5.12 (Berkeley) 06/18/88";
2633738Sbostic #endif /* not lint */
2722499Sdist 
2810275Ssam /*
2910275Ssam  * FTP server.
3010275Ssam  */
3110303Ssam #include <sys/param.h>
3210275Ssam #include <sys/stat.h>
3310275Ssam #include <sys/ioctl.h>
3410275Ssam #include <sys/socket.h>
3513247Ssam #include <sys/file.h>
3613595Ssam #include <sys/wait.h>
3710275Ssam 
3810275Ssam #include <netinet/in.h>
3910275Ssam 
4013034Ssam #include <arpa/ftp.h>
4113211Sroot #include <arpa/inet.h>
4226044Sminshall #include <arpa/telnet.h>
4313034Ssam 
4410275Ssam #include <stdio.h>
4510275Ssam #include <signal.h>
4610275Ssam #include <pwd.h>
4710275Ssam #include <setjmp.h>
4810275Ssam #include <netdb.h>
4910423Ssam #include <errno.h>
5026044Sminshall #include <strings.h>
5126493Sminshall #include <syslog.h>
5210275Ssam 
5310695Ssam /*
5410695Ssam  * File containing login names
5510695Ssam  * NOT to be used on this machine.
5610695Ssam  * Commonly used to disallow uucp.
5710695Ssam  */
5810695Ssam #define	FTPUSERS	"/etc/ftpusers"
5910695Ssam 
6010275Ssam extern	int errno;
6110275Ssam extern	char *sys_errlist[];
6210275Ssam extern	char *crypt();
6310275Ssam extern	char version[];
6410275Ssam extern	char *home;		/* pointer to home directory for glob */
6526044Sminshall extern	FILE *popen(), *fopen(), *freopen();
6626493Sminshall extern	int  pclose(), fclose();
6726044Sminshall extern	char *getline();
6826044Sminshall extern	char cbuf[];
6910275Ssam 
7010275Ssam struct	sockaddr_in ctrl_addr;
7110275Ssam struct	sockaddr_in data_source;
7210275Ssam struct	sockaddr_in data_dest;
7310275Ssam struct	sockaddr_in his_addr;
7410275Ssam 
7510275Ssam int	data;
7626044Sminshall jmp_buf	errcatch, urgcatch;
7710275Ssam int	logged_in;
7810275Ssam struct	passwd *pw;
7910275Ssam int	debug;
8026493Sminshall int	timeout = 900;    /* timeout after 15 minutes of inactivity */
8111757Ssam int	logging;
8210275Ssam int	guest;
8316033Sralph int	wtmp;
8410275Ssam int	type;
8510275Ssam int	form;
8610275Ssam int	stru;			/* avoid C keyword */
8710275Ssam int	mode;
8810321Ssam int	usedefault = 1;		/* for data transfers */
8926044Sminshall int	pdata;			/* for passive mode */
9026044Sminshall int	unique;
9126044Sminshall int	transflag;
9226044Sminshall char	tmpline[7];
9310275Ssam char	hostname[32];
9413247Ssam char	remotehost[32];
9510275Ssam 
9611653Ssam /*
9711653Ssam  * Timeout intervals for retrying connections
9811653Ssam  * to hosts that don't accept PORT cmds.  This
9911653Ssam  * is a kludge, but given the problems with TCP...
10011653Ssam  */
10111653Ssam #define	SWAITMAX	90	/* wait at most 90 seconds */
10211653Ssam #define	SWAITINT	5	/* interval between retries */
10311653Ssam 
10411653Ssam int	swaitmax = SWAITMAX;
10511653Ssam int	swaitint = SWAITINT;
10611653Ssam 
10710275Ssam int	lostconn();
10826044Sminshall int	myoob();
10910275Ssam FILE	*getdatasock(), *dataconn();
11010275Ssam 
11110275Ssam main(argc, argv)
11210275Ssam 	int argc;
11310275Ssam 	char *argv[];
11410275Ssam {
11527750Sminshall 	int addrlen, on = 1;
11626044Sminshall 	long pgid;
11710275Ssam 	char *cp;
11810275Ssam 
11916339Skarels 	addrlen = sizeof (his_addr);
12016339Skarels 	if (getpeername(0, &his_addr, &addrlen) < 0) {
12126493Sminshall 		syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
12210275Ssam 		exit(1);
12310275Ssam 	}
12416339Skarels 	addrlen = sizeof (ctrl_addr);
12526493Sminshall 	if (getsockname(0, (char *) &ctrl_addr, &addrlen) < 0) {
12626493Sminshall 		syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
12716339Skarels 		exit(1);
12816339Skarels 	}
12916339Skarels 	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
13010275Ssam 	debug = 0;
13126493Sminshall 	openlog("ftpd", LOG_PID, LOG_DAEMON);
13210275Ssam 	argc--, argv++;
13310275Ssam 	while (argc > 0 && *argv[0] == '-') {
13410275Ssam 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
13510275Ssam 
13611653Ssam 		case 'v':
13711653Ssam 			debug = 1;
13811653Ssam 			break;
13911653Ssam 
14010275Ssam 		case 'd':
14110275Ssam 			debug = 1;
14210275Ssam 			break;
14310275Ssam 
14411757Ssam 		case 'l':
14511757Ssam 			logging = 1;
14611757Ssam 			break;
14711757Ssam 
14811653Ssam 		case 't':
14911653Ssam 			timeout = atoi(++cp);
15011653Ssam 			goto nextopt;
15111653Ssam 			break;
15211653Ssam 
15310275Ssam 		default:
15416339Skarels 			fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
15516339Skarels 			     *cp);
15610275Ssam 			break;
15710275Ssam 		}
15811653Ssam nextopt:
15910275Ssam 		argc--, argv++;
16010275Ssam 	}
16130944Scsvsj 	(void) freopen("/dev/null", "w", stderr);
16226493Sminshall 	(void) signal(SIGPIPE, lostconn);
16326493Sminshall 	(void) signal(SIGCHLD, SIG_IGN);
16426044Sminshall 	if (signal(SIGURG, myoob) < 0) {
16526493Sminshall 		syslog(LOG_ERR, "signal: %m");
16626044Sminshall 	}
16727750Sminshall 	/* handle urgent data inline */
16827750Sminshall #ifdef SO_OOBINLINE
16927750Sminshall 	if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) {
17027750Sminshall 		syslog(LOG_ERR, "setsockopt: %m");
17127750Sminshall 	}
17227750Sminshall #endif SO_OOBINLINE
17326044Sminshall 	pgid = getpid();
17427750Sminshall 	if (ioctl(fileno(stdin), SIOCSPGRP, (char *) &pgid) < 0) {
17526493Sminshall 		syslog(LOG_ERR, "ioctl: %m");
17626044Sminshall 	}
17716760Slepreau 	dolog(&his_addr);
17816339Skarels 	/* do telnet option negotiation here */
17916339Skarels 	/*
18016339Skarels 	 * Set up default state
18116339Skarels 	 */
18216339Skarels 	logged_in = 0;
18316339Skarels 	data = -1;
18416339Skarels 	type = TYPE_A;
18516339Skarels 	form = FORM_N;
18616339Skarels 	stru = STRU_F;
18716339Skarels 	mode = MODE_S;
18826044Sminshall 	tmpline[0] = '\0';
18926493Sminshall 	(void) gethostname(hostname, sizeof (hostname));
19016339Skarels 	reply(220, "%s FTP server (%s) ready.",
19116339Skarels 		hostname, version);
19210275Ssam 	for (;;) {
19326493Sminshall 		(void) setjmp(errcatch);
19426493Sminshall 		(void) yyparse();
19510275Ssam 	}
19610275Ssam }
19710419Ssam 
19810275Ssam lostconn()
19910275Ssam {
20010275Ssam 
20114089Ssam 	if (debug)
20226493Sminshall 		syslog(LOG_DEBUG, "lost connection");
20314089Ssam 	dologout(-1);
20410275Ssam }
20510275Ssam 
20610275Ssam pass(passwd)
20710275Ssam 	char *passwd;
20810275Ssam {
20910303Ssam 	char *xpasswd, *savestr();
21010303Ssam 	static struct passwd save;
21110275Ssam 
21210275Ssam 	if (logged_in || pw == NULL) {
21310275Ssam 		reply(503, "Login with USER first.");
21410275Ssam 		return;
21510275Ssam 	}
21610275Ssam 	if (!guest) {		/* "ftp" is only account allowed no password */
21710275Ssam 		xpasswd = crypt(passwd, pw->pw_passwd);
21816760Slepreau 		/* The strcmp does not catch null passwords! */
21916760Slepreau 		if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) {
22010275Ssam 			reply(530, "Login incorrect.");
22110275Ssam 			pw = NULL;
22210275Ssam 			return;
22310275Ssam 		}
22410275Ssam 	}
22510303Ssam 	setegid(pw->pw_gid);
22610275Ssam 	initgroups(pw->pw_name, pw->pw_gid);
22710275Ssam 	if (chdir(pw->pw_dir)) {
22827106Smckusick 		reply(530, "User %s: can't change directory to %s.",
22910275Ssam 			pw->pw_name, pw->pw_dir);
23010303Ssam 		goto bad;
23110275Ssam 	}
23216033Sralph 
23316760Slepreau 	/* grab wtmp before chroot */
23416760Slepreau 	wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
23510303Ssam 	if (guest && chroot(pw->pw_dir) < 0) {
23610275Ssam 		reply(550, "Can't set guest privileges.");
23716760Slepreau 		if (wtmp >= 0) {
23816760Slepreau 			(void) close(wtmp);
23916760Slepreau 			wtmp = -1;
24016760Slepreau 		}
24110303Ssam 		goto bad;
24210275Ssam 	}
24310275Ssam 	if (!guest)
24410275Ssam 		reply(230, "User %s logged in.", pw->pw_name);
24510275Ssam 	else
24610275Ssam 		reply(230, "Guest login ok, access restrictions apply.");
24710275Ssam 	logged_in = 1;
24813247Ssam 	dologin(pw);
24910303Ssam 	seteuid(pw->pw_uid);
25010303Ssam 	/*
25110303Ssam 	 * Save everything so globbing doesn't
25210303Ssam 	 * clobber the fields.
25310303Ssam 	 */
25410303Ssam 	save = *pw;
25510303Ssam 	save.pw_name = savestr(pw->pw_name);
25610303Ssam 	save.pw_passwd = savestr(pw->pw_passwd);
25710303Ssam 	save.pw_comment = savestr(pw->pw_comment);
25826493Sminshall 	save.pw_gecos = savestr(pw->pw_gecos);
25910303Ssam 	save.pw_dir = savestr(pw->pw_dir);
26010303Ssam 	save.pw_shell = savestr(pw->pw_shell);
26110303Ssam 	pw = &save;
26210303Ssam 	home = pw->pw_dir;		/* home dir for globbing */
26310303Ssam 	return;
26410303Ssam bad:
26510303Ssam 	seteuid(0);
26610303Ssam 	pw = NULL;
26710275Ssam }
26810275Ssam 
26910303Ssam char *
27010303Ssam savestr(s)
27110303Ssam 	char *s;
27210303Ssam {
27310303Ssam 	char *malloc();
27426493Sminshall 	char *new = malloc((unsigned) strlen(s) + 1);
27510303Ssam 
27610303Ssam 	if (new != NULL)
27726493Sminshall 		(void) strcpy(new, s);
27811347Ssam 	return (new);
27910303Ssam }
28010303Ssam 
28110275Ssam retrieve(cmd, name)
28210275Ssam 	char *cmd, *name;
28310275Ssam {
28410275Ssam 	FILE *fin, *dout;
28510275Ssam 	struct stat st;
28626044Sminshall 	int (*closefunc)(), tmp;
28710275Ssam 
28810275Ssam 	if (cmd == 0) {
28910317Ssam #ifdef notdef
29010317Ssam 		/* no remote command execution -- it's a security hole */
29111653Ssam 		if (*name == '|')
29210275Ssam 			fin = popen(name + 1, "r"), closefunc = pclose;
29310275Ssam 		else
29410317Ssam #endif
29510275Ssam 			fin = fopen(name, "r"), closefunc = fclose;
29610275Ssam 	} else {
29710275Ssam 		char line[BUFSIZ];
29810275Ssam 
29926493Sminshall 		(void) sprintf(line, cmd, name), name = line;
30010275Ssam 		fin = popen(line, "r"), closefunc = pclose;
30110275Ssam 	}
30210275Ssam 	if (fin == NULL) {
30313152Ssam 		if (errno != 0)
30413152Ssam 			reply(550, "%s: %s.", name, sys_errlist[errno]);
30510275Ssam 		return;
30610275Ssam 	}
30710275Ssam 	st.st_size = 0;
30810275Ssam 	if (cmd == 0 &&
30910275Ssam 	    (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
31010275Ssam 		reply(550, "%s: not a plain file.", name);
31110275Ssam 		goto done;
31210275Ssam 	}
31310275Ssam 	dout = dataconn(name, st.st_size, "w");
31410275Ssam 	if (dout == NULL)
31510275Ssam 		goto done;
31626044Sminshall 	if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) {
31710275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
31826044Sminshall 	}
31926044Sminshall 	else if (tmp == 0) {
32010275Ssam 		reply(226, "Transfer complete.");
32126044Sminshall 	}
32226493Sminshall 	(void) fclose(dout);
32326044Sminshall 	data = -1;
32426044Sminshall 	pdata = -1;
32510275Ssam done:
32610275Ssam 	(*closefunc)(fin);
32710275Ssam }
32810275Ssam 
32910275Ssam store(name, mode)
33010275Ssam 	char *name, *mode;
33110275Ssam {
33210275Ssam 	FILE *fout, *din;
33326044Sminshall 	int (*closefunc)(), dochown = 0, tmp;
33426044Sminshall 	char *gunique(), *local;
33510275Ssam 
33610317Ssam #ifdef notdef
33710317Ssam 	/* no remote command execution -- it's a security hole */
33811653Ssam 	if (name[0] == '|')
33910275Ssam 		fout = popen(&name[1], "w"), closefunc = pclose;
34010317Ssam 	else
34110317Ssam #endif
34210317Ssam 	{
34310303Ssam 		struct stat st;
34410303Ssam 
34526044Sminshall 		local = name;
34626044Sminshall 		if (stat(name, &st) < 0) {
34710303Ssam 			dochown++;
34826044Sminshall 		}
34926044Sminshall 		else if (unique) {
35026044Sminshall 			if ((local = gunique(name)) == NULL) {
35126044Sminshall 				return;
35226044Sminshall 			}
35326044Sminshall 			dochown++;
35426044Sminshall 		}
35526044Sminshall 		fout = fopen(local, mode), closefunc = fclose;
35610303Ssam 	}
35710275Ssam 	if (fout == NULL) {
35827106Smckusick 		reply(553, "%s: %s.", local, sys_errlist[errno]);
35910275Ssam 		return;
36010275Ssam 	}
36126044Sminshall 	din = dataconn(local, (off_t)-1, "r");
36210275Ssam 	if (din == NULL)
36310275Ssam 		goto done;
36426044Sminshall 	if ((tmp = receive_data(din, fout)) > 0 || ferror(fout) > 0) {
36527106Smckusick 		reply(552, "%s: %s.", local, sys_errlist[errno]);
36626044Sminshall 	}
36726044Sminshall 	else if (tmp == 0 && !unique) {
36810275Ssam 		reply(226, "Transfer complete.");
36926044Sminshall 	}
37026044Sminshall 	else if (tmp == 0 && unique) {
37126044Sminshall 		reply(226, "Transfer complete (unique file name:%s).", local);
37226044Sminshall 	}
37326493Sminshall 	(void) fclose(din);
37426044Sminshall 	data = -1;
37526044Sminshall 	pdata = -1;
37610275Ssam done:
37710303Ssam 	if (dochown)
37826044Sminshall 		(void) chown(local, pw->pw_uid, -1);
37910275Ssam 	(*closefunc)(fout);
38010275Ssam }
38110275Ssam 
38210275Ssam FILE *
38310275Ssam getdatasock(mode)
38410275Ssam 	char *mode;
38510275Ssam {
38617157Ssam 	int s, on = 1;
38710275Ssam 
38810275Ssam 	if (data >= 0)
38910275Ssam 		return (fdopen(data, mode));
39013247Ssam 	s = socket(AF_INET, SOCK_STREAM, 0);
39110602Ssam 	if (s < 0)
39210275Ssam 		return (NULL);
39310275Ssam 	seteuid(0);
39426493Sminshall 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0)
39510602Ssam 		goto bad;
39613152Ssam 	/* anchor socket to avoid multi-homing problems */
39713152Ssam 	data_source.sin_family = AF_INET;
39813152Ssam 	data_source.sin_addr = ctrl_addr.sin_addr;
39926493Sminshall 	if (bind(s, &data_source, sizeof (data_source)) < 0)
40010602Ssam 		goto bad;
40110311Ssam 	seteuid(pw->pw_uid);
40210275Ssam 	return (fdopen(s, mode));
40310602Ssam bad:
40410602Ssam 	seteuid(pw->pw_uid);
40526493Sminshall 	(void) close(s);
40610602Ssam 	return (NULL);
40710275Ssam }
40810275Ssam 
40910275Ssam FILE *
41010275Ssam dataconn(name, size, mode)
41110275Ssam 	char *name;
41211653Ssam 	off_t size;
41310275Ssam 	char *mode;
41410275Ssam {
41510275Ssam 	char sizebuf[32];
41610275Ssam 	FILE *file;
41711653Ssam 	int retry = 0;
41810275Ssam 
41910275Ssam 	if (size >= 0)
42026493Sminshall 		(void) sprintf (sizebuf, " (%ld bytes)", size);
42110275Ssam 	else
42210275Ssam 		(void) strcpy(sizebuf, "");
42326044Sminshall 	if (pdata > 0) {
42426044Sminshall 		struct sockaddr_in from;
42526044Sminshall 		int s, fromlen = sizeof(from);
42626044Sminshall 
42726493Sminshall 		s = accept(pdata, &from, &fromlen);
42826044Sminshall 		if (s < 0) {
42926044Sminshall 			reply(425, "Can't open data connection.");
43026044Sminshall 			(void) close(pdata);
43126044Sminshall 			pdata = -1;
43226044Sminshall 			return(NULL);
43326044Sminshall 		}
43426044Sminshall 		(void) close(pdata);
43526044Sminshall 		pdata = s;
43626044Sminshall 		reply(150, "Openning data connection for %s (%s,%d)%s.",
43726493Sminshall 		     name, inet_ntoa(from.sin_addr),
43826044Sminshall 		     ntohs(from.sin_port), sizebuf);
43926044Sminshall 		return(fdopen(pdata, mode));
44026044Sminshall 	}
44110275Ssam 	if (data >= 0) {
44210275Ssam 		reply(125, "Using existing data connection for %s%s.",
44310275Ssam 		    name, sizebuf);
44410321Ssam 		usedefault = 1;
44510275Ssam 		return (fdopen(data, mode));
44610275Ssam 	}
44710566Ssam 	if (usedefault)
44810422Ssam 		data_dest = his_addr;
44910422Ssam 	usedefault = 1;
45010275Ssam 	file = getdatasock(mode);
45110275Ssam 	if (file == NULL) {
45210275Ssam 		reply(425, "Can't create data socket (%s,%d): %s.",
45313247Ssam 		    inet_ntoa(data_source.sin_addr),
45410275Ssam 		    ntohs(data_source.sin_port),
45510275Ssam 		    sys_errlist[errno]);
45610275Ssam 		return (NULL);
45710275Ssam 	}
45810275Ssam 	data = fileno(file);
45926044Sminshall 	while (connect(data, &data_dest, sizeof (data_dest)) < 0) {
46011653Ssam 		if (errno == EADDRINUSE && retry < swaitmax) {
46126493Sminshall 			sleep((unsigned) swaitint);
46211653Ssam 			retry += swaitint;
46311653Ssam 			continue;
46411653Ssam 		}
46510275Ssam 		reply(425, "Can't build data connection: %s.",
46610275Ssam 		    sys_errlist[errno]);
46710275Ssam 		(void) fclose(file);
46810275Ssam 		data = -1;
46910275Ssam 		return (NULL);
47010275Ssam 	}
47127750Sminshall 	reply(150, "Opening data connection for %s (%s,%d)%s.",
47227750Sminshall 	    name, inet_ntoa(data_dest.sin_addr),
47327750Sminshall 	    ntohs(data_dest.sin_port), sizebuf);
47410275Ssam 	return (file);
47510275Ssam }
47610275Ssam 
47710275Ssam /*
47810275Ssam  * Tranfer the contents of "instr" to
47910275Ssam  * "outstr" peer using the appropriate
48010275Ssam  * encapulation of the date subject
48110275Ssam  * to Mode, Structure, and Type.
48210275Ssam  *
48310275Ssam  * NB: Form isn't handled.
48410275Ssam  */
48510275Ssam send_data(instr, outstr)
48610275Ssam 	FILE *instr, *outstr;
48710275Ssam {
48810275Ssam 	register int c;
48910275Ssam 	int netfd, filefd, cnt;
49010275Ssam 	char buf[BUFSIZ];
49110275Ssam 
49226044Sminshall 	transflag++;
49326044Sminshall 	if (setjmp(urgcatch)) {
49426044Sminshall 		transflag = 0;
49526044Sminshall 		return(-1);
49626044Sminshall 	}
49710275Ssam 	switch (type) {
49810275Ssam 
49910275Ssam 	case TYPE_A:
50010275Ssam 		while ((c = getc(instr)) != EOF) {
50111220Ssam 			if (c == '\n') {
50226044Sminshall 				if (ferror (outstr)) {
50326044Sminshall 					transflag = 0;
50411220Ssam 					return (1);
50526044Sminshall 				}
50627750Sminshall 				(void) putc('\r', outstr);
50711220Ssam 			}
50827750Sminshall 			(void) putc(c, outstr);
50926044Sminshall 		/*	if (c == '\r')			*/
51026044Sminshall 		/*		putc ('\0', outstr);	*/
51110275Ssam 		}
51226044Sminshall 		transflag = 0;
51326044Sminshall 		if (ferror (instr) || ferror (outstr)) {
51411220Ssam 			return (1);
51526044Sminshall 		}
51610275Ssam 		return (0);
51710275Ssam 
51810275Ssam 	case TYPE_I:
51910275Ssam 	case TYPE_L:
52010275Ssam 		netfd = fileno(outstr);
52110275Ssam 		filefd = fileno(instr);
52210275Ssam 
52326044Sminshall 		while ((cnt = read(filefd, buf, sizeof (buf))) > 0) {
52426044Sminshall 			if (write(netfd, buf, cnt) < 0) {
52526044Sminshall 				transflag = 0;
52610275Ssam 				return (1);
52726044Sminshall 			}
52826044Sminshall 		}
52926044Sminshall 		transflag = 0;
53010275Ssam 		return (cnt < 0);
53110275Ssam 	}
53227106Smckusick 	reply(550, "Unimplemented TYPE %d in send_data", type);
53326044Sminshall 	transflag = 0;
53427106Smckusick 	return (-1);
53510275Ssam }
53610275Ssam 
53710275Ssam /*
53810275Ssam  * Transfer data from peer to
53910275Ssam  * "outstr" using the appropriate
54010275Ssam  * encapulation of the data subject
54110275Ssam  * to Mode, Structure, and Type.
54210275Ssam  *
54310275Ssam  * N.B.: Form isn't handled.
54410275Ssam  */
54510275Ssam receive_data(instr, outstr)
54610275Ssam 	FILE *instr, *outstr;
54710275Ssam {
54810275Ssam 	register int c;
54911220Ssam 	int cnt;
55010275Ssam 	char buf[BUFSIZ];
55110275Ssam 
55210275Ssam 
55326044Sminshall 	transflag++;
55426044Sminshall 	if (setjmp(urgcatch)) {
55526044Sminshall 		transflag = 0;
55626044Sminshall 		return(-1);
55726044Sminshall 	}
55810275Ssam 	switch (type) {
55910275Ssam 
56010275Ssam 	case TYPE_I:
56110275Ssam 	case TYPE_L:
56226044Sminshall 		while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
56326044Sminshall 			if (write(fileno(outstr), buf, cnt) < 0) {
56426044Sminshall 				transflag = 0;
56510275Ssam 				return (1);
56626044Sminshall 			}
56726044Sminshall 		}
56826044Sminshall 		transflag = 0;
56910275Ssam 		return (cnt < 0);
57010275Ssam 
57110275Ssam 	case TYPE_E:
57227106Smckusick 		reply(553, "TYPE E not implemented.");
57326044Sminshall 		transflag = 0;
57427106Smckusick 		return (-1);
57510275Ssam 
57610275Ssam 	case TYPE_A:
57710275Ssam 		while ((c = getc(instr)) != EOF) {
57827750Sminshall 			while (c == '\r') {
57926044Sminshall 				if (ferror (outstr)) {
58026044Sminshall 					transflag = 0;
58111220Ssam 					return (1);
58226044Sminshall 				}
58311220Ssam 				if ((c = getc(instr)) != '\n')
58427750Sminshall 					(void) putc ('\r', outstr);
58526044Sminshall 			/*	if (c == '\0')			*/
58626044Sminshall 			/*		continue;		*/
58710275Ssam 			}
58827750Sminshall 			(void) putc (c, outstr);
58910275Ssam 		}
59026044Sminshall 		transflag = 0;
59111220Ssam 		if (ferror (instr) || ferror (outstr))
59211220Ssam 			return (1);
59310275Ssam 		return (0);
59410275Ssam 	}
59526044Sminshall 	transflag = 0;
59610275Ssam 	fatal("Unknown type in receive_data.");
59710275Ssam 	/*NOTREACHED*/
59810275Ssam }
59910275Ssam 
60010275Ssam fatal(s)
60110275Ssam 	char *s;
60210275Ssam {
60310275Ssam 	reply(451, "Error in server: %s\n", s);
60410275Ssam 	reply(221, "Closing connection due to server error.");
60513247Ssam 	dologout(0);
60610275Ssam }
60710275Ssam 
60832110Smckusick reply(n, s, p0, p1, p2, p3, p4)
60910275Ssam 	int n;
61010275Ssam 	char *s;
61110275Ssam {
61210275Ssam 
61310275Ssam 	printf("%d ", n);
61432110Smckusick 	printf(s, p0, p1, p2, p3, p4);
61510275Ssam 	printf("\r\n");
61626493Sminshall 	(void) fflush(stdout);
61710275Ssam 	if (debug) {
61826493Sminshall 		syslog(LOG_DEBUG, "<--- %d ", n);
61932110Smckusick 		syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4);
62010275Ssam 	}
62110275Ssam }
62210275Ssam 
62332110Smckusick lreply(n, s, p0, p1, p2, p3, p4)
62410275Ssam 	int n;
62510275Ssam 	char *s;
62610275Ssam {
62710275Ssam 	printf("%d-", n);
62832110Smckusick 	printf(s, p0, p1, p2, p3, p4);
62910275Ssam 	printf("\r\n");
63026493Sminshall 	(void) fflush(stdout);
63110275Ssam 	if (debug) {
63226493Sminshall 		syslog(LOG_DEBUG, "<--- %d- ", n);
63332110Smckusick 		syslog(LOG_DEBUG, s, p0, p1, p2, p3, p4);
63410275Ssam 	}
63510275Ssam }
63610275Ssam 
63710275Ssam ack(s)
63810275Ssam 	char *s;
63910275Ssam {
64027106Smckusick 	reply(250, "%s command successful.", s);
64110275Ssam }
64210275Ssam 
64310275Ssam nack(s)
64410275Ssam 	char *s;
64510275Ssam {
64610275Ssam 	reply(502, "%s command not implemented.", s);
64710275Ssam }
64810275Ssam 
64926493Sminshall yyerror(s)
65026493Sminshall 	char *s;
65110275Ssam {
65226044Sminshall 	char *cp;
65326044Sminshall 
65426044Sminshall 	cp = index(cbuf,'\n');
65526044Sminshall 	*cp = '\0';
65626044Sminshall 	reply(500, "'%s': command not understood.",cbuf);
65710275Ssam }
65810275Ssam 
65910275Ssam delete(name)
66010275Ssam 	char *name;
66110275Ssam {
66210275Ssam 	struct stat st;
66310275Ssam 
66410275Ssam 	if (stat(name, &st) < 0) {
66510275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
66610275Ssam 		return;
66710275Ssam 	}
66810275Ssam 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
66910275Ssam 		if (rmdir(name) < 0) {
67010275Ssam 			reply(550, "%s: %s.", name, sys_errlist[errno]);
67110275Ssam 			return;
67210275Ssam 		}
67310275Ssam 		goto done;
67410275Ssam 	}
67510275Ssam 	if (unlink(name) < 0) {
67610275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
67710275Ssam 		return;
67810275Ssam 	}
67910275Ssam done:
68010275Ssam 	ack("DELE");
68110275Ssam }
68210275Ssam 
68310275Ssam cwd(path)
68410275Ssam 	char *path;
68510275Ssam {
68610275Ssam 
68710275Ssam 	if (chdir(path) < 0) {
68810275Ssam 		reply(550, "%s: %s.", path, sys_errlist[errno]);
68910275Ssam 		return;
69010275Ssam 	}
69110275Ssam 	ack("CWD");
69210275Ssam }
69310275Ssam 
69410303Ssam makedir(name)
69510275Ssam 	char *name;
69610275Ssam {
69710303Ssam 	struct stat st;
69810303Ssam 	int dochown = stat(name, &st) < 0;
69910275Ssam 
70010275Ssam 	if (mkdir(name, 0777) < 0) {
70110275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
70210275Ssam 		return;
70310275Ssam 	}
70410303Ssam 	if (dochown)
70510303Ssam 		(void) chown(name, pw->pw_uid, -1);
70627106Smckusick 	reply(257, "MKD command successful.");
70710275Ssam }
70810275Ssam 
70910303Ssam removedir(name)
71010275Ssam 	char *name;
71110275Ssam {
71210275Ssam 
71310275Ssam 	if (rmdir(name) < 0) {
71410275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
71510275Ssam 		return;
71610275Ssam 	}
71727106Smckusick 	ack("RMD");
71810275Ssam }
71910275Ssam 
72010303Ssam pwd()
72110275Ssam {
72210303Ssam 	char path[MAXPATHLEN + 1];
72310275Ssam 
72410275Ssam 	if (getwd(path) == NULL) {
72527106Smckusick 		reply(550, "%s.", path);
72610275Ssam 		return;
72710275Ssam 	}
72827106Smckusick 	reply(257, "\"%s\" is current directory.", path);
72910275Ssam }
73010275Ssam 
73110275Ssam char *
73210275Ssam renamefrom(name)
73310275Ssam 	char *name;
73410275Ssam {
73510275Ssam 	struct stat st;
73610275Ssam 
73710275Ssam 	if (stat(name, &st) < 0) {
73810275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
73910275Ssam 		return ((char *)0);
74010275Ssam 	}
74110303Ssam 	reply(350, "File exists, ready for destination name");
74210275Ssam 	return (name);
74310275Ssam }
74410275Ssam 
74510275Ssam renamecmd(from, to)
74610275Ssam 	char *from, *to;
74710275Ssam {
74810275Ssam 
74910275Ssam 	if (rename(from, to) < 0) {
75010275Ssam 		reply(550, "rename: %s.", sys_errlist[errno]);
75110275Ssam 		return;
75210275Ssam 	}
75310275Ssam 	ack("RNTO");
75410275Ssam }
75510275Ssam 
75610275Ssam dolog(sin)
75710275Ssam 	struct sockaddr_in *sin;
75810275Ssam {
75910275Ssam 	struct hostent *hp = gethostbyaddr(&sin->sin_addr,
76010275Ssam 		sizeof (struct in_addr), AF_INET);
76110275Ssam 	time_t t;
76226493Sminshall 	extern char *ctime();
76310275Ssam 
76413247Ssam 	if (hp) {
76526493Sminshall 		(void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
76613247Ssam 		endhostent();
76713247Ssam 	} else
76826493Sminshall 		(void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
76913247Ssam 		    sizeof (remotehost));
77013247Ssam 	if (!logging)
77113247Ssam 		return;
77226493Sminshall 	t = time((time_t *) 0);
77326493Sminshall 	syslog(LOG_INFO,"FTPD: connection from %s at %s", remotehost, ctime(&t));
77410275Ssam }
77510695Ssam 
77613247Ssam #include <utmp.h>
77713247Ssam 
77826493Sminshall #define	SCPYN(a, b)	(void) strncpy(a, b, sizeof (a))
77913247Ssam struct	utmp utmp;
78013247Ssam 
78110695Ssam /*
78213247Ssam  * Record login in wtmp file.
78313247Ssam  */
78413247Ssam dologin(pw)
78513247Ssam 	struct passwd *pw;
78613247Ssam {
78713247Ssam 	char line[32];
78813247Ssam 
78913247Ssam 	if (wtmp >= 0) {
79013247Ssam 		/* hack, but must be unique and no tty line */
79126493Sminshall 		(void) sprintf(line, "ftp%d", getpid());
79213247Ssam 		SCPYN(utmp.ut_line, line);
79313247Ssam 		SCPYN(utmp.ut_name, pw->pw_name);
79413247Ssam 		SCPYN(utmp.ut_host, remotehost);
79526493Sminshall 		utmp.ut_time = (long) time((time_t *) 0);
79613247Ssam 		(void) write(wtmp, (char *)&utmp, sizeof (utmp));
79716760Slepreau 		if (!guest) {		/* anon must hang on */
79816760Slepreau 			(void) close(wtmp);
79916760Slepreau 			wtmp = -1;
80016760Slepreau 		}
80113247Ssam 	}
80213247Ssam }
80313247Ssam 
80413247Ssam /*
80513247Ssam  * Record logout in wtmp file
80613247Ssam  * and exit with supplied status.
80713247Ssam  */
80813247Ssam dologout(status)
80913247Ssam 	int status;
81013247Ssam {
81116339Skarels 
81217580Ssam 	if (logged_in) {
81317580Ssam 		(void) seteuid(0);
81417580Ssam 		if (wtmp < 0)
81517580Ssam 			wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
81617580Ssam 		if (wtmp >= 0) {
81717580Ssam 			SCPYN(utmp.ut_name, "");
81817580Ssam 			SCPYN(utmp.ut_host, "");
81926493Sminshall 			utmp.ut_time = (long) time((time_t *) 0);
82017580Ssam 			(void) write(wtmp, (char *)&utmp, sizeof (utmp));
82117580Ssam 			(void) close(wtmp);
82217580Ssam 		}
82313247Ssam 	}
82414436Ssam 	/* beware of flushing buffers after a SIGPIPE */
82514436Ssam 	_exit(status);
82613247Ssam }
82713247Ssam 
82813247Ssam /*
82910695Ssam  * Special version of popen which avoids
83010695Ssam  * call to shell.  This insures noone may
83110695Ssam  * create a pipe to a hidden program as a side
83210695Ssam  * effect of a list or dir command.
83310695Ssam  */
83410695Ssam #define	tst(a,b)	(*mode == 'r'? (b) : (a))
83510695Ssam #define	RDR	0
83610695Ssam #define	WTR	1
83710695Ssam static	int popen_pid[5];
83810695Ssam 
83910695Ssam static char *
84010695Ssam nextarg(cpp)
84110695Ssam 	char *cpp;
84210695Ssam {
84310695Ssam 	register char *cp = cpp;
84410695Ssam 
84510695Ssam 	if (cp == 0)
84610695Ssam 		return (cp);
84710695Ssam 	while (*cp && *cp != ' ' && *cp != '\t')
84810695Ssam 		cp++;
84910695Ssam 	if (*cp == ' ' || *cp == '\t') {
85010695Ssam 		*cp++ = '\0';
85110695Ssam 		while (*cp == ' ' || *cp == '\t')
85210695Ssam 			cp++;
85310695Ssam 	}
85410695Ssam 	if (cp == cpp)
85510695Ssam 		return ((char *)0);
85610695Ssam 	return (cp);
85710695Ssam }
85810695Ssam 
85910695Ssam FILE *
86010695Ssam popen(cmd, mode)
86110695Ssam 	char *cmd, *mode;
86210695Ssam {
86313211Sroot 	int p[2], ac, gac;
86410695Ssam 	register myside, hisside, pid;
86513211Sroot 	char *av[20], *gav[512];
86610695Ssam 	register char *cp;
86710695Ssam 
86810695Ssam 	if (pipe(p) < 0)
86910695Ssam 		return (NULL);
87010695Ssam 	cp = cmd, ac = 0;
87113211Sroot 	/* break up string into pieces */
87210695Ssam 	do {
87310695Ssam 		av[ac++] = cp;
87410695Ssam 		cp = nextarg(cp);
87513211Sroot 	} while (cp && *cp && ac < 20);
87610695Ssam 	av[ac] = (char *)0;
87713211Sroot 	gav[0] = av[0];
87813211Sroot 	/* glob each piece */
87913211Sroot 	for (gac = ac = 1; av[ac] != NULL; ac++) {
88013211Sroot 		char **pop;
88122024Ssam 		extern char **glob(), **copyblk();
88213211Sroot 
88313211Sroot 		pop = glob(av[ac]);
88422024Ssam 		if (pop == (char **)NULL) {	/* globbing failed */
88522024Ssam 			char *vv[2];
88622024Ssam 
88722024Ssam 			vv[0] = av[ac];
88822024Ssam 			vv[1] = 0;
88922024Ssam 			pop = copyblk(vv);
89013211Sroot 		}
89122024Ssam 		av[ac] = (char *)pop;		/* save to free later */
89222024Ssam 		while (*pop && gac < 512)
89322024Ssam 			gav[gac++] = *pop++;
89411757Ssam 	}
89513211Sroot 	gav[gac] = (char *)0;
89610695Ssam 	myside = tst(p[WTR], p[RDR]);
89710695Ssam 	hisside = tst(p[RDR], p[WTR]);
89810695Ssam 	if ((pid = fork()) == 0) {
89910695Ssam 		/* myside and hisside reverse roles in child */
90026493Sminshall 		(void) close(myside);
90126493Sminshall 		(void) dup2(hisside, tst(0, 1));
90226493Sminshall 		(void) close(hisside);
90313211Sroot 		execv(gav[0], gav);
90410695Ssam 		_exit(1);
90510695Ssam 	}
90613211Sroot 	for (ac = 1; av[ac] != NULL; ac++)
90713211Sroot 		blkfree((char **)av[ac]);
90810695Ssam 	if (pid == -1)
90910695Ssam 		return (NULL);
91010695Ssam 	popen_pid[myside] = pid;
91126493Sminshall 	(void) close(hisside);
91210695Ssam 	return (fdopen(myside, mode));
91310695Ssam }
91410695Ssam 
91510695Ssam pclose(ptr)
91610695Ssam 	FILE *ptr;
91710695Ssam {
91810695Ssam 	register f, r, (*hstat)(), (*istat)(), (*qstat)();
91910695Ssam 	int status;
92010695Ssam 
92110695Ssam 	f = fileno(ptr);
92226493Sminshall 	(void) fclose(ptr);
92310695Ssam 	istat = signal(SIGINT, SIG_IGN);
92410695Ssam 	qstat = signal(SIGQUIT, SIG_IGN);
92510695Ssam 	hstat = signal(SIGHUP, SIG_IGN);
92610695Ssam 	while ((r = wait(&status)) != popen_pid[f] && r != -1)
92710695Ssam 		;
92810695Ssam 	if (r == -1)
92910695Ssam 		status = -1;
93026493Sminshall 	(void) signal(SIGINT, istat);
93126493Sminshall 	(void) signal(SIGQUIT, qstat);
93226493Sminshall 	(void) signal(SIGHUP, hstat);
93310695Ssam 	return (status);
93410695Ssam }
93510695Ssam 
93610695Ssam /*
93710695Ssam  * Check user requesting login priviledges.
93828864Smckusick  * Disallow anyone who does not have a standard
93928864Smckusick  * shell returned by getusershell() (/etc/shells).
94010695Ssam  * Disallow anyone mentioned in the file FTPUSERS
94110695Ssam  * to allow people such as uucp to be avoided.
94210695Ssam  */
94310695Ssam checkuser(name)
94410695Ssam 	register char *name;
94510695Ssam {
94628864Smckusick 	register char *cp;
94728864Smckusick 	char line[BUFSIZ], *index(), *getusershell();
94810695Ssam 	FILE *fd;
94928864Smckusick 	struct passwd *pw;
95010695Ssam 	int found = 0;
95110695Ssam 
95228864Smckusick 	pw = getpwnam(name);
95328864Smckusick 	if (pw == NULL)
95428864Smckusick 		return (0);
95530102Sbostic 	if (pw ->pw_shell == NULL || pw->pw_shell[0] == NULL)
95630102Sbostic 		pw->pw_shell = "/bin/sh";
95728864Smckusick 	while ((cp = getusershell()) != NULL)
95828864Smckusick 		if (strcmp(cp, pw->pw_shell) == 0)
95928864Smckusick 			break;
96028864Smckusick 	endusershell();
96128864Smckusick 	if (cp == NULL)
96228864Smckusick 		return (0);
96310695Ssam 	fd = fopen(FTPUSERS, "r");
96410695Ssam 	if (fd == NULL)
96510695Ssam 		return (1);
96610695Ssam 	while (fgets(line, sizeof (line), fd) != NULL) {
96728864Smckusick 		cp = index(line, '\n');
96810695Ssam 		if (cp)
96910695Ssam 			*cp = '\0';
97010695Ssam 		if (strcmp(line, name) == 0) {
97110695Ssam 			found++;
97210695Ssam 			break;
97310695Ssam 		}
97410695Ssam 	}
97526493Sminshall 	(void) fclose(fd);
97610695Ssam 	return (!found);
97710695Ssam }
97826044Sminshall 
97926044Sminshall myoob()
98026044Sminshall {
98127750Sminshall 	char *cp;
98226044Sminshall 
98327750Sminshall 	/* only process if transfer occurring */
98426044Sminshall 	if (!transflag) {
98526044Sminshall 		return;
98626044Sminshall 	}
98727750Sminshall 	cp = tmpline;
98827750Sminshall 	if (getline(cp, 7, stdin) == NULL) {
98927750Sminshall 		reply(221, "You could at least say goodby.");
99027750Sminshall 		dologout(0);
99126044Sminshall 	}
99226044Sminshall 	upper(cp);
99326227Ssam 	if (strcmp(cp, "ABOR\r\n"))
99426044Sminshall 		return;
99526044Sminshall 	tmpline[0] = '\0';
99626044Sminshall 	reply(426,"Transfer aborted. Data connection closed.");
99726044Sminshall 	reply(226,"Abort successful");
99826044Sminshall 	longjmp(urgcatch, 1);
99926044Sminshall }
100026044Sminshall 
100127106Smckusick /*
100227106Smckusick  * Note: The 530 reply codes could be 4xx codes, except nothing is
100327106Smckusick  * given in the state tables except 421 which implies an exit.  (RFC959)
100427106Smckusick  */
100526044Sminshall passive()
100626044Sminshall {
100726044Sminshall 	int len;
100826044Sminshall 	struct sockaddr_in tmp;
100926044Sminshall 	register char *p, *a;
101026044Sminshall 
101126044Sminshall 	pdata = socket(AF_INET, SOCK_STREAM, 0);
101226044Sminshall 	if (pdata < 0) {
101327106Smckusick 		reply(530, "Can't open passive connection");
101426044Sminshall 		return;
101526044Sminshall 	}
101626044Sminshall 	tmp = ctrl_addr;
101726044Sminshall 	tmp.sin_port = 0;
101826044Sminshall 	seteuid(0);
101926493Sminshall 	if (bind(pdata, (struct sockaddr *) &tmp, sizeof(tmp)) < 0) {
102026044Sminshall 		seteuid(pw->pw_uid);
102126044Sminshall 		(void) close(pdata);
102226044Sminshall 		pdata = -1;
102327106Smckusick 		reply(530, "Can't open passive connection");
102426044Sminshall 		return;
102526044Sminshall 	}
102626044Sminshall 	seteuid(pw->pw_uid);
102726044Sminshall 	len = sizeof(tmp);
102826044Sminshall 	if (getsockname(pdata, (char *) &tmp, &len) < 0) {
102926044Sminshall 		(void) close(pdata);
103026044Sminshall 		pdata = -1;
103127106Smckusick 		reply(530, "Can't open passive connection");
103226044Sminshall 		return;
103326044Sminshall 	}
103426044Sminshall 	if (listen(pdata, 1) < 0) {
103526044Sminshall 		(void) close(pdata);
103626044Sminshall 		pdata = -1;
103727106Smckusick 		reply(530, "Can't open passive connection");
103826044Sminshall 		return;
103926044Sminshall 	}
104026044Sminshall 	a = (char *) &tmp.sin_addr;
104126044Sminshall 	p = (char *) &tmp.sin_port;
104226044Sminshall 
104326044Sminshall #define UC(b) (((int) b) & 0xff)
104426044Sminshall 
104526044Sminshall 	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
104626044Sminshall 		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
104726044Sminshall }
104826044Sminshall 
104926044Sminshall char *
105026044Sminshall gunique(local)
105126044Sminshall 	char *local;
105226044Sminshall {
105326044Sminshall 	static char new[MAXPATHLEN];
105426044Sminshall 	char *cp = rindex(local, '/');
105526044Sminshall 	int d, count=0;
105626044Sminshall 	char ext = '1';
105726044Sminshall 
105826044Sminshall 	if (cp) {
105926044Sminshall 		*cp = '\0';
106026044Sminshall 	}
106126044Sminshall 	d = access(cp ? local : ".", 2);
106226044Sminshall 	if (cp) {
106326044Sminshall 		*cp = '/';
106426044Sminshall 	}
106526044Sminshall 	if (d < 0) {
106626493Sminshall 		syslog(LOG_ERR, "%s: %m", local);
106726044Sminshall 		return((char *) 0);
106826044Sminshall 	}
106926044Sminshall 	(void) strcpy(new, local);
107026044Sminshall 	cp = new + strlen(new);
107126044Sminshall 	*cp++ = '.';
107226044Sminshall 	while (!d) {
107326044Sminshall 		if (++count == 100) {
107427106Smckusick 			reply(452, "Unique file name not cannot be created.");
107526044Sminshall 			return((char *) 0);
107626044Sminshall 		}
107726044Sminshall 		*cp++ = ext;
107826044Sminshall 		*cp = '\0';
107926044Sminshall 		if (ext == '9') {
108026044Sminshall 			ext = '0';
108126044Sminshall 		}
108226044Sminshall 		else {
108326044Sminshall 			ext++;
108426044Sminshall 		}
108526044Sminshall 		if ((d = access(new, 0)) < 0) {
108626044Sminshall 			break;
108726044Sminshall 		}
108826044Sminshall 		if (ext != '0') {
108926044Sminshall 			cp--;
109026044Sminshall 		}
109126044Sminshall 		else if (*(cp - 2) == '.') {
109226044Sminshall 			*(cp - 1) = '1';
109326044Sminshall 		}
109426044Sminshall 		else {
109526044Sminshall 			*(cp - 2) = *(cp - 2) + 1;
109626044Sminshall 			cp--;
109726044Sminshall 		}
109826044Sminshall 	}
109926044Sminshall 	return(new);
110026044Sminshall }
1101