xref: /csrg-svn/libexec/ftpd/ftpd.c (revision 26227)
122499Sdist /*
226044Sminshall  * Copyright (c) 1985 Regents of the University of California.
322499Sdist  * All rights reserved.  The Berkeley software License Agreement
422499Sdist  * specifies the terms and conditions for redistribution.
522499Sdist  */
622499Sdist 
710275Ssam #ifndef lint
822499Sdist char copyright[] =
926044Sminshall "@(#) Copyright (c) 1985 Regents of the University of California.\n\
1022499Sdist  All rights reserved.\n";
1122499Sdist #endif not lint
1210275Ssam 
1322499Sdist #ifndef lint
14*26227Ssam static char sccsid[] = "@(#)ftpd.c	5.3 (Berkeley) 02/18/86";
1522499Sdist #endif not lint
1622499Sdist 
1710275Ssam /*
1810275Ssam  * FTP server.
1910275Ssam  */
2010303Ssam #include <sys/param.h>
2110275Ssam #include <sys/stat.h>
2210275Ssam #include <sys/ioctl.h>
2310275Ssam #include <sys/socket.h>
2413247Ssam #include <sys/file.h>
2513595Ssam #include <sys/wait.h>
2610275Ssam 
2710275Ssam #include <netinet/in.h>
2810275Ssam 
2913034Ssam #include <arpa/ftp.h>
3013211Sroot #include <arpa/inet.h>
3126044Sminshall #include <arpa/telnet.h>
3213034Ssam 
3310275Ssam #include <stdio.h>
3410275Ssam #include <signal.h>
3510275Ssam #include <pwd.h>
3610275Ssam #include <setjmp.h>
3710275Ssam #include <netdb.h>
3810423Ssam #include <errno.h>
3926044Sminshall #include <strings.h>
4010275Ssam 
4110695Ssam /*
4210695Ssam  * File containing login names
4310695Ssam  * NOT to be used on this machine.
4410695Ssam  * Commonly used to disallow uucp.
4510695Ssam  */
4610695Ssam #define	FTPUSERS	"/etc/ftpusers"
4710695Ssam 
4810275Ssam extern	int errno;
4910275Ssam extern	char *sys_errlist[];
5010275Ssam extern	char *crypt();
5110275Ssam extern	char version[];
5210275Ssam extern	char *home;		/* pointer to home directory for glob */
5326044Sminshall extern	FILE *popen(), *fopen(), *freopen();
5410275Ssam extern	int pclose(), fclose();
5526044Sminshall extern	char *getline();
5626044Sminshall extern	char cbuf[];
5710275Ssam 
5810275Ssam struct	sockaddr_in ctrl_addr;
5910275Ssam struct	sockaddr_in data_source;
6010275Ssam struct	sockaddr_in data_dest;
6110275Ssam struct	sockaddr_in his_addr;
6210275Ssam 
6310275Ssam struct	hostent *hp;
6410275Ssam 
6510275Ssam int	data;
6626044Sminshall jmp_buf	errcatch, urgcatch;
6710275Ssam int	logged_in;
6810275Ssam struct	passwd *pw;
6910275Ssam int	debug;
7011653Ssam int	timeout;
7111757Ssam int	logging;
7210275Ssam int	guest;
7316033Sralph int	wtmp;
7410275Ssam int	type;
7510275Ssam int	form;
7610275Ssam int	stru;			/* avoid C keyword */
7710275Ssam int	mode;
7810321Ssam int	usedefault = 1;		/* for data transfers */
7926044Sminshall int	pdata;			/* for passive mode */
8026044Sminshall int	unique;
8126044Sminshall int	transflag;
8226044Sminshall char	tmpline[7];
8310275Ssam char	hostname[32];
8413247Ssam char	remotehost[32];
8510275Ssam 
8611653Ssam /*
8711653Ssam  * Timeout intervals for retrying connections
8811653Ssam  * to hosts that don't accept PORT cmds.  This
8911653Ssam  * is a kludge, but given the problems with TCP...
9011653Ssam  */
9111653Ssam #define	SWAITMAX	90	/* wait at most 90 seconds */
9211653Ssam #define	SWAITINT	5	/* interval between retries */
9311653Ssam 
9411653Ssam int	swaitmax = SWAITMAX;
9511653Ssam int	swaitint = SWAITINT;
9611653Ssam 
9710275Ssam int	lostconn();
9810419Ssam int	reapchild();
9926044Sminshall int	myoob();
10010275Ssam FILE	*getdatasock(), *dataconn();
10110275Ssam 
10210275Ssam main(argc, argv)
10310275Ssam 	int argc;
10410275Ssam 	char *argv[];
10510275Ssam {
10616339Skarels 	int options = 0, addrlen;
10726044Sminshall 	long pgid;
10810275Ssam 	char *cp;
10910275Ssam 
11016339Skarels 	addrlen = sizeof (his_addr);
11116339Skarels 	if (getpeername(0, &his_addr, &addrlen) < 0) {
11216339Skarels 		fprintf(stderr, "%s: ", argv[0]);
11316339Skarels 		perror("getpeername");
11410275Ssam 		exit(1);
11510275Ssam 	}
11616339Skarels 	addrlen = sizeof (ctrl_addr);
11716339Skarels 	if (getsockname(0, &ctrl_addr, &addrlen) < 0) {
11816339Skarels 		fprintf(stderr, "%s: ", argv[0]);
11916339Skarels 		perror("getsockname");
12016339Skarels 		exit(1);
12116339Skarels 	}
12216339Skarels 	data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
12310275Ssam 	debug = 0;
12410275Ssam 	argc--, argv++;
12510275Ssam 	while (argc > 0 && *argv[0] == '-') {
12610275Ssam 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
12710275Ssam 
12811653Ssam 		case 'v':
12911653Ssam 			debug = 1;
13011653Ssam 			break;
13111653Ssam 
13210275Ssam 		case 'd':
13310275Ssam 			debug = 1;
13410275Ssam 			options |= SO_DEBUG;
13510275Ssam 			break;
13610275Ssam 
13711757Ssam 		case 'l':
13811757Ssam 			logging = 1;
13926044Sminshall 			(void) freopen("/tmp/ftplog", "a", stderr);
14011757Ssam 			break;
14111757Ssam 
14211653Ssam 		case 't':
14311653Ssam 			timeout = atoi(++cp);
14411653Ssam 			goto nextopt;
14511653Ssam 			break;
14611653Ssam 
14710275Ssam 		default:
14816339Skarels 			fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
14916339Skarels 			     *cp);
15010275Ssam 			break;
15110275Ssam 		}
15211653Ssam nextopt:
15310275Ssam 		argc--, argv++;
15410275Ssam 	}
15516339Skarels 	signal(SIGPIPE, lostconn);
15616339Skarels 	signal(SIGCHLD, SIG_IGN);
15726044Sminshall 	if (signal(SIGURG, myoob) < 0) {
15826044Sminshall 		perror("signal");
15926044Sminshall 	}
16026044Sminshall 	pgid = getpid();
16126044Sminshall 	if (ioctl(fileno(stdin), SIOCSPGRP, (char *) &pgid) < 0) {
16226044Sminshall 		perror("ioctl");
16326044Sminshall 	}
16416760Slepreau 	dolog(&his_addr);
16516339Skarels 	/* do telnet option negotiation here */
16616339Skarels 	/*
16716339Skarels 	 * Set up default state
16816339Skarels 	 */
16916339Skarels 	logged_in = 0;
17016339Skarels 	data = -1;
17116339Skarels 	type = TYPE_A;
17216339Skarels 	form = FORM_N;
17316339Skarels 	stru = STRU_F;
17416339Skarels 	mode = MODE_S;
17526044Sminshall 	tmpline[0] = '\0';
17616339Skarels 	gethostname(hostname, sizeof (hostname));
17716339Skarels 	reply(220, "%s FTP server (%s) ready.",
17816339Skarels 		hostname, version);
17910275Ssam 	for (;;) {
18016339Skarels 		setjmp(errcatch);
18116339Skarels 		yyparse();
18210275Ssam 	}
18310275Ssam }
18410275Ssam 
18510419Ssam reapchild()
18610419Ssam {
18710419Ssam 	union wait status;
18810419Ssam 
18910419Ssam 	while (wait3(&status, WNOHANG, 0) > 0)
19010419Ssam 		;
19110419Ssam }
19210419Ssam 
19310275Ssam lostconn()
19410275Ssam {
19510275Ssam 
19614089Ssam 	if (debug)
19714089Ssam 		fprintf(stderr, "Lost connection.\n");
19814089Ssam 	dologout(-1);
19910275Ssam }
20010275Ssam 
20110275Ssam pass(passwd)
20210275Ssam 	char *passwd;
20310275Ssam {
20410303Ssam 	char *xpasswd, *savestr();
20510303Ssam 	static struct passwd save;
20610275Ssam 
20710275Ssam 	if (logged_in || pw == NULL) {
20810275Ssam 		reply(503, "Login with USER first.");
20910275Ssam 		return;
21010275Ssam 	}
21110275Ssam 	if (!guest) {		/* "ftp" is only account allowed no password */
21210275Ssam 		xpasswd = crypt(passwd, pw->pw_passwd);
21316760Slepreau 		/* The strcmp does not catch null passwords! */
21416760Slepreau 		if (*pw->pw_passwd == '\0' || strcmp(xpasswd, pw->pw_passwd)) {
21510275Ssam 			reply(530, "Login incorrect.");
21610275Ssam 			pw = NULL;
21710275Ssam 			return;
21810275Ssam 		}
21910275Ssam 	}
22010303Ssam 	setegid(pw->pw_gid);
22110275Ssam 	initgroups(pw->pw_name, pw->pw_gid);
22210275Ssam 	if (chdir(pw->pw_dir)) {
22318107Sbloom 		reply(550, "User %s: can't change directory to %s.",
22410275Ssam 			pw->pw_name, pw->pw_dir);
22510303Ssam 		goto bad;
22610275Ssam 	}
22716033Sralph 
22816760Slepreau 	/* grab wtmp before chroot */
22916760Slepreau 	wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
23010303Ssam 	if (guest && chroot(pw->pw_dir) < 0) {
23110275Ssam 		reply(550, "Can't set guest privileges.");
23216760Slepreau 		if (wtmp >= 0) {
23316760Slepreau 			(void) close(wtmp);
23416760Slepreau 			wtmp = -1;
23516760Slepreau 		}
23610303Ssam 		goto bad;
23710275Ssam 	}
23810275Ssam 	if (!guest)
23910275Ssam 		reply(230, "User %s logged in.", pw->pw_name);
24010275Ssam 	else
24110275Ssam 		reply(230, "Guest login ok, access restrictions apply.");
24210275Ssam 	logged_in = 1;
24313247Ssam 	dologin(pw);
24410303Ssam 	seteuid(pw->pw_uid);
24510303Ssam 	/*
24610303Ssam 	 * Save everything so globbing doesn't
24710303Ssam 	 * clobber the fields.
24810303Ssam 	 */
24910303Ssam 	save = *pw;
25010303Ssam 	save.pw_name = savestr(pw->pw_name);
25110303Ssam 	save.pw_passwd = savestr(pw->pw_passwd);
25210303Ssam 	save.pw_comment = savestr(pw->pw_comment);
25310303Ssam 	save.pw_gecos = savestr(pw->pw_gecos, &save.pw_gecos);
25410303Ssam 	save.pw_dir = savestr(pw->pw_dir);
25510303Ssam 	save.pw_shell = savestr(pw->pw_shell);
25610303Ssam 	pw = &save;
25710303Ssam 	home = pw->pw_dir;		/* home dir for globbing */
25810303Ssam 	return;
25910303Ssam bad:
26010303Ssam 	seteuid(0);
26110303Ssam 	pw = NULL;
26210275Ssam }
26310275Ssam 
26410303Ssam char *
26510303Ssam savestr(s)
26610303Ssam 	char *s;
26710303Ssam {
26810303Ssam 	char *malloc();
26910303Ssam 	char *new = malloc(strlen(s) + 1);
27010303Ssam 
27110303Ssam 	if (new != NULL)
27210303Ssam 		strcpy(new, s);
27311347Ssam 	return (new);
27410303Ssam }
27510303Ssam 
27610275Ssam retrieve(cmd, name)
27710275Ssam 	char *cmd, *name;
27810275Ssam {
27910275Ssam 	FILE *fin, *dout;
28010275Ssam 	struct stat st;
28126044Sminshall 	int (*closefunc)(), tmp;
28210275Ssam 
28310275Ssam 	if (cmd == 0) {
28410317Ssam #ifdef notdef
28510317Ssam 		/* no remote command execution -- it's a security hole */
28611653Ssam 		if (*name == '|')
28710275Ssam 			fin = popen(name + 1, "r"), closefunc = pclose;
28810275Ssam 		else
28910317Ssam #endif
29010275Ssam 			fin = fopen(name, "r"), closefunc = fclose;
29110275Ssam 	} else {
29210275Ssam 		char line[BUFSIZ];
29310275Ssam 
29410422Ssam 		sprintf(line, cmd, name), name = line;
29510275Ssam 		fin = popen(line, "r"), closefunc = pclose;
29610275Ssam 	}
29710275Ssam 	if (fin == NULL) {
29813152Ssam 		if (errno != 0)
29913152Ssam 			reply(550, "%s: %s.", name, sys_errlist[errno]);
30010275Ssam 		return;
30110275Ssam 	}
30210275Ssam 	st.st_size = 0;
30310275Ssam 	if (cmd == 0 &&
30410275Ssam 	    (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
30510275Ssam 		reply(550, "%s: not a plain file.", name);
30610275Ssam 		goto done;
30710275Ssam 	}
30810275Ssam 	dout = dataconn(name, st.st_size, "w");
30910275Ssam 	if (dout == NULL)
31010275Ssam 		goto done;
31126044Sminshall 	if ((tmp = send_data(fin, dout)) > 0 || ferror(dout) > 0) {
31210275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
31326044Sminshall 	}
31426044Sminshall 	else if (tmp == 0) {
31510275Ssam 		reply(226, "Transfer complete.");
31626044Sminshall 	}
31726044Sminshall 	fclose(dout);
31826044Sminshall 	data = -1;
31926044Sminshall 	pdata = -1;
32010275Ssam done:
32110275Ssam 	(*closefunc)(fin);
32210275Ssam }
32310275Ssam 
32410275Ssam store(name, mode)
32510275Ssam 	char *name, *mode;
32610275Ssam {
32710275Ssam 	FILE *fout, *din;
32826044Sminshall 	int (*closefunc)(), dochown = 0, tmp;
32926044Sminshall 	char *gunique(), *local;
33010275Ssam 
33110317Ssam #ifdef notdef
33210317Ssam 	/* no remote command execution -- it's a security hole */
33311653Ssam 	if (name[0] == '|')
33410275Ssam 		fout = popen(&name[1], "w"), closefunc = pclose;
33510317Ssam 	else
33610317Ssam #endif
33710317Ssam 	{
33810303Ssam 		struct stat st;
33910303Ssam 
34026044Sminshall 		local = name;
34126044Sminshall 		if (stat(name, &st) < 0) {
34210303Ssam 			dochown++;
34326044Sminshall 		}
34426044Sminshall 		else if (unique) {
34526044Sminshall 			if ((local = gunique(name)) == NULL) {
34626044Sminshall 				return;
34726044Sminshall 			}
34826044Sminshall 			dochown++;
34926044Sminshall 		}
35026044Sminshall 		fout = fopen(local, mode), closefunc = fclose;
35110303Ssam 	}
35210275Ssam 	if (fout == NULL) {
35326044Sminshall 		reply(550, "%s: %s.", local, sys_errlist[errno]);
35410275Ssam 		return;
35510275Ssam 	}
35626044Sminshall 	din = dataconn(local, (off_t)-1, "r");
35710275Ssam 	if (din == NULL)
35810275Ssam 		goto done;
35926044Sminshall 	if ((tmp = receive_data(din, fout)) > 0 || ferror(fout) > 0) {
36026044Sminshall 		reply(550, "%s: %s.", local, sys_errlist[errno]);
36126044Sminshall 	}
36226044Sminshall 	else if (tmp == 0 && !unique) {
36310275Ssam 		reply(226, "Transfer complete.");
36426044Sminshall 	}
36526044Sminshall 	else if (tmp == 0 && unique) {
36626044Sminshall 		reply(226, "Transfer complete (unique file name:%s).", local);
36726044Sminshall 	}
36826044Sminshall 	fclose(din);
36926044Sminshall 	data = -1;
37026044Sminshall 	pdata = -1;
37110275Ssam done:
37210303Ssam 	if (dochown)
37326044Sminshall 		(void) chown(local, pw->pw_uid, -1);
37410275Ssam 	(*closefunc)(fout);
37510275Ssam }
37610275Ssam 
37710275Ssam FILE *
37810275Ssam getdatasock(mode)
37910275Ssam 	char *mode;
38010275Ssam {
38117157Ssam 	int s, on = 1;
38210275Ssam 
38310275Ssam 	if (data >= 0)
38410275Ssam 		return (fdopen(data, mode));
38513247Ssam 	s = socket(AF_INET, SOCK_STREAM, 0);
38610602Ssam 	if (s < 0)
38710275Ssam 		return (NULL);
38810275Ssam 	seteuid(0);
38917157Ssam 	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
39010602Ssam 		goto bad;
39113152Ssam 	/* anchor socket to avoid multi-homing problems */
39213152Ssam 	data_source.sin_family = AF_INET;
39313152Ssam 	data_source.sin_addr = ctrl_addr.sin_addr;
39410602Ssam 	if (bind(s, &data_source, sizeof (data_source), 0) < 0)
39510602Ssam 		goto bad;
39610311Ssam 	seteuid(pw->pw_uid);
39710275Ssam 	return (fdopen(s, mode));
39810602Ssam bad:
39910602Ssam 	seteuid(pw->pw_uid);
40010602Ssam 	close(s);
40110602Ssam 	return (NULL);
40210275Ssam }
40310275Ssam 
40410275Ssam FILE *
40510275Ssam dataconn(name, size, mode)
40610275Ssam 	char *name;
40711653Ssam 	off_t size;
40810275Ssam 	char *mode;
40910275Ssam {
41010275Ssam 	char sizebuf[32];
41110275Ssam 	FILE *file;
41211653Ssam 	int retry = 0;
41310275Ssam 
41410275Ssam 	if (size >= 0)
41511653Ssam 		sprintf (sizebuf, " (%ld bytes)", size);
41610275Ssam 	else
41710275Ssam 		(void) strcpy(sizebuf, "");
41826044Sminshall 	if (pdata > 0) {
41926044Sminshall 		struct sockaddr_in from;
42026044Sminshall 		int s, fromlen = sizeof(from);
42126044Sminshall 
42226044Sminshall 		s = accept(pdata, &from, &fromlen, 0);
42326044Sminshall 		if (s < 0) {
42426044Sminshall 			reply(425, "Can't open data connection.");
42526044Sminshall 			(void) close(pdata);
42626044Sminshall 			pdata = -1;
42726044Sminshall 			return(NULL);
42826044Sminshall 		}
42926044Sminshall 		(void) close(pdata);
43026044Sminshall 		pdata = s;
43126044Sminshall 		reply(150, "Openning data connection for %s (%s,%d)%s.",
43226044Sminshall 		     name, inet_ntoa(from.sin_addr.s_addr),
43326044Sminshall 		     ntohs(from.sin_port), sizebuf);
43426044Sminshall 		return(fdopen(pdata, mode));
43526044Sminshall 	}
43610275Ssam 	if (data >= 0) {
43710275Ssam 		reply(125, "Using existing data connection for %s%s.",
43810275Ssam 		    name, sizebuf);
43910321Ssam 		usedefault = 1;
44010275Ssam 		return (fdopen(data, mode));
44110275Ssam 	}
44210566Ssam 	if (usedefault)
44310422Ssam 		data_dest = his_addr;
44410422Ssam 	usedefault = 1;
44510275Ssam 	file = getdatasock(mode);
44610275Ssam 	if (file == NULL) {
44710275Ssam 		reply(425, "Can't create data socket (%s,%d): %s.",
44813247Ssam 		    inet_ntoa(data_source.sin_addr),
44910275Ssam 		    ntohs(data_source.sin_port),
45010275Ssam 		    sys_errlist[errno]);
45110275Ssam 		return (NULL);
45210275Ssam 	}
45310602Ssam 	reply(150, "Opening data connection for %s (%s,%d)%s.",
45413247Ssam 	    name, inet_ntoa(data_dest.sin_addr.s_addr),
45510602Ssam 	    ntohs(data_dest.sin_port), sizebuf);
45610275Ssam 	data = fileno(file);
45726044Sminshall 	while (connect(data, &data_dest, sizeof (data_dest)) < 0) {
45811653Ssam 		if (errno == EADDRINUSE && retry < swaitmax) {
45911653Ssam 			sleep(swaitint);
46011653Ssam 			retry += swaitint;
46111653Ssam 			continue;
46211653Ssam 		}
46310275Ssam 		reply(425, "Can't build data connection: %s.",
46410275Ssam 		    sys_errlist[errno]);
46510275Ssam 		(void) fclose(file);
46610275Ssam 		data = -1;
46710275Ssam 		return (NULL);
46810275Ssam 	}
46910275Ssam 	return (file);
47010275Ssam }
47110275Ssam 
47210275Ssam /*
47310275Ssam  * Tranfer the contents of "instr" to
47410275Ssam  * "outstr" peer using the appropriate
47510275Ssam  * encapulation of the date subject
47610275Ssam  * to Mode, Structure, and Type.
47710275Ssam  *
47810275Ssam  * NB: Form isn't handled.
47910275Ssam  */
48010275Ssam send_data(instr, outstr)
48110275Ssam 	FILE *instr, *outstr;
48210275Ssam {
48310275Ssam 	register int c;
48410275Ssam 	int netfd, filefd, cnt;
48510275Ssam 	char buf[BUFSIZ];
48610275Ssam 
48726044Sminshall 	transflag++;
48826044Sminshall 	if (setjmp(urgcatch)) {
48926044Sminshall 		transflag = 0;
49026044Sminshall 		return(-1);
49126044Sminshall 	}
49210275Ssam 	switch (type) {
49310275Ssam 
49410275Ssam 	case TYPE_A:
49510275Ssam 		while ((c = getc(instr)) != EOF) {
49611220Ssam 			if (c == '\n') {
49726044Sminshall 				if (ferror (outstr)) {
49826044Sminshall 					transflag = 0;
49911220Ssam 					return (1);
50026044Sminshall 				}
50110275Ssam 				putc('\r', outstr);
50211220Ssam 			}
50311220Ssam 			putc(c, outstr);
50426044Sminshall 		/*	if (c == '\r')			*/
50526044Sminshall 		/*		putc ('\0', outstr);	*/
50610275Ssam 		}
50726044Sminshall 		transflag = 0;
50826044Sminshall 		if (ferror (instr) || ferror (outstr)) {
50911220Ssam 			return (1);
51026044Sminshall 		}
51110275Ssam 		return (0);
51210275Ssam 
51310275Ssam 	case TYPE_I:
51410275Ssam 	case TYPE_L:
51510275Ssam 		netfd = fileno(outstr);
51610275Ssam 		filefd = fileno(instr);
51710275Ssam 
51826044Sminshall 		while ((cnt = read(filefd, buf, sizeof (buf))) > 0) {
51926044Sminshall 			if (write(netfd, buf, cnt) < 0) {
52026044Sminshall 				transflag = 0;
52110275Ssam 				return (1);
52226044Sminshall 			}
52326044Sminshall 		}
52426044Sminshall 		transflag = 0;
52510275Ssam 		return (cnt < 0);
52610275Ssam 	}
52710275Ssam 	reply(504,"Unimplemented TYPE %d in send_data", type);
52826044Sminshall 	transflag = 0;
52910275Ssam 	return (1);
53010275Ssam }
53110275Ssam 
53210275Ssam /*
53310275Ssam  * Transfer data from peer to
53410275Ssam  * "outstr" using the appropriate
53510275Ssam  * encapulation of the data subject
53610275Ssam  * to Mode, Structure, and Type.
53710275Ssam  *
53810275Ssam  * N.B.: Form isn't handled.
53910275Ssam  */
54010275Ssam receive_data(instr, outstr)
54110275Ssam 	FILE *instr, *outstr;
54210275Ssam {
54310275Ssam 	register int c;
54411220Ssam 	int cnt;
54510275Ssam 	char buf[BUFSIZ];
54610275Ssam 
54710275Ssam 
54826044Sminshall 	transflag++;
54926044Sminshall 	if (setjmp(urgcatch)) {
55026044Sminshall 		transflag = 0;
55126044Sminshall 		return(-1);
55226044Sminshall 	}
55310275Ssam 	switch (type) {
55410275Ssam 
55510275Ssam 	case TYPE_I:
55610275Ssam 	case TYPE_L:
55726044Sminshall 		while ((cnt = read(fileno(instr), buf, sizeof buf)) > 0) {
55826044Sminshall 			if (write(fileno(outstr), buf, cnt) < 0) {
55926044Sminshall 				transflag = 0;
56010275Ssam 				return (1);
56126044Sminshall 			}
56226044Sminshall 		}
56326044Sminshall 		transflag = 0;
56410275Ssam 		return (cnt < 0);
56510275Ssam 
56610275Ssam 	case TYPE_E:
56710275Ssam 		reply(504, "TYPE E not implemented.");
56826044Sminshall 		transflag = 0;
56910275Ssam 		return (1);
57010275Ssam 
57110275Ssam 	case TYPE_A:
57210275Ssam 		while ((c = getc(instr)) != EOF) {
57310275Ssam 			if (c == '\r') {
57426044Sminshall 				if (ferror (outstr)) {
57526044Sminshall 					transflag = 0;
57611220Ssam 					return (1);
57726044Sminshall 				}
57811220Ssam 				if ((c = getc(instr)) != '\n')
57911220Ssam 					putc ('\r', outstr);
58026044Sminshall 			/*	if (c == '\0')			*/
58126044Sminshall 			/*		continue;		*/
58210275Ssam 			}
58311220Ssam 			putc (c, outstr);
58410275Ssam 		}
58526044Sminshall 		transflag = 0;
58611220Ssam 		if (ferror (instr) || ferror (outstr))
58711220Ssam 			return (1);
58810275Ssam 		return (0);
58910275Ssam 	}
59026044Sminshall 	transflag = 0;
59110275Ssam 	fatal("Unknown type in receive_data.");
59210275Ssam 	/*NOTREACHED*/
59310275Ssam }
59410275Ssam 
59510275Ssam fatal(s)
59610275Ssam 	char *s;
59710275Ssam {
59810275Ssam 	reply(451, "Error in server: %s\n", s);
59910275Ssam 	reply(221, "Closing connection due to server error.");
60013247Ssam 	dologout(0);
60110275Ssam }
60210275Ssam 
60310275Ssam reply(n, s, args)
60410275Ssam 	int n;
60510275Ssam 	char *s;
60610275Ssam {
60710275Ssam 
60810275Ssam 	printf("%d ", n);
60910275Ssam 	_doprnt(s, &args, stdout);
61010275Ssam 	printf("\r\n");
61110275Ssam 	fflush(stdout);
61210275Ssam 	if (debug) {
61310275Ssam 		fprintf(stderr, "<--- %d ", n);
61410275Ssam 		_doprnt(s, &args, stderr);
61510275Ssam 		fprintf(stderr, "\n");
61610275Ssam 		fflush(stderr);
61710275Ssam 	}
61810275Ssam }
61910275Ssam 
62010275Ssam lreply(n, s, args)
62110275Ssam 	int n;
62210275Ssam 	char *s;
62310275Ssam {
62410275Ssam 	printf("%d-", n);
62510275Ssam 	_doprnt(s, &args, stdout);
62610275Ssam 	printf("\r\n");
62710275Ssam 	fflush(stdout);
62810275Ssam 	if (debug) {
62910275Ssam 		fprintf(stderr, "<--- %d-", n);
63010275Ssam 		_doprnt(s, &args, stderr);
63110275Ssam 		fprintf(stderr, "\n");
63210275Ssam 	}
63310275Ssam }
63410275Ssam 
63510275Ssam replystr(s)
63610275Ssam 	char *s;
63710275Ssam {
63810275Ssam 	printf("%s\r\n", s);
63910275Ssam 	fflush(stdout);
64010275Ssam 	if (debug)
64110275Ssam 		fprintf(stderr, "<--- %s\n", s);
64210275Ssam }
64310275Ssam 
64410275Ssam ack(s)
64510275Ssam 	char *s;
64610275Ssam {
64726044Sminshall 	reply(200, "%s command successful.", s);
64810275Ssam }
64910275Ssam 
65010275Ssam nack(s)
65110275Ssam 	char *s;
65210275Ssam {
65310275Ssam 	reply(502, "%s command not implemented.", s);
65410275Ssam }
65510275Ssam 
65610275Ssam yyerror()
65710275Ssam {
65826044Sminshall 	char *cp;
65926044Sminshall 
66026044Sminshall 	cp = index(cbuf,'\n');
66126044Sminshall 	*cp = '\0';
66226044Sminshall 	reply(500, "'%s': command not understood.",cbuf);
66310275Ssam }
66410275Ssam 
66510275Ssam delete(name)
66610275Ssam 	char *name;
66710275Ssam {
66810275Ssam 	struct stat st;
66910275Ssam 
67010275Ssam 	if (stat(name, &st) < 0) {
67110275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
67210275Ssam 		return;
67310275Ssam 	}
67410275Ssam 	if ((st.st_mode&S_IFMT) == S_IFDIR) {
67510275Ssam 		if (rmdir(name) < 0) {
67610275Ssam 			reply(550, "%s: %s.", name, sys_errlist[errno]);
67710275Ssam 			return;
67810275Ssam 		}
67910275Ssam 		goto done;
68010275Ssam 	}
68110275Ssam 	if (unlink(name) < 0) {
68210275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
68310275Ssam 		return;
68410275Ssam 	}
68510275Ssam done:
68610275Ssam 	ack("DELE");
68710275Ssam }
68810275Ssam 
68910275Ssam cwd(path)
69010275Ssam 	char *path;
69110275Ssam {
69210275Ssam 
69310275Ssam 	if (chdir(path) < 0) {
69410275Ssam 		reply(550, "%s: %s.", path, sys_errlist[errno]);
69510275Ssam 		return;
69610275Ssam 	}
69710275Ssam 	ack("CWD");
69810275Ssam }
69910275Ssam 
70010303Ssam makedir(name)
70110275Ssam 	char *name;
70210275Ssam {
70310303Ssam 	struct stat st;
70410303Ssam 	int dochown = stat(name, &st) < 0;
70510275Ssam 
70610275Ssam 	if (mkdir(name, 0777) < 0) {
70710275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
70810275Ssam 		return;
70910275Ssam 	}
71010303Ssam 	if (dochown)
71110303Ssam 		(void) chown(name, pw->pw_uid, -1);
71210275Ssam 	ack("MKDIR");
71310275Ssam }
71410275Ssam 
71510303Ssam removedir(name)
71610275Ssam 	char *name;
71710275Ssam {
71810275Ssam 
71910275Ssam 	if (rmdir(name) < 0) {
72010275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
72110275Ssam 		return;
72210275Ssam 	}
72310275Ssam 	ack("RMDIR");
72410275Ssam }
72510275Ssam 
72610303Ssam pwd()
72710275Ssam {
72810303Ssam 	char path[MAXPATHLEN + 1];
72910275Ssam 
73010275Ssam 	if (getwd(path) == NULL) {
73110275Ssam 		reply(451, "%s.", path);
73210275Ssam 		return;
73310275Ssam 	}
73410275Ssam 	reply(251, "\"%s\" is current directory.", path);
73510275Ssam }
73610275Ssam 
73710275Ssam char *
73810275Ssam renamefrom(name)
73910275Ssam 	char *name;
74010275Ssam {
74110275Ssam 	struct stat st;
74210275Ssam 
74310275Ssam 	if (stat(name, &st) < 0) {
74410275Ssam 		reply(550, "%s: %s.", name, sys_errlist[errno]);
74510275Ssam 		return ((char *)0);
74610275Ssam 	}
74710303Ssam 	reply(350, "File exists, ready for destination name");
74810275Ssam 	return (name);
74910275Ssam }
75010275Ssam 
75110275Ssam renamecmd(from, to)
75210275Ssam 	char *from, *to;
75310275Ssam {
75410275Ssam 
75510275Ssam 	if (rename(from, to) < 0) {
75610275Ssam 		reply(550, "rename: %s.", sys_errlist[errno]);
75710275Ssam 		return;
75810275Ssam 	}
75910275Ssam 	ack("RNTO");
76010275Ssam }
76110275Ssam 
76210275Ssam dolog(sin)
76310275Ssam 	struct sockaddr_in *sin;
76410275Ssam {
76510275Ssam 	struct hostent *hp = gethostbyaddr(&sin->sin_addr,
76610275Ssam 		sizeof (struct in_addr), AF_INET);
76710275Ssam 	time_t t;
76810275Ssam 
76913247Ssam 	if (hp) {
77013247Ssam 		strncpy(remotehost, hp->h_name, sizeof (remotehost));
77113247Ssam 		endhostent();
77213247Ssam 	} else
77313247Ssam 		strncpy(remotehost, inet_ntoa(sin->sin_addr),
77413247Ssam 		    sizeof (remotehost));
77513247Ssam 	if (!logging)
77613247Ssam 		return;
77710275Ssam 	t = time(0);
77811757Ssam 	fprintf(stderr,"FTPD: connection from %s at %s", remotehost, ctime(&t));
77910275Ssam 	fflush(stderr);
78010275Ssam }
78110695Ssam 
78213247Ssam #include <utmp.h>
78313247Ssam 
78413247Ssam #define	SCPYN(a, b)	strncpy(a, b, sizeof (a))
78513247Ssam struct	utmp utmp;
78613247Ssam 
78710695Ssam /*
78813247Ssam  * Record login in wtmp file.
78913247Ssam  */
79013247Ssam dologin(pw)
79113247Ssam 	struct passwd *pw;
79213247Ssam {
79313247Ssam 	char line[32];
79413247Ssam 
79513247Ssam 	if (wtmp >= 0) {
79613247Ssam 		/* hack, but must be unique and no tty line */
79713247Ssam 		sprintf(line, "ftp%d", getpid());
79813247Ssam 		SCPYN(utmp.ut_line, line);
79913247Ssam 		SCPYN(utmp.ut_name, pw->pw_name);
80013247Ssam 		SCPYN(utmp.ut_host, remotehost);
80113247Ssam 		utmp.ut_time = time(0);
80213247Ssam 		(void) write(wtmp, (char *)&utmp, sizeof (utmp));
80316760Slepreau 		if (!guest) {		/* anon must hang on */
80416760Slepreau 			(void) close(wtmp);
80516760Slepreau 			wtmp = -1;
80616760Slepreau 		}
80713247Ssam 	}
80813247Ssam }
80913247Ssam 
81013247Ssam /*
81113247Ssam  * Record logout in wtmp file
81213247Ssam  * and exit with supplied status.
81313247Ssam  */
81413247Ssam dologout(status)
81513247Ssam 	int status;
81613247Ssam {
81716339Skarels 
81817580Ssam 	if (logged_in) {
81917580Ssam 		(void) seteuid(0);
82017580Ssam 		if (wtmp < 0)
82117580Ssam 			wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
82217580Ssam 		if (wtmp >= 0) {
82317580Ssam 			SCPYN(utmp.ut_name, "");
82417580Ssam 			SCPYN(utmp.ut_host, "");
82517580Ssam 			utmp.ut_time = time(0);
82617580Ssam 			(void) write(wtmp, (char *)&utmp, sizeof (utmp));
82717580Ssam 			(void) close(wtmp);
82817580Ssam 		}
82913247Ssam 	}
83014436Ssam 	/* beware of flushing buffers after a SIGPIPE */
83114436Ssam 	_exit(status);
83213247Ssam }
83313247Ssam 
83413247Ssam /*
83510695Ssam  * Special version of popen which avoids
83610695Ssam  * call to shell.  This insures noone may
83710695Ssam  * create a pipe to a hidden program as a side
83810695Ssam  * effect of a list or dir command.
83910695Ssam  */
84010695Ssam #define	tst(a,b)	(*mode == 'r'? (b) : (a))
84110695Ssam #define	RDR	0
84210695Ssam #define	WTR	1
84310695Ssam static	int popen_pid[5];
84410695Ssam 
84510695Ssam static char *
84610695Ssam nextarg(cpp)
84710695Ssam 	char *cpp;
84810695Ssam {
84910695Ssam 	register char *cp = cpp;
85010695Ssam 
85110695Ssam 	if (cp == 0)
85210695Ssam 		return (cp);
85310695Ssam 	while (*cp && *cp != ' ' && *cp != '\t')
85410695Ssam 		cp++;
85510695Ssam 	if (*cp == ' ' || *cp == '\t') {
85610695Ssam 		*cp++ = '\0';
85710695Ssam 		while (*cp == ' ' || *cp == '\t')
85810695Ssam 			cp++;
85910695Ssam 	}
86010695Ssam 	if (cp == cpp)
86110695Ssam 		return ((char *)0);
86210695Ssam 	return (cp);
86310695Ssam }
86410695Ssam 
86510695Ssam FILE *
86610695Ssam popen(cmd, mode)
86710695Ssam 	char *cmd, *mode;
86810695Ssam {
86913211Sroot 	int p[2], ac, gac;
87010695Ssam 	register myside, hisside, pid;
87113211Sroot 	char *av[20], *gav[512];
87210695Ssam 	register char *cp;
87310695Ssam 
87410695Ssam 	if (pipe(p) < 0)
87510695Ssam 		return (NULL);
87610695Ssam 	cp = cmd, ac = 0;
87713211Sroot 	/* break up string into pieces */
87810695Ssam 	do {
87910695Ssam 		av[ac++] = cp;
88010695Ssam 		cp = nextarg(cp);
88113211Sroot 	} while (cp && *cp && ac < 20);
88210695Ssam 	av[ac] = (char *)0;
88313211Sroot 	gav[0] = av[0];
88413211Sroot 	/* glob each piece */
88513211Sroot 	for (gac = ac = 1; av[ac] != NULL; ac++) {
88613211Sroot 		char **pop;
88722024Ssam 		extern char **glob(), **copyblk();
88813211Sroot 
88913211Sroot 		pop = glob(av[ac]);
89022024Ssam 		if (pop == (char **)NULL) {	/* globbing failed */
89122024Ssam 			char *vv[2];
89222024Ssam 
89322024Ssam 			vv[0] = av[ac];
89422024Ssam 			vv[1] = 0;
89522024Ssam 			pop = copyblk(vv);
89613211Sroot 		}
89722024Ssam 		av[ac] = (char *)pop;		/* save to free later */
89822024Ssam 		while (*pop && gac < 512)
89922024Ssam 			gav[gac++] = *pop++;
90011757Ssam 	}
90113211Sroot 	gav[gac] = (char *)0;
90210695Ssam 	myside = tst(p[WTR], p[RDR]);
90310695Ssam 	hisside = tst(p[RDR], p[WTR]);
90410695Ssam 	if ((pid = fork()) == 0) {
90510695Ssam 		/* myside and hisside reverse roles in child */
90610695Ssam 		close(myside);
90710695Ssam 		dup2(hisside, tst(0, 1));
90810695Ssam 		close(hisside);
90913211Sroot 		execv(gav[0], gav);
91010695Ssam 		_exit(1);
91110695Ssam 	}
91213211Sroot 	for (ac = 1; av[ac] != NULL; ac++)
91313211Sroot 		blkfree((char **)av[ac]);
91410695Ssam 	if (pid == -1)
91510695Ssam 		return (NULL);
91610695Ssam 	popen_pid[myside] = pid;
91710695Ssam 	close(hisside);
91810695Ssam 	return (fdopen(myside, mode));
91910695Ssam }
92010695Ssam 
92110695Ssam pclose(ptr)
92210695Ssam 	FILE *ptr;
92310695Ssam {
92410695Ssam 	register f, r, (*hstat)(), (*istat)(), (*qstat)();
92510695Ssam 	int status;
92610695Ssam 
92710695Ssam 	f = fileno(ptr);
92810695Ssam 	fclose(ptr);
92910695Ssam 	istat = signal(SIGINT, SIG_IGN);
93010695Ssam 	qstat = signal(SIGQUIT, SIG_IGN);
93110695Ssam 	hstat = signal(SIGHUP, SIG_IGN);
93210695Ssam 	while ((r = wait(&status)) != popen_pid[f] && r != -1)
93310695Ssam 		;
93410695Ssam 	if (r == -1)
93510695Ssam 		status = -1;
93610695Ssam 	signal(SIGINT, istat);
93710695Ssam 	signal(SIGQUIT, qstat);
93810695Ssam 	signal(SIGHUP, hstat);
93910695Ssam 	return (status);
94010695Ssam }
94110695Ssam 
94210695Ssam /*
94310695Ssam  * Check user requesting login priviledges.
94410695Ssam  * Disallow anyone mentioned in the file FTPUSERS
94510695Ssam  * to allow people such as uucp to be avoided.
94610695Ssam  */
94710695Ssam checkuser(name)
94810695Ssam 	register char *name;
94910695Ssam {
95010695Ssam 	char line[BUFSIZ], *index();
95110695Ssam 	FILE *fd;
95210695Ssam 	int found = 0;
95310695Ssam 
95410695Ssam 	fd = fopen(FTPUSERS, "r");
95510695Ssam 	if (fd == NULL)
95610695Ssam 		return (1);
95710695Ssam 	while (fgets(line, sizeof (line), fd) != NULL) {
95810695Ssam 		register char *cp = index(line, '\n');
95910695Ssam 
96010695Ssam 		if (cp)
96110695Ssam 			*cp = '\0';
96210695Ssam 		if (strcmp(line, name) == 0) {
96310695Ssam 			found++;
96410695Ssam 			break;
96510695Ssam 		}
96610695Ssam 	}
96710695Ssam 	fclose(fd);
96810695Ssam 	return (!found);
96910695Ssam }
97026044Sminshall 
97126044Sminshall myoob()
97226044Sminshall {
973*26227Ssam 	int aflag = 0, count = 0, iacflag = 0, atmark;
974*26227Ssam 	char c, *cp;
97526044Sminshall 
97626044Sminshall 	if (!transflag) {
97726044Sminshall 		for (;;) {
978*26227Ssam 			if (ioctl(fileno(stdin), SIOCATMARK, &atmark) < 0) {
97926044Sminshall 				perror("ioctl");
98026044Sminshall 				break;
98126044Sminshall 			}
982*26227Ssam 			if (atmark)
98326044Sminshall 				break;
984*26227Ssam 			read(fileno(stdin), &c, 1);
98526044Sminshall 		}
986*26227Ssam 		recv(fileno(stdin), &c, 1, MSG_OOB);
987*26227Ssam 		read(fileno(stdin), &c, 1);
98826044Sminshall 		return;
98926044Sminshall 	}
99026044Sminshall 	for (;;) {
991*26227Ssam 		if (ioctl(fileno(stdin), SIOCATMARK, &atmark) < 0) {
99226044Sminshall 			perror("ioctl");
99326044Sminshall 			break;
99426044Sminshall 		}
995*26227Ssam 		if (atmark)
99626044Sminshall 			break;
997*26227Ssam 		read(fileno(stdin), &c, 1);
998*26227Ssam 		if (c == IAC || c == IP)
99926044Sminshall 			aflag++;
100026044Sminshall 	}
1001*26227Ssam 	recv(fileno(stdin), &c, 1, MSG_OOB);
1002*26227Ssam 	if (c == IAC)
100326044Sminshall 		aflag++;
1004*26227Ssam 	read(fileno(stdin), &c, 1);
1005*26227Ssam 	if (c == DM)
100626044Sminshall 		aflag++;
1007*26227Ssam 	if (aflag != 4)
100826044Sminshall 		return;
100926044Sminshall 	cp = tmpline;
101026044Sminshall 	(void) getline(cp, 7, stdin);
101126044Sminshall 	upper(cp);
1012*26227Ssam 	if (strcmp(cp, "ABOR\r\n"))
101326044Sminshall 		return;
101426044Sminshall 	tmpline[0] = '\0';
101526044Sminshall 	reply(426,"Transfer aborted. Data connection closed.");
101626044Sminshall 	reply(226,"Abort successful");
101726044Sminshall 	longjmp(urgcatch, 1);
101826044Sminshall }
101926044Sminshall 
102026044Sminshall passive()
102126044Sminshall {
102226044Sminshall 	int len;
102326044Sminshall 	struct sockaddr_in tmp;
102426044Sminshall 	register char *p, *a;
102526044Sminshall 
102626044Sminshall 	pdata = socket(AF_INET, SOCK_STREAM, 0);
102726044Sminshall 	if (pdata < 0) {
102826044Sminshall 		reply(451, "Can't open passive connection");
102926044Sminshall 		return;
103026044Sminshall 	}
103126044Sminshall 	tmp = ctrl_addr;
103226044Sminshall 	tmp.sin_port = 0;
103326044Sminshall 	seteuid(0);
103426044Sminshall 	if (bind(pdata, (char *) &tmp, sizeof(tmp), 0) < 0) {
103526044Sminshall 		seteuid(pw->pw_uid);
103626044Sminshall 		(void) close(pdata);
103726044Sminshall 		pdata = -1;
103826044Sminshall 		reply(451, "Can't open passive connection");
103926044Sminshall 		return;
104026044Sminshall 	}
104126044Sminshall 	seteuid(pw->pw_uid);
104226044Sminshall 	len = sizeof(tmp);
104326044Sminshall 	if (getsockname(pdata, (char *) &tmp, &len) < 0) {
104426044Sminshall 		(void) close(pdata);
104526044Sminshall 		pdata = -1;
104626044Sminshall 		reply(451, "Can't open passive connection");
104726044Sminshall 		return;
104826044Sminshall 	}
104926044Sminshall 	if (listen(pdata, 1) < 0) {
105026044Sminshall 		(void) close(pdata);
105126044Sminshall 		pdata = -1;
105226044Sminshall 		reply(451, "Can't open passive connection");
105326044Sminshall 		return;
105426044Sminshall 	}
105526044Sminshall 	a = (char *) &tmp.sin_addr;
105626044Sminshall 	p = (char *) &tmp.sin_port;
105726044Sminshall 
105826044Sminshall #define UC(b) (((int) b) & 0xff)
105926044Sminshall 
106026044Sminshall 	reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
106126044Sminshall 		UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
106226044Sminshall }
106326044Sminshall 
106426044Sminshall char *
106526044Sminshall gunique(local)
106626044Sminshall 	char *local;
106726044Sminshall {
106826044Sminshall 	static char new[MAXPATHLEN];
106926044Sminshall 	char *cp = rindex(local, '/');
107026044Sminshall 	int d, count=0;
107126044Sminshall 	char ext = '1';
107226044Sminshall 
107326044Sminshall 	if (cp) {
107426044Sminshall 		*cp = '\0';
107526044Sminshall 	}
107626044Sminshall 	d = access(cp ? local : ".", 2);
107726044Sminshall 	if (cp) {
107826044Sminshall 		*cp = '/';
107926044Sminshall 	}
108026044Sminshall 	if (d < 0) {
108126044Sminshall 		perror(local);
108226044Sminshall 		return((char *) 0);
108326044Sminshall 	}
108426044Sminshall 	(void) strcpy(new, local);
108526044Sminshall 	cp = new + strlen(new);
108626044Sminshall 	*cp++ = '.';
108726044Sminshall 	while (!d) {
108826044Sminshall 		if (++count == 100) {
108926044Sminshall 			reply(451, "Unique file name not cannot be created.");
109026044Sminshall 			return((char *) 0);
109126044Sminshall 		}
109226044Sminshall 		*cp++ = ext;
109326044Sminshall 		*cp = '\0';
109426044Sminshall 		if (ext == '9') {
109526044Sminshall 			ext = '0';
109626044Sminshall 		}
109726044Sminshall 		else {
109826044Sminshall 			ext++;
109926044Sminshall 		}
110026044Sminshall 		if ((d = access(new, 0)) < 0) {
110126044Sminshall 			break;
110226044Sminshall 		}
110326044Sminshall 		if (ext != '0') {
110426044Sminshall 			cp--;
110526044Sminshall 		}
110626044Sminshall 		else if (*(cp - 2) == '.') {
110726044Sminshall 			*(cp - 1) = '1';
110826044Sminshall 		}
110926044Sminshall 		else {
111026044Sminshall 			*(cp - 2) = *(cp - 2) + 1;
111126044Sminshall 			cp--;
111226044Sminshall 		}
111326044Sminshall 	}
111426044Sminshall 	return(new);
111526044Sminshall }
1116