ftpcmd.y revision 89935
11592Srgrimes/*
21592Srgrimes * Copyright (c) 1985, 1988, 1993, 1994
31592Srgrimes *	The Regents of the University of California.  All rights reserved.
41592Srgrimes *
51592Srgrimes * Redistribution and use in source and binary forms, with or without
61592Srgrimes * modification, are permitted provided that the following conditions
71592Srgrimes * are met:
81592Srgrimes * 1. Redistributions of source code must retain the above copyright
91592Srgrimes *    notice, this list of conditions and the following disclaimer.
101592Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111592Srgrimes *    notice, this list of conditions and the following disclaimer in the
121592Srgrimes *    documentation and/or other materials provided with the distribution.
131592Srgrimes * 3. All advertising materials mentioning features or use of this software
141592Srgrimes *    must display the following acknowledgement:
151592Srgrimes *	This product includes software developed by the University of
161592Srgrimes *	California, Berkeley and its contributors.
171592Srgrimes * 4. Neither the name of the University nor the names of its contributors
181592Srgrimes *    may be used to endorse or promote products derived from this software
191592Srgrimes *    without specific prior written permission.
201592Srgrimes *
211592Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221592Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231592Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241592Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251592Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261592Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271592Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281592Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291592Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301592Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311592Srgrimes * SUCH DAMAGE.
321592Srgrimes *
331592Srgrimes *	@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94
341592Srgrimes */
351592Srgrimes
361592Srgrimes/*
371592Srgrimes * Grammar for FTP commands.
381592Srgrimes * See RFC 959.
391592Srgrimes */
401592Srgrimes
411592Srgrimes%{
421592Srgrimes
431592Srgrimes#ifndef lint
4431329Scharnier#if 0
451592Srgrimesstatic char sccsid[] = "@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94";
4631329Scharnier#endif
4731329Scharnierstatic const char rcsid[] =
4850476Speter  "$FreeBSD: head/libexec/ftpd/ftpcmd.y 89935 2002-01-28 19:28:14Z yar $";
491592Srgrimes#endif /* not lint */
501592Srgrimes
511592Srgrimes#include <sys/param.h>
521592Srgrimes#include <sys/socket.h>
531592Srgrimes#include <sys/stat.h>
541592Srgrimes
551592Srgrimes#include <netinet/in.h>
561592Srgrimes#include <arpa/ftp.h>
571592Srgrimes
581592Srgrimes#include <ctype.h>
591592Srgrimes#include <errno.h>
601592Srgrimes#include <glob.h>
6156668Sshin#include <netdb.h>
621592Srgrimes#include <pwd.h>
631592Srgrimes#include <signal.h>
641592Srgrimes#include <stdio.h>
651592Srgrimes#include <stdlib.h>
661592Srgrimes#include <string.h>
671592Srgrimes#include <syslog.h>
681592Srgrimes#include <time.h>
691592Srgrimes#include <unistd.h>
7013139Speter#include <libutil.h>
7175535Sphk#include <md5.h>
721592Srgrimes
731592Srgrimes#include "extern.h"
741592Srgrimes
7556668Sshinextern	union sockunion data_dest, his_addr;
761592Srgrimesextern	int logged_in;
771592Srgrimesextern	struct passwd *pw;
781592Srgrimesextern	int guest;
7917435Spstextern 	int paranoid;
801592Srgrimesextern	int logging;
811592Srgrimesextern	int type;
821592Srgrimesextern	int form;
8376096Smarkmextern	int ftpdebug;
841592Srgrimesextern	int timeout;
851592Srgrimesextern	int maxtimeout;
861592Srgrimesextern  int pdata;
8727650Sdavidnextern	char *hostname;
8827650Sdavidnextern	char remotehost[];
891592Srgrimesextern	char proctitle[];
901592Srgrimesextern	int usedefault;
911592Srgrimesextern  int transflag;
921592Srgrimesextern  char tmpline[];
9370102Sphkextern	int readonly;
9470102Sphkextern	int noepsv;
9582460Snikextern	int noretr;
9682796Ssheldonhextern	int noguestretr;
971592Srgrimes
981592Srgrimesoff_t	restart_point;
991592Srgrimes
1001592Srgrimesstatic	int cmd_type;
1011592Srgrimesstatic	int cmd_form;
1021592Srgrimesstatic	int cmd_bytesz;
10389935Syarstatic	int state;
1041592Srgrimeschar	cbuf[512];
10588935Sdwmalonechar	*fromname = (char *) 0;
1061592Srgrimes
10756668Sshinextern int epsvall;
10856668Sshin
1091592Srgrimes%}
1101592Srgrimes
1111592Srgrimes%union {
1121592Srgrimes	int	i;
1131592Srgrimes	char   *s;
1141592Srgrimes}
1151592Srgrimes
1161592Srgrimes%token
1171592Srgrimes	A	B	C	E	F	I
1181592Srgrimes	L	N	P	R	S	T
11956668Sshin	ALL
1201592Srgrimes
1211592Srgrimes	SP	CRLF	COMMA
1221592Srgrimes
1231592Srgrimes	USER	PASS	ACCT	REIN	QUIT	PORT
1241592Srgrimes	PASV	TYPE	STRU	MODE	RETR	STOR
1251592Srgrimes	APPE	MLFL	MAIL	MSND	MSOM	MSAM
1261592Srgrimes	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
1271592Srgrimes	ABOR	DELE	CWD	LIST	NLST	SITE
1281592Srgrimes	STAT	HELP	NOOP	MKD	RMD	PWD
1291592Srgrimes	CDUP	STOU	SMNT	SYST	SIZE	MDTM
13056668Sshin	LPRT	LPSV	EPRT	EPSV
1311592Srgrimes
13275535Sphk	UMASK	IDLE	CHMOD	MDFIVE
1331592Srgrimes
1341592Srgrimes	LEXERR
1351592Srgrimes
1361592Srgrimes%token	<s> STRING
1371592Srgrimes%token	<i> NUMBER
1381592Srgrimes
1391592Srgrimes%type	<i> check_login octal_number byte_size
14070102Sphk%type	<i> check_login_ro octal_number byte_size
14170102Sphk%type	<i> check_login_epsv octal_number byte_size
1421592Srgrimes%type	<i> struct_code mode_code type_code form_code
14375567Speter%type	<s> pathstring pathname password username
14456668Sshin%type	<s> ALL
1451592Srgrimes
1461592Srgrimes%start	cmd_list
1471592Srgrimes
1481592Srgrimes%%
1491592Srgrimes
1501592Srgrimescmd_list
1511592Srgrimes	: /* empty */
1521592Srgrimes	| cmd_list cmd
1531592Srgrimes		{
15488935Sdwmalone			if (fromname)
15588935Sdwmalone				free(fromname);
1561592Srgrimes			fromname = (char *) 0;
1571592Srgrimes			restart_point = (off_t) 0;
1581592Srgrimes		}
1591592Srgrimes	| cmd_list rcmd
1601592Srgrimes	;
1611592Srgrimes
1621592Srgrimescmd
1631592Srgrimes	: USER SP username CRLF
1641592Srgrimes		{
1651592Srgrimes			user($3);
1661592Srgrimes			free($3);
1671592Srgrimes		}
1681592Srgrimes	| PASS SP password CRLF
1691592Srgrimes		{
1701592Srgrimes			pass($3);
1711592Srgrimes			free($3);
1721592Srgrimes		}
17375556Sgreen	| PASS CRLF
17475556Sgreen		{
17575556Sgreen			pass("");
17675556Sgreen		}
17717433Spst	| PORT check_login SP host_port CRLF
1781592Srgrimes		{
17956668Sshin			if (epsvall) {
18056668Sshin				reply(501, "no PORT allowed after EPSV ALL");
18156668Sshin				goto port_done;
18256668Sshin			}
18356668Sshin			if (!$2)
18456668Sshin				goto port_done;
18556668Sshin			if (port_check("PORT") == 1)
18656668Sshin				goto port_done;
18756668Sshin#ifdef INET6
18856668Sshin			if ((his_addr.su_family != AF_INET6 ||
18956668Sshin			     !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
19056668Sshin				/* shoud never happen */
19156668Sshin				usedefault = 1;
19256668Sshin				reply(500, "Invalid address rejected.");
19356668Sshin				goto port_done;
19456668Sshin			}
19556668Sshin			port_check_v6("pcmd");
19656668Sshin#endif
19756668Sshin		port_done:
19856668Sshin		}
19956668Sshin	| LPRT check_login SP host_long_port CRLF
20056668Sshin		{
20156668Sshin			if (epsvall) {
20256668Sshin				reply(501, "no LPRT allowed after EPSV ALL");
20356668Sshin				goto lprt_done;
20456668Sshin			}
20556668Sshin			if (!$2)
20656668Sshin				goto lprt_done;
20756668Sshin			if (port_check("LPRT") == 1)
20856668Sshin				goto lprt_done;
20956668Sshin#ifdef INET6
21056668Sshin			if (his_addr.su_family != AF_INET6) {
21156668Sshin				usedefault = 1;
21256668Sshin				reply(500, "Invalid address rejected.");
21356668Sshin				goto lprt_done;
21456668Sshin			}
21556668Sshin			if (port_check_v6("LPRT") == 1)
21656668Sshin				goto lprt_done;
21756668Sshin#endif
21856668Sshin		lprt_done:
21956668Sshin		}
22056668Sshin	| EPRT check_login SP STRING CRLF
22156668Sshin		{
22256668Sshin			char delim;
22356668Sshin			char *tmp = NULL;
22456668Sshin			char *p, *q;
22556668Sshin			char *result[3];
22656668Sshin			struct addrinfo hints;
22756668Sshin			struct addrinfo *res;
22856668Sshin			int i;
22956668Sshin
23056668Sshin			if (epsvall) {
23156668Sshin				reply(501, "no EPRT allowed after EPSV ALL");
23256668Sshin				goto eprt_done;
23356668Sshin			}
23456668Sshin			if (!$2)
23556668Sshin				goto eprt_done;
23656668Sshin
23756668Sshin			memset(&data_dest, 0, sizeof(data_dest));
23856668Sshin			tmp = strdup($4);
23976096Smarkm			if (ftpdebug)
24056668Sshin				syslog(LOG_DEBUG, "%s", tmp);
24156668Sshin			if (!tmp) {
24276096Smarkm				fatalerror("not enough core");
24356668Sshin				/*NOTREACHED*/
24456668Sshin			}
24556668Sshin			p = tmp;
24656668Sshin			delim = p[0];
24756668Sshin			p++;
24856668Sshin			memset(result, 0, sizeof(result));
24956668Sshin			for (i = 0; i < 3; i++) {
25056668Sshin				q = strchr(p, delim);
25156668Sshin				if (!q || *q != delim) {
25256668Sshin		parsefail:
25356668Sshin					reply(500,
25456668Sshin						"Invalid argument, rejected.");
25556668Sshin					if (tmp)
25656668Sshin						free(tmp);
25717433Spst					usedefault = 1;
25856668Sshin					goto eprt_done;
25917433Spst				}
26056668Sshin				*q++ = '\0';
26156668Sshin				result[i] = p;
26276096Smarkm				if (ftpdebug)
26356668Sshin					syslog(LOG_DEBUG, "%d: %s", i, p);
26456668Sshin				p = q;
2651592Srgrimes			}
26656668Sshin
26756668Sshin			/* some more sanity check */
26856668Sshin			p = result[0];
26956668Sshin			while (*p) {
27056668Sshin				if (!isdigit(*p))
27156668Sshin					goto parsefail;
27256668Sshin				p++;
27356668Sshin			}
27456668Sshin			p = result[2];
27556668Sshin			while (*p) {
27656668Sshin				if (!isdigit(*p))
27756668Sshin					goto parsefail;
27856668Sshin				p++;
27956668Sshin			}
28056668Sshin
28156668Sshin			/* grab address */
28256668Sshin			memset(&hints, 0, sizeof(hints));
28356668Sshin			if (atoi(result[0]) == 1)
28456668Sshin				hints.ai_family = PF_INET;
28556668Sshin#ifdef INET6
28656668Sshin			else if (atoi(result[0]) == 2)
28756668Sshin				hints.ai_family = PF_INET6;
28856668Sshin#endif
28956668Sshin			else
29056668Sshin				hints.ai_family = PF_UNSPEC;	/*XXX*/
29156668Sshin			hints.ai_socktype = SOCK_STREAM;
29256668Sshin			i = getaddrinfo(result[1], result[2], &hints, &res);
29356668Sshin			if (i)
29456668Sshin				goto parsefail;
29556668Sshin			memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
29656668Sshin#ifdef INET6
29756668Sshin			if (his_addr.su_family == AF_INET6
29856668Sshin			    && data_dest.su_family == AF_INET6) {
29956668Sshin				/* XXX more sanity checks! */
30056668Sshin				data_dest.su_sin6.sin6_scope_id =
30156668Sshin					his_addr.su_sin6.sin6_scope_id;
30256668Sshin			}
30356668Sshin#endif
30456668Sshin			free(tmp);
30556668Sshin			tmp = NULL;
30656668Sshin
30756668Sshin			if (port_check("EPRT") == 1)
30856668Sshin				goto eprt_done;
30956668Sshin#ifdef INET6
31056668Sshin			if (his_addr.su_family != AF_INET6) {
31156668Sshin				usedefault = 1;
31256668Sshin				reply(500, "Invalid address rejected.");
31356668Sshin				goto eprt_done;
31456668Sshin			}
31556668Sshin			if (port_check_v6("EPRT") == 1)
31656668Sshin				goto eprt_done;
31756668Sshin#endif
31888935Sdwmalone		eprt_done:
31988935Sdwmalone			free($4);
3201592Srgrimes		}
32117433Spst	| PASV check_login CRLF
3221592Srgrimes		{
32356668Sshin			if (epsvall)
32456668Sshin				reply(501, "no PASV allowed after EPSV ALL");
32556668Sshin			else if ($2)
32617433Spst				passive();
3271592Srgrimes		}
32856668Sshin	| LPSV check_login CRLF
32956668Sshin		{
33056668Sshin			if (epsvall)
33156668Sshin				reply(501, "no LPSV allowed after EPSV ALL");
33256668Sshin			else if ($2)
33356668Sshin				long_passive("LPSV", PF_UNSPEC);
33456668Sshin		}
33570102Sphk	| EPSV check_login_epsv SP NUMBER CRLF
33656668Sshin		{
33756668Sshin			if ($2) {
33856668Sshin				int pf;
33956668Sshin				switch ($4) {
34056668Sshin				case 1:
34156668Sshin					pf = PF_INET;
34256668Sshin					break;
34356668Sshin#ifdef INET6
34456668Sshin				case 2:
34556668Sshin					pf = PF_INET6;
34656668Sshin					break;
34756668Sshin#endif
34856668Sshin				default:
34956668Sshin					pf = -1;	/*junk value*/
35056668Sshin					break;
35156668Sshin				}
35256668Sshin				long_passive("EPSV", pf);
35356668Sshin			}
35456668Sshin		}
35570102Sphk	| EPSV check_login_epsv SP ALL CRLF
35656668Sshin		{
35756668Sshin			if ($2) {
35856668Sshin				reply(200,
35956668Sshin				      "EPSV ALL command successful.");
36056668Sshin				epsvall++;
36156668Sshin			}
36256668Sshin		}
36370102Sphk	| EPSV check_login_epsv CRLF
36456668Sshin		{
36556668Sshin			if ($2)
36656668Sshin				long_passive("EPSV", PF_UNSPEC);
36756668Sshin		}
36871278Sjedgar	| TYPE check_login SP type_code CRLF
3691592Srgrimes		{
37071278Sjedgar			if ($2) {
37171278Sjedgar				switch (cmd_type) {
3721592Srgrimes
37371278Sjedgar				case TYPE_A:
37471278Sjedgar					if (cmd_form == FORM_N) {
37571278Sjedgar						reply(200, "Type set to A.");
37671278Sjedgar						type = cmd_type;
37771278Sjedgar						form = cmd_form;
37871278Sjedgar					} else
37971278Sjedgar						reply(504, "Form must be N.");
38071278Sjedgar					break;
3811592Srgrimes
38271278Sjedgar				case TYPE_E:
38371278Sjedgar					reply(504, "Type E not implemented.");
38471278Sjedgar					break;
3851592Srgrimes
38671278Sjedgar				case TYPE_I:
38771278Sjedgar					reply(200, "Type set to I.");
38871278Sjedgar					type = cmd_type;
38971278Sjedgar					break;
3901592Srgrimes
39171278Sjedgar				case TYPE_L:
3921592Srgrimes#if NBBY == 8
39371278Sjedgar					if (cmd_bytesz == 8) {
39471278Sjedgar						reply(200,
39571278Sjedgar						    "Type set to L (byte size 8).");
39671278Sjedgar						type = cmd_type;
39771278Sjedgar					} else
39871278Sjedgar						reply(504, "Byte size must be 8.");
3991592Srgrimes#else /* NBBY == 8 */
40071278Sjedgar					UNIMPLEMENTED for NBBY != 8
4011592Srgrimes#endif /* NBBY == 8 */
40271278Sjedgar				}
4031592Srgrimes			}
4041592Srgrimes		}
40571278Sjedgar	| STRU check_login SP struct_code CRLF
4061592Srgrimes		{
40771278Sjedgar			if ($2) {
40871278Sjedgar				switch ($4) {
4091592Srgrimes
41071278Sjedgar				case STRU_F:
41171278Sjedgar					reply(200, "STRU F ok.");
41271278Sjedgar					break;
4131592Srgrimes
41471278Sjedgar				default:
41571278Sjedgar					reply(504, "Unimplemented STRU type.");
41671278Sjedgar				}
4171592Srgrimes			}
4181592Srgrimes		}
41971278Sjedgar	| MODE check_login SP mode_code CRLF
4201592Srgrimes		{
42171278Sjedgar			if ($2) {
42271278Sjedgar				switch ($4) {
4231592Srgrimes
42471278Sjedgar				case MODE_S:
42571278Sjedgar					reply(200, "MODE S ok.");
42671278Sjedgar					break;
42771278Sjedgar
42871278Sjedgar				default:
42971278Sjedgar					reply(502, "Unimplemented MODE type.");
43071278Sjedgar				}
4311592Srgrimes			}
4321592Srgrimes		}
43371278Sjedgar	| ALLO check_login SP NUMBER CRLF
4341592Srgrimes		{
43571278Sjedgar			if ($2) {
43671278Sjedgar				reply(202, "ALLO command ignored.");
43771278Sjedgar			}
4381592Srgrimes		}
43971278Sjedgar	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
4401592Srgrimes		{
44171278Sjedgar			if ($2) {
44271278Sjedgar				reply(202, "ALLO command ignored.");
44371278Sjedgar			}
4441592Srgrimes		}
4451592Srgrimes	| RETR check_login SP pathname CRLF
4461592Srgrimes		{
44782796Ssheldonh			if (noretr || (guest && noguestretr))
44882460Snik				reply(500, "RETR command is disabled");
44982460Snik			else if ($2 && $4 != NULL)
4501592Srgrimes				retrieve((char *) 0, $4);
45182460Snik
4521592Srgrimes			if ($4 != NULL)
4531592Srgrimes				free($4);
4541592Srgrimes		}
45570102Sphk	| STOR check_login_ro SP pathname CRLF
4561592Srgrimes		{
4571592Srgrimes			if ($2 && $4 != NULL)
4581592Srgrimes				store($4, "w", 0);
4591592Srgrimes			if ($4 != NULL)
4601592Srgrimes				free($4);
4611592Srgrimes		}
46270102Sphk	| APPE check_login_ro SP pathname CRLF
4631592Srgrimes		{
4641592Srgrimes			if ($2 && $4 != NULL)
4651592Srgrimes				store($4, "a", 0);
4661592Srgrimes			if ($4 != NULL)
4671592Srgrimes				free($4);
4681592Srgrimes		}
4691592Srgrimes	| NLST check_login CRLF
4701592Srgrimes		{
4711592Srgrimes			if ($2)
4721592Srgrimes				send_file_list(".");
4731592Srgrimes		}
4741592Srgrimes	| NLST check_login SP STRING CRLF
4751592Srgrimes		{
4761592Srgrimes			if ($2 && $4 != NULL)
4771592Srgrimes				send_file_list($4);
4781592Srgrimes			if ($4 != NULL)
4791592Srgrimes				free($4);
4801592Srgrimes		}
4811592Srgrimes	| LIST check_login CRLF
4821592Srgrimes		{
4831592Srgrimes			if ($2)
4841592Srgrimes				retrieve("/bin/ls -lgA", "");
4851592Srgrimes		}
48675567Speter	| LIST check_login SP pathstring CRLF
4871592Srgrimes		{
4881592Srgrimes			if ($2 && $4 != NULL)
4891592Srgrimes				retrieve("/bin/ls -lgA %s", $4);
4901592Srgrimes			if ($4 != NULL)
4911592Srgrimes				free($4);
4921592Srgrimes		}
4931592Srgrimes	| STAT check_login SP pathname CRLF
4941592Srgrimes		{
4951592Srgrimes			if ($2 && $4 != NULL)
4961592Srgrimes				statfilecmd($4);
4971592Srgrimes			if ($4 != NULL)
4981592Srgrimes				free($4);
4991592Srgrimes		}
50071278Sjedgar	| STAT check_login CRLF
5011592Srgrimes		{
50271278Sjedgar			if ($2) {
50371278Sjedgar				statcmd();
50471278Sjedgar			}
5051592Srgrimes		}
50670102Sphk	| DELE check_login_ro SP pathname CRLF
5071592Srgrimes		{
5081592Srgrimes			if ($2 && $4 != NULL)
5091592Srgrimes				delete($4);
5101592Srgrimes			if ($4 != NULL)
5111592Srgrimes				free($4);
5121592Srgrimes		}
51370102Sphk	| RNTO check_login_ro SP pathname CRLF
5141592Srgrimes		{
51517433Spst			if ($2) {
51617433Spst				if (fromname) {
51717433Spst					renamecmd(fromname, $4);
51817433Spst					free(fromname);
51917433Spst					fromname = (char *) 0;
52017433Spst				} else {
52117433Spst					reply(503, "Bad sequence of commands.");
52217433Spst				}
5231592Srgrimes			}
52417433Spst			free($4);
5251592Srgrimes		}
52671278Sjedgar	| ABOR check_login CRLF
5271592Srgrimes		{
52871278Sjedgar			if ($2)
52971278Sjedgar				reply(225, "ABOR command successful.");
5301592Srgrimes		}
5311592Srgrimes	| CWD check_login CRLF
5321592Srgrimes		{
53369234Sdanny			if ($2) {
53469234Sdanny				if (guest)
53569234Sdanny					cwd("/");
53669234Sdanny				else
53769234Sdanny					cwd(pw->pw_dir);
53869234Sdanny			}
5391592Srgrimes		}
5401592Srgrimes	| CWD check_login SP pathname CRLF
5411592Srgrimes		{
5421592Srgrimes			if ($2 && $4 != NULL)
5431592Srgrimes				cwd($4);
5441592Srgrimes			if ($4 != NULL)
5451592Srgrimes				free($4);
5461592Srgrimes		}
5471592Srgrimes	| HELP CRLF
5481592Srgrimes		{
5491592Srgrimes			help(cmdtab, (char *) 0);
5501592Srgrimes		}
5511592Srgrimes	| HELP SP STRING CRLF
5521592Srgrimes		{
5531592Srgrimes			char *cp = $3;
5541592Srgrimes
5551592Srgrimes			if (strncasecmp(cp, "SITE", 4) == 0) {
5561592Srgrimes				cp = $3 + 4;
5571592Srgrimes				if (*cp == ' ')
5581592Srgrimes					cp++;
5591592Srgrimes				if (*cp)
5601592Srgrimes					help(sitetab, cp);
5611592Srgrimes				else
5621592Srgrimes					help(sitetab, (char *) 0);
5631592Srgrimes			} else
5641592Srgrimes				help(cmdtab, $3);
56588935Sdwmalone			free($3);
5661592Srgrimes		}
5671592Srgrimes	| NOOP CRLF
5681592Srgrimes		{
5691592Srgrimes			reply(200, "NOOP command successful.");
5701592Srgrimes		}
57170102Sphk	| MKD check_login_ro SP pathname CRLF
5721592Srgrimes		{
5731592Srgrimes			if ($2 && $4 != NULL)
5741592Srgrimes				makedir($4);
5751592Srgrimes			if ($4 != NULL)
5761592Srgrimes				free($4);
5771592Srgrimes		}
57870102Sphk	| RMD check_login_ro SP pathname CRLF
5791592Srgrimes		{
5801592Srgrimes			if ($2 && $4 != NULL)
5811592Srgrimes				removedir($4);
5821592Srgrimes			if ($4 != NULL)
5831592Srgrimes				free($4);
5841592Srgrimes		}
5851592Srgrimes	| PWD check_login CRLF
5861592Srgrimes		{
5871592Srgrimes			if ($2)
5881592Srgrimes				pwd();
5891592Srgrimes		}
5901592Srgrimes	| CDUP check_login CRLF
5911592Srgrimes		{
5921592Srgrimes			if ($2)
5931592Srgrimes				cwd("..");
5941592Srgrimes		}
5951592Srgrimes	| SITE SP HELP CRLF
5961592Srgrimes		{
5971592Srgrimes			help(sitetab, (char *) 0);
5981592Srgrimes		}
5991592Srgrimes	| SITE SP HELP SP STRING CRLF
6001592Srgrimes		{
6011592Srgrimes			help(sitetab, $5);
60288935Sdwmalone			free($5);
6031592Srgrimes		}
60475535Sphk	| SITE SP MDFIVE check_login SP pathname CRLF
60575535Sphk		{
60675535Sphk			char p[64], *q;
60775535Sphk
60875535Sphk			if ($4) {
60975535Sphk				q = MD5File($6, p);
61075535Sphk				if (q != NULL)
61175535Sphk					reply(200, "MD5(%s) = %s", $6, p);
61275535Sphk				else
61375535Sphk					perror_reply(550, $6);
61475535Sphk			}
61588935Sdwmalone			if ($6)
61688935Sdwmalone				free($6);
61775535Sphk		}
6181592Srgrimes	| SITE SP UMASK check_login CRLF
6191592Srgrimes		{
6201592Srgrimes			int oldmask;
6211592Srgrimes
6221592Srgrimes			if ($4) {
6231592Srgrimes				oldmask = umask(0);
6241592Srgrimes				(void) umask(oldmask);
6251592Srgrimes				reply(200, "Current UMASK is %03o", oldmask);
6261592Srgrimes			}
6271592Srgrimes		}
6281592Srgrimes	| SITE SP UMASK check_login SP octal_number CRLF
6291592Srgrimes		{
6301592Srgrimes			int oldmask;
6311592Srgrimes
6321592Srgrimes			if ($4) {
6331592Srgrimes				if (($6 == -1) || ($6 > 0777)) {
6341592Srgrimes					reply(501, "Bad UMASK value");
6351592Srgrimes				} else {
6361592Srgrimes					oldmask = umask($6);
6371592Srgrimes					reply(200,
6381592Srgrimes					    "UMASK set to %03o (was %03o)",
6391592Srgrimes					    $6, oldmask);
6401592Srgrimes				}
6411592Srgrimes			}
6421592Srgrimes		}
64370102Sphk	| SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
6441592Srgrimes		{
6451592Srgrimes			if ($4 && ($8 != NULL)) {
6461592Srgrimes				if ($6 > 0777)
6471592Srgrimes					reply(501,
6481592Srgrimes				"CHMOD: Mode value must be between 0 and 0777");
6491592Srgrimes				else if (chmod($8, $6) < 0)
6501592Srgrimes					perror_reply(550, $8);
6511592Srgrimes				else
6521592Srgrimes					reply(200, "CHMOD command successful.");
6531592Srgrimes			}
6541592Srgrimes			if ($8 != NULL)
6551592Srgrimes				free($8);
6561592Srgrimes		}
65771278Sjedgar	| SITE SP check_login IDLE CRLF
6581592Srgrimes		{
65971278Sjedgar			if ($3)
66071278Sjedgar				reply(200,
66171278Sjedgar			    	    "Current IDLE time limit is %d seconds; max %d",
66271278Sjedgar				    timeout, maxtimeout);
6631592Srgrimes		}
66471278Sjedgar	| SITE SP check_login IDLE SP NUMBER CRLF
6651592Srgrimes		{
66671278Sjedgar			if ($3) {
66771278Sjedgar				if ($6 < 30 || $6 > maxtimeout) {
66871278Sjedgar					reply(501,
66971278Sjedgar					    "Maximum IDLE time must be between 30 and %d seconds",
67071278Sjedgar					    maxtimeout);
67171278Sjedgar				} else {
67271278Sjedgar					timeout = $6;
67371278Sjedgar					(void) alarm((unsigned) timeout);
67471278Sjedgar					reply(200,
67571278Sjedgar					    "Maximum IDLE time set to %d seconds",
67671278Sjedgar					    timeout);
67771278Sjedgar				}
6781592Srgrimes			}
6791592Srgrimes		}
68070102Sphk	| STOU check_login_ro SP pathname CRLF
6811592Srgrimes		{
6821592Srgrimes			if ($2 && $4 != NULL)
6831592Srgrimes				store($4, "w", 1);
6841592Srgrimes			if ($4 != NULL)
6851592Srgrimes				free($4);
6861592Srgrimes		}
68771278Sjedgar	| SYST check_login CRLF
6881592Srgrimes		{
68971278Sjedgar			if ($2)
6901592Srgrimes#ifdef unix
6911592Srgrimes#ifdef BSD
6921592Srgrimes			reply(215, "UNIX Type: L%d Version: BSD-%d",
6931592Srgrimes				NBBY, BSD);
6941592Srgrimes#else /* BSD */
6951592Srgrimes			reply(215, "UNIX Type: L%d", NBBY);
6961592Srgrimes#endif /* BSD */
6971592Srgrimes#else /* unix */
6981592Srgrimes			reply(215, "UNKNOWN Type: L%d", NBBY);
6991592Srgrimes#endif /* unix */
7001592Srgrimes		}
7011592Srgrimes
7021592Srgrimes		/*
7031592Srgrimes		 * SIZE is not in RFC959, but Postel has blessed it and
7041592Srgrimes		 * it will be in the updated RFC.
7051592Srgrimes		 *
7061592Srgrimes		 * Return size of file in a format suitable for
7071592Srgrimes		 * using with RESTART (we just count bytes).
7081592Srgrimes		 */
7091592Srgrimes	| SIZE check_login SP pathname CRLF
7101592Srgrimes		{
7111592Srgrimes			if ($2 && $4 != NULL)
7121592Srgrimes				sizecmd($4);
7131592Srgrimes			if ($4 != NULL)
7141592Srgrimes				free($4);
7151592Srgrimes		}
7161592Srgrimes
7171592Srgrimes		/*
7181592Srgrimes		 * MDTM is not in RFC959, but Postel has blessed it and
7191592Srgrimes		 * it will be in the updated RFC.
7201592Srgrimes		 *
7211592Srgrimes		 * Return modification time of file as an ISO 3307
7221592Srgrimes		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
7231592Srgrimes		 * where xxx is the fractional second (of any precision,
7241592Srgrimes		 * not necessarily 3 digits)
7251592Srgrimes		 */
7261592Srgrimes	| MDTM check_login SP pathname CRLF
7271592Srgrimes		{
7281592Srgrimes			if ($2 && $4 != NULL) {
7291592Srgrimes				struct stat stbuf;
7301592Srgrimes				if (stat($4, &stbuf) < 0)
7311592Srgrimes					reply(550, "%s: %s",
7321592Srgrimes					    $4, strerror(errno));
7331592Srgrimes				else if (!S_ISREG(stbuf.st_mode)) {
7341592Srgrimes					reply(550, "%s: not a plain file.", $4);
7351592Srgrimes				} else {
7361592Srgrimes					struct tm *t;
7371592Srgrimes					t = gmtime(&stbuf.st_mtime);
7381592Srgrimes					reply(213,
73917435Spst					    "%04d%02d%02d%02d%02d%02d",
74017435Spst					    1900 + t->tm_year,
74117435Spst					    t->tm_mon+1, t->tm_mday,
7421592Srgrimes					    t->tm_hour, t->tm_min, t->tm_sec);
7431592Srgrimes				}
7441592Srgrimes			}
7451592Srgrimes			if ($4 != NULL)
7461592Srgrimes				free($4);
7471592Srgrimes		}
7481592Srgrimes	| QUIT CRLF
7491592Srgrimes		{
7501592Srgrimes			reply(221, "Goodbye.");
7511592Srgrimes			dologout(0);
7521592Srgrimes		}
75389935Syar	| error
7541592Srgrimes		{
75589935Syar			yyclearin;		/* discard lookahead data */
75689935Syar			yyerrok;		/* clear error condition */
75789935Syar			state = 0;		/* reset lexer state */
7581592Srgrimes		}
7591592Srgrimes	;
7601592Srgrimesrcmd
76170102Sphk	: RNFR check_login_ro SP pathname CRLF
7621592Srgrimes		{
7631592Srgrimes			restart_point = (off_t) 0;
7641592Srgrimes			if ($2 && $4) {
76588935Sdwmalone				if (fromname)
76688935Sdwmalone					free(fromname);
76788935Sdwmalone				fromname = (char *) 0;
76888935Sdwmalone				if (renamefrom($4))
76988935Sdwmalone					fromname = $4;
77088935Sdwmalone				else
7711592Srgrimes					free($4);
77288935Sdwmalone			} else if ($4) {
77388935Sdwmalone				free($4);
7741592Srgrimes			}
7751592Srgrimes		}
77671278Sjedgar	| REST check_login SP byte_size CRLF
7771592Srgrimes		{
77871278Sjedgar			if ($2) {
77988935Sdwmalone				if (fromname)
78088935Sdwmalone					free(fromname);
78171278Sjedgar				fromname = (char *) 0;
78271278Sjedgar				restart_point = $4;  /* XXX $4 is only "int" */
78371278Sjedgar				reply(350, "Restarting at %qd. %s",
78471278Sjedgar				    restart_point,
78571278Sjedgar				    "Send STORE or RETRIEVE to initiate transfer.");
78671278Sjedgar			}
7871592Srgrimes		}
7881592Srgrimes	;
7891592Srgrimes
7901592Srgrimesusername
7911592Srgrimes	: STRING
7921592Srgrimes	;
7931592Srgrimes
7941592Srgrimespassword
7951592Srgrimes	: /* empty */
7961592Srgrimes		{
7971592Srgrimes			$$ = (char *)calloc(1, sizeof(char));
7981592Srgrimes		}
7991592Srgrimes	| STRING
8001592Srgrimes	;
8011592Srgrimes
8021592Srgrimesbyte_size
8031592Srgrimes	: NUMBER
8041592Srgrimes	;
8051592Srgrimes
8061592Srgrimeshost_port
8071592Srgrimes	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
8081592Srgrimes		NUMBER COMMA NUMBER
8091592Srgrimes		{
8101592Srgrimes			char *a, *p;
8111592Srgrimes
81256668Sshin			data_dest.su_len = sizeof(struct sockaddr_in);
81356668Sshin			data_dest.su_family = AF_INET;
81456668Sshin			p = (char *)&data_dest.su_sin.sin_port;
81517435Spst			p[0] = $9; p[1] = $11;
81656668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
8171592Srgrimes			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
8181592Srgrimes		}
8191592Srgrimes	;
8201592Srgrimes
82156668Sshinhost_long_port
82256668Sshin	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
82356668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
82456668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
82556668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
82656668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
82756668Sshin		NUMBER
82856668Sshin		{
82956668Sshin			char *a, *p;
83056668Sshin
83156668Sshin			memset(&data_dest, 0, sizeof(data_dest));
83256668Sshin			data_dest.su_len = sizeof(struct sockaddr_in6);
83356668Sshin			data_dest.su_family = AF_INET6;
83456668Sshin			p = (char *)&data_dest.su_port;
83556668Sshin			p[0] = $39; p[1] = $41;
83656668Sshin			a = (char *)&data_dest.su_sin6.sin6_addr;
83756668Sshin			 a[0] =  $5;  a[1] =  $7;  a[2] =  $9;  a[3] = $11;
83856668Sshin			 a[4] = $13;  a[5] = $15;  a[6] = $17;  a[7] = $19;
83956668Sshin			 a[8] = $21;  a[9] = $23; a[10] = $25; a[11] = $27;
84056668Sshin			a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
84156668Sshin			if (his_addr.su_family == AF_INET6) {
84256668Sshin				/* XXX more sanity checks! */
84356668Sshin				data_dest.su_sin6.sin6_scope_id =
84456668Sshin					his_addr.su_sin6.sin6_scope_id;
84556668Sshin			}
84656668Sshin			if ($1 != 6 || $3 != 16 || $37 != 2)
84756668Sshin				memset(&data_dest, 0, sizeof(data_dest));
84856668Sshin		}
84956668Sshin	| NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
85056668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
85156668Sshin		NUMBER
85256668Sshin		{
85356668Sshin			char *a, *p;
85456668Sshin
85556668Sshin			memset(&data_dest, 0, sizeof(data_dest));
85656668Sshin			data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
85756668Sshin			data_dest.su_family = AF_INET;
85856668Sshin			p = (char *)&data_dest.su_port;
85956668Sshin			p[0] = $15; p[1] = $17;
86056668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
86156668Sshin			a[0] =  $5;  a[1] =  $7;  a[2] =  $9;  a[3] = $11;
86256668Sshin			if ($1 != 4 || $3 != 4 || $13 != 2)
86356668Sshin				memset(&data_dest, 0, sizeof(data_dest));
86456668Sshin		}
86556668Sshin	;
86656668Sshin
8671592Srgrimesform_code
8681592Srgrimes	: N
8691592Srgrimes		{
8701592Srgrimes			$$ = FORM_N;
8711592Srgrimes		}
8721592Srgrimes	| T
8731592Srgrimes		{
8741592Srgrimes			$$ = FORM_T;
8751592Srgrimes		}
8761592Srgrimes	| C
8771592Srgrimes		{
8781592Srgrimes			$$ = FORM_C;
8791592Srgrimes		}
8801592Srgrimes	;
8811592Srgrimes
8821592Srgrimestype_code
8831592Srgrimes	: A
8841592Srgrimes		{
8851592Srgrimes			cmd_type = TYPE_A;
8861592Srgrimes			cmd_form = FORM_N;
8871592Srgrimes		}
8881592Srgrimes	| A SP form_code
8891592Srgrimes		{
8901592Srgrimes			cmd_type = TYPE_A;
8911592Srgrimes			cmd_form = $3;
8921592Srgrimes		}
8931592Srgrimes	| E
8941592Srgrimes		{
8951592Srgrimes			cmd_type = TYPE_E;
8961592Srgrimes			cmd_form = FORM_N;
8971592Srgrimes		}
8981592Srgrimes	| E SP form_code
8991592Srgrimes		{
9001592Srgrimes			cmd_type = TYPE_E;
9011592Srgrimes			cmd_form = $3;
9021592Srgrimes		}
9031592Srgrimes	| I
9041592Srgrimes		{
9051592Srgrimes			cmd_type = TYPE_I;
9061592Srgrimes		}
9071592Srgrimes	| L
9081592Srgrimes		{
9091592Srgrimes			cmd_type = TYPE_L;
9101592Srgrimes			cmd_bytesz = NBBY;
9111592Srgrimes		}
9121592Srgrimes	| L SP byte_size
9131592Srgrimes		{
9141592Srgrimes			cmd_type = TYPE_L;
9151592Srgrimes			cmd_bytesz = $3;
9161592Srgrimes		}
9171592Srgrimes		/* this is for a bug in the BBN ftp */
9181592Srgrimes	| L byte_size
9191592Srgrimes		{
9201592Srgrimes			cmd_type = TYPE_L;
9211592Srgrimes			cmd_bytesz = $2;
9221592Srgrimes		}
9231592Srgrimes	;
9241592Srgrimes
9251592Srgrimesstruct_code
9261592Srgrimes	: F
9271592Srgrimes		{
9281592Srgrimes			$$ = STRU_F;
9291592Srgrimes		}
9301592Srgrimes	| R
9311592Srgrimes		{
9321592Srgrimes			$$ = STRU_R;
9331592Srgrimes		}
9341592Srgrimes	| P
9351592Srgrimes		{
9361592Srgrimes			$$ = STRU_P;
9371592Srgrimes		}
9381592Srgrimes	;
9391592Srgrimes
9401592Srgrimesmode_code
9411592Srgrimes	: S
9421592Srgrimes		{
9431592Srgrimes			$$ = MODE_S;
9441592Srgrimes		}
9451592Srgrimes	| B
9461592Srgrimes		{
9471592Srgrimes			$$ = MODE_B;
9481592Srgrimes		}
9491592Srgrimes	| C
9501592Srgrimes		{
9511592Srgrimes			$$ = MODE_C;
9521592Srgrimes		}
9531592Srgrimes	;
9541592Srgrimes
9551592Srgrimespathname
9561592Srgrimes	: pathstring
9571592Srgrimes		{
9581592Srgrimes			/*
9591592Srgrimes			 * Problem: this production is used for all pathname
9601592Srgrimes			 * processing, but only gives a 550 error reply.
9611592Srgrimes			 * This is a valid reply in some cases but not in others.
9621592Srgrimes			 */
96375567Speter			if (logged_in && $1) {
9641592Srgrimes				glob_t gl;
9651592Srgrimes				int flags =
9661592Srgrimes				 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
9671592Srgrimes
9681592Srgrimes				memset(&gl, 0, sizeof(gl));
96975560Sjedgar				flags |= GLOB_MAXPATH;
97075560Sjedgar				gl.gl_matchc = MAXGLOBARGS;
9711592Srgrimes				if (glob($1, flags, NULL, &gl) ||
9721592Srgrimes				    gl.gl_pathc == 0) {
9731592Srgrimes					reply(550, "not found");
9741592Srgrimes					$$ = NULL;
97575567Speter				} else if (gl.gl_pathc > 1) {
97675567Speter					reply(550, "ambiguous");
97775567Speter					$$ = NULL;
9781592Srgrimes				} else {
9791592Srgrimes					$$ = strdup(gl.gl_pathv[0]);
9801592Srgrimes				}
9811592Srgrimes				globfree(&gl);
9821592Srgrimes				free($1);
9831592Srgrimes			} else
9841592Srgrimes				$$ = $1;
9851592Srgrimes		}
9861592Srgrimes	;
9871592Srgrimes
9881592Srgrimespathstring
9891592Srgrimes	: STRING
9901592Srgrimes	;
9911592Srgrimes
9921592Srgrimesoctal_number
9931592Srgrimes	: NUMBER
9941592Srgrimes		{
9951592Srgrimes			int ret, dec, multby, digit;
9961592Srgrimes
9971592Srgrimes			/*
9981592Srgrimes			 * Convert a number that was read as decimal number
9991592Srgrimes			 * to what it would be if it had been read as octal.
10001592Srgrimes			 */
10011592Srgrimes			dec = $1;
10021592Srgrimes			multby = 1;
10031592Srgrimes			ret = 0;
10041592Srgrimes			while (dec) {
10051592Srgrimes				digit = dec%10;
10061592Srgrimes				if (digit > 7) {
10071592Srgrimes					ret = -1;
10081592Srgrimes					break;
10091592Srgrimes				}
10101592Srgrimes				ret += digit * multby;
10111592Srgrimes				multby *= 8;
10121592Srgrimes				dec /= 10;
10131592Srgrimes			}
10141592Srgrimes			$$ = ret;
10151592Srgrimes		}
10161592Srgrimes	;
10171592Srgrimes
10181592Srgrimes
10191592Srgrimescheck_login
10201592Srgrimes	: /* empty */
10211592Srgrimes		{
102270102Sphk		$$ = check_login1();
10231592Srgrimes		}
10241592Srgrimes	;
10251592Srgrimes
102670102Sphkcheck_login_epsv
102770102Sphk	: /* empty */
102870102Sphk		{
102970102Sphk		if (noepsv) {
103070102Sphk			reply(500, "EPSV command disabled");
103170102Sphk			$$ = 0;
103270102Sphk		}
103370102Sphk		else
103470102Sphk			$$ = check_login1();
103570102Sphk		}
103670102Sphk	;
103770102Sphk
103870102Sphkcheck_login_ro
103970102Sphk	: /* empty */
104070102Sphk		{
104170102Sphk		if (readonly) {
104272710Sdes			reply(550, "Permission denied.");
104370102Sphk			$$ = 0;
104470102Sphk		}
104570102Sphk		else
104670102Sphk			$$ = check_login1();
104770102Sphk		}
104870102Sphk	;
104970102Sphk
10501592Srgrimes%%
10511592Srgrimes
10521592Srgrimes#define	CMD	0	/* beginning of command */
10531592Srgrimes#define	ARGS	1	/* expect miscellaneous arguments */
10541592Srgrimes#define	STR1	2	/* expect SP followed by STRING */
10551592Srgrimes#define	STR2	3	/* expect STRING */
10561592Srgrimes#define	OSTR	4	/* optional SP then STRING */
105775556Sgreen#define	ZSTR1	5	/* optional SP then optional STRING */
10581592Srgrimes#define	ZSTR2	6	/* optional STRING after SP */
10591592Srgrimes#define	SITECMD	7	/* SITE command */
10601592Srgrimes#define	NSTR	8	/* Number followed by a string */
10611592Srgrimes
106275560Sjedgar#define	MAXGLOBARGS	1000
106375560Sjedgar
10641592Srgrimesstruct tab {
10651592Srgrimes	char	*name;
10661592Srgrimes	short	token;
10671592Srgrimes	short	state;
10681592Srgrimes	short	implemented;	/* 1 if command is implemented */
10691592Srgrimes	char	*help;
10701592Srgrimes};
10711592Srgrimes
10721592Srgrimesstruct tab cmdtab[] = {		/* In order defined in RFC 765 */
10731592Srgrimes	{ "USER", USER, STR1, 1,	"<sp> username" },
107475556Sgreen	{ "PASS", PASS, ZSTR1, 1,	"[<sp> [password]]" },
10751592Srgrimes	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
10761592Srgrimes	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
10771592Srgrimes	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
10781592Srgrimes	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
10791592Srgrimes	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
108056668Sshin	{ "LPRT", LPRT, ARGS, 1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
108156668Sshin	{ "EPRT", EPRT, STR1, 1,	"<sp> |af|addr|port|" },
10821592Srgrimes	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
108356668Sshin	{ "LPSV", LPSV, ARGS, 1,	"(set server in passive mode)" },
108456668Sshin	{ "EPSV", EPSV, ARGS, 1,	"[<sp> af|ALL]" },
10851592Srgrimes	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
10861592Srgrimes	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
10871592Srgrimes	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
10881592Srgrimes	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
10891592Srgrimes	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
10901592Srgrimes	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
10911592Srgrimes	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
10921592Srgrimes	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
10931592Srgrimes	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
10941592Srgrimes	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
10951592Srgrimes	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
10961592Srgrimes	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
10971592Srgrimes	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
10981592Srgrimes	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
10991592Srgrimes	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
11001592Srgrimes	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
11011592Srgrimes	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
11021592Srgrimes	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
11031592Srgrimes	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
11041592Srgrimes	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
11051592Srgrimes	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
11061592Srgrimes	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
11071592Srgrimes	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
11081592Srgrimes	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
11091592Srgrimes	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
11101592Srgrimes	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
11111592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
11121592Srgrimes	{ "NOOP", NOOP, ARGS, 1,	"" },
11131592Srgrimes	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
11141592Srgrimes	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
11151592Srgrimes	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
11161592Srgrimes	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
11171592Srgrimes	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
11181592Srgrimes	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
11191592Srgrimes	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
11201592Srgrimes	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
11211592Srgrimes	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
11221592Srgrimes	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
11231592Srgrimes	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
11241592Srgrimes	{ NULL,   0,    0,    0,	0 }
11251592Srgrimes};
11261592Srgrimes
11271592Srgrimesstruct tab sitetab[] = {
112875535Sphk	{ "MD5", MDFIVE, STR1, 1,	"[ <sp> file-name ]" },
11291592Srgrimes	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
11301592Srgrimes	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
11311592Srgrimes	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
11321592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
11331592Srgrimes	{ NULL,   0,    0,    0,	0 }
11341592Srgrimes};
11351592Srgrimes
11361592Srgrimesstatic char	*copy __P((char *));
11371592Srgrimesstatic void	 help __P((struct tab *, char *));
11381592Srgrimesstatic struct tab *
11391592Srgrimes		 lookup __P((struct tab *, char *));
114056668Sshinstatic int	 port_check __P((const char *));
114156668Sshinstatic int	 port_check_v6 __P((const char *));
11421592Srgrimesstatic void	 sizecmd __P((char *));
11431592Srgrimesstatic void	 toolong __P((int));
114456668Sshinstatic void	 v4map_data_dest __P((void));
11451592Srgrimesstatic int	 yylex __P((void));
11461592Srgrimes
11471592Srgrimesstatic struct tab *
11481592Srgrimeslookup(p, cmd)
11491592Srgrimes	struct tab *p;
11501592Srgrimes	char *cmd;
11511592Srgrimes{
11521592Srgrimes
11531592Srgrimes	for (; p->name != NULL; p++)
11541592Srgrimes		if (strcmp(cmd, p->name) == 0)
11551592Srgrimes			return (p);
11561592Srgrimes	return (0);
11571592Srgrimes}
11581592Srgrimes
11591592Srgrimes#include <arpa/telnet.h>
11601592Srgrimes
11611592Srgrimes/*
11621592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes.
11631592Srgrimes */
11641592Srgrimeschar *
11651592Srgrimesgetline(s, n, iop)
11661592Srgrimes	char *s;
11671592Srgrimes	int n;
11681592Srgrimes	FILE *iop;
11691592Srgrimes{
11701592Srgrimes	int c;
11711592Srgrimes	register char *cs;
11721592Srgrimes
11731592Srgrimes	cs = s;
11741592Srgrimes/* tmpline may contain saved command from urgent mode interruption */
11751592Srgrimes	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
11761592Srgrimes		*cs++ = tmpline[c];
11771592Srgrimes		if (tmpline[c] == '\n') {
11781592Srgrimes			*cs++ = '\0';
117976096Smarkm			if (ftpdebug)
11801592Srgrimes				syslog(LOG_DEBUG, "command: %s", s);
11811592Srgrimes			tmpline[0] = '\0';
11821592Srgrimes			return(s);
11831592Srgrimes		}
11841592Srgrimes		if (c == 0)
11851592Srgrimes			tmpline[0] = '\0';
11861592Srgrimes	}
11871592Srgrimes	while ((c = getc(iop)) != EOF) {
11881592Srgrimes		c &= 0377;
11891592Srgrimes		if (c == IAC) {
11901592Srgrimes		    if ((c = getc(iop)) != EOF) {
11911592Srgrimes			c &= 0377;
11921592Srgrimes			switch (c) {
11931592Srgrimes			case WILL:
11941592Srgrimes			case WONT:
11951592Srgrimes				c = getc(iop);
11961592Srgrimes				printf("%c%c%c", IAC, DONT, 0377&c);
11971592Srgrimes				(void) fflush(stdout);
11981592Srgrimes				continue;
11991592Srgrimes			case DO:
12001592Srgrimes			case DONT:
12011592Srgrimes				c = getc(iop);
12021592Srgrimes				printf("%c%c%c", IAC, WONT, 0377&c);
12031592Srgrimes				(void) fflush(stdout);
12041592Srgrimes				continue;
12051592Srgrimes			case IAC:
12061592Srgrimes				break;
12071592Srgrimes			default:
12081592Srgrimes				continue;	/* ignore command */
12091592Srgrimes			}
12101592Srgrimes		    }
12111592Srgrimes		}
12121592Srgrimes		*cs++ = c;
12131592Srgrimes		if (--n <= 0 || c == '\n')
12141592Srgrimes			break;
12151592Srgrimes	}
12161592Srgrimes	if (c == EOF && cs == s)
12171592Srgrimes		return (NULL);
12181592Srgrimes	*cs++ = '\0';
121976096Smarkm	if (ftpdebug) {
12201592Srgrimes		if (!guest && strncasecmp("pass ", s, 5) == 0) {
12211592Srgrimes			/* Don't syslog passwords */
12221592Srgrimes			syslog(LOG_DEBUG, "command: %.5s ???", s);
12231592Srgrimes		} else {
12241592Srgrimes			register char *cp;
12251592Srgrimes			register int len;
12261592Srgrimes
12271592Srgrimes			/* Don't syslog trailing CR-LF */
12281592Srgrimes			len = strlen(s);
12291592Srgrimes			cp = s + len - 1;
12301592Srgrimes			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
12311592Srgrimes				--cp;
12321592Srgrimes				--len;
12331592Srgrimes			}
12341592Srgrimes			syslog(LOG_DEBUG, "command: %.*s", len, s);
12351592Srgrimes		}
12361592Srgrimes	}
12371592Srgrimes	return (s);
12381592Srgrimes}
12391592Srgrimes
12401592Srgrimesstatic void
12411592Srgrimestoolong(signo)
12421592Srgrimes	int signo;
12431592Srgrimes{
12441592Srgrimes
12451592Srgrimes	reply(421,
12461592Srgrimes	    "Timeout (%d seconds): closing control connection.", timeout);
12471592Srgrimes	if (logging)
12481592Srgrimes		syslog(LOG_INFO, "User %s timed out after %d seconds",
12491592Srgrimes		    (pw ? pw -> pw_name : "unknown"), timeout);
12501592Srgrimes	dologout(1);
12511592Srgrimes}
12521592Srgrimes
12531592Srgrimesstatic int
12541592Srgrimesyylex()
12551592Srgrimes{
125689935Syar	static int cpos;
12571592Srgrimes	char *cp, *cp2;
12581592Srgrimes	struct tab *p;
12591592Srgrimes	int n;
12601592Srgrimes	char c;
12611592Srgrimes
12621592Srgrimes	for (;;) {
12631592Srgrimes		switch (state) {
12641592Srgrimes
12651592Srgrimes		case CMD:
12661592Srgrimes			(void) signal(SIGALRM, toolong);
12671592Srgrimes			(void) alarm((unsigned) timeout);
12681592Srgrimes			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
12691592Srgrimes				reply(221, "You could at least say goodbye.");
12701592Srgrimes				dologout(0);
12711592Srgrimes			}
12721592Srgrimes			(void) alarm(0);
12731592Srgrimes#ifdef SETPROCTITLE
127429574Sphk			if (strncasecmp(cbuf, "PASS", 4) != 0)
12751592Srgrimes				setproctitle("%s: %s", proctitle, cbuf);
12761592Srgrimes#endif /* SETPROCTITLE */
12771592Srgrimes			if ((cp = strchr(cbuf, '\r'))) {
12781592Srgrimes				*cp++ = '\n';
12791592Srgrimes				*cp = '\0';
12801592Srgrimes			}
12811592Srgrimes			if ((cp = strpbrk(cbuf, " \n")))
12821592Srgrimes				cpos = cp - cbuf;
12831592Srgrimes			if (cpos == 0)
12841592Srgrimes				cpos = 4;
12851592Srgrimes			c = cbuf[cpos];
12861592Srgrimes			cbuf[cpos] = '\0';
12871592Srgrimes			upper(cbuf);
12881592Srgrimes			p = lookup(cmdtab, cbuf);
12891592Srgrimes			cbuf[cpos] = c;
12903776Spst			if (p != 0) {
12911592Srgrimes				if (p->implemented == 0) {
12921592Srgrimes					nack(p->name);
129389935Syar					return (LEXERR);
12941592Srgrimes				}
12951592Srgrimes				state = p->state;
12961592Srgrimes				yylval.s = p->name;
12971592Srgrimes				return (p->token);
12981592Srgrimes			}
12991592Srgrimes			break;
13001592Srgrimes
13011592Srgrimes		case SITECMD:
13021592Srgrimes			if (cbuf[cpos] == ' ') {
13031592Srgrimes				cpos++;
13041592Srgrimes				return (SP);
13051592Srgrimes			}
13061592Srgrimes			cp = &cbuf[cpos];
13071592Srgrimes			if ((cp2 = strpbrk(cp, " \n")))
13081592Srgrimes				cpos = cp2 - cbuf;
13091592Srgrimes			c = cbuf[cpos];
13101592Srgrimes			cbuf[cpos] = '\0';
13111592Srgrimes			upper(cp);
13121592Srgrimes			p = lookup(sitetab, cp);
13131592Srgrimes			cbuf[cpos] = c;
13143777Spst			if (guest == 0 && p != 0) {
13151592Srgrimes				if (p->implemented == 0) {
13161592Srgrimes					state = CMD;
13171592Srgrimes					nack(p->name);
131889935Syar					return (LEXERR);
13191592Srgrimes				}
13201592Srgrimes				state = p->state;
13211592Srgrimes				yylval.s = p->name;
13221592Srgrimes				return (p->token);
13231592Srgrimes			}
13241592Srgrimes			state = CMD;
13251592Srgrimes			break;
13261592Srgrimes
132775556Sgreen		case ZSTR1:
13281592Srgrimes		case OSTR:
13291592Srgrimes			if (cbuf[cpos] == '\n') {
13301592Srgrimes				state = CMD;
13311592Srgrimes				return (CRLF);
13321592Srgrimes			}
13331592Srgrimes			/* FALLTHROUGH */
13341592Srgrimes
13351592Srgrimes		case STR1:
13361592Srgrimes		dostr1:
13371592Srgrimes			if (cbuf[cpos] == ' ') {
13381592Srgrimes				cpos++;
133951979Salfred				state = state == OSTR ? STR2 : state+1;
13401592Srgrimes				return (SP);
13411592Srgrimes			}
13421592Srgrimes			break;
13431592Srgrimes
13441592Srgrimes		case ZSTR2:
13451592Srgrimes			if (cbuf[cpos] == '\n') {
13461592Srgrimes				state = CMD;
13471592Srgrimes				return (CRLF);
13481592Srgrimes			}
13491592Srgrimes			/* FALLTHROUGH */
13501592Srgrimes
13511592Srgrimes		case STR2:
13521592Srgrimes			cp = &cbuf[cpos];
13531592Srgrimes			n = strlen(cp);
13541592Srgrimes			cpos += n - 1;
13551592Srgrimes			/*
13561592Srgrimes			 * Make sure the string is nonempty and \n terminated.
13571592Srgrimes			 */
13581592Srgrimes			if (n > 1 && cbuf[cpos] == '\n') {
13591592Srgrimes				cbuf[cpos] = '\0';
13601592Srgrimes				yylval.s = copy(cp);
13611592Srgrimes				cbuf[cpos] = '\n';
13621592Srgrimes				state = ARGS;
13631592Srgrimes				return (STRING);
13641592Srgrimes			}
13651592Srgrimes			break;
13661592Srgrimes
13671592Srgrimes		case NSTR:
13681592Srgrimes			if (cbuf[cpos] == ' ') {
13691592Srgrimes				cpos++;
13701592Srgrimes				return (SP);
13711592Srgrimes			}
13721592Srgrimes			if (isdigit(cbuf[cpos])) {
13731592Srgrimes				cp = &cbuf[cpos];
13741592Srgrimes				while (isdigit(cbuf[++cpos]))
13751592Srgrimes					;
13761592Srgrimes				c = cbuf[cpos];
13771592Srgrimes				cbuf[cpos] = '\0';
13781592Srgrimes				yylval.i = atoi(cp);
13791592Srgrimes				cbuf[cpos] = c;
13801592Srgrimes				state = STR1;
13811592Srgrimes				return (NUMBER);
13821592Srgrimes			}
13831592Srgrimes			state = STR1;
13841592Srgrimes			goto dostr1;
13851592Srgrimes
13861592Srgrimes		case ARGS:
13871592Srgrimes			if (isdigit(cbuf[cpos])) {
13881592Srgrimes				cp = &cbuf[cpos];
13891592Srgrimes				while (isdigit(cbuf[++cpos]))
13901592Srgrimes					;
13911592Srgrimes				c = cbuf[cpos];
13921592Srgrimes				cbuf[cpos] = '\0';
13931592Srgrimes				yylval.i = atoi(cp);
13941592Srgrimes				cbuf[cpos] = c;
13951592Srgrimes				return (NUMBER);
13961592Srgrimes			}
139756668Sshin			if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
139856668Sshin			 && !isalnum(cbuf[cpos + 3])) {
139956668Sshin				cpos += 3;
140056668Sshin				return ALL;
140156668Sshin			}
14021592Srgrimes			switch (cbuf[cpos++]) {
14031592Srgrimes
14041592Srgrimes			case '\n':
14051592Srgrimes				state = CMD;
14061592Srgrimes				return (CRLF);
14071592Srgrimes
14081592Srgrimes			case ' ':
14091592Srgrimes				return (SP);
14101592Srgrimes
14111592Srgrimes			case ',':
14121592Srgrimes				return (COMMA);
14131592Srgrimes
14141592Srgrimes			case 'A':
14151592Srgrimes			case 'a':
14161592Srgrimes				return (A);
14171592Srgrimes
14181592Srgrimes			case 'B':
14191592Srgrimes			case 'b':
14201592Srgrimes				return (B);
14211592Srgrimes
14221592Srgrimes			case 'C':
14231592Srgrimes			case 'c':
14241592Srgrimes				return (C);
14251592Srgrimes
14261592Srgrimes			case 'E':
14271592Srgrimes			case 'e':
14281592Srgrimes				return (E);
14291592Srgrimes
14301592Srgrimes			case 'F':
14311592Srgrimes			case 'f':
14321592Srgrimes				return (F);
14331592Srgrimes
14341592Srgrimes			case 'I':
14351592Srgrimes			case 'i':
14361592Srgrimes				return (I);
14371592Srgrimes
14381592Srgrimes			case 'L':
14391592Srgrimes			case 'l':
14401592Srgrimes				return (L);
14411592Srgrimes
14421592Srgrimes			case 'N':
14431592Srgrimes			case 'n':
14441592Srgrimes				return (N);
14451592Srgrimes
14461592Srgrimes			case 'P':
14471592Srgrimes			case 'p':
14481592Srgrimes				return (P);
14491592Srgrimes
14501592Srgrimes			case 'R':
14511592Srgrimes			case 'r':
14521592Srgrimes				return (R);
14531592Srgrimes
14541592Srgrimes			case 'S':
14551592Srgrimes			case 's':
14561592Srgrimes				return (S);
14571592Srgrimes
14581592Srgrimes			case 'T':
14591592Srgrimes			case 't':
14601592Srgrimes				return (T);
14611592Srgrimes
14621592Srgrimes			}
14631592Srgrimes			break;
14641592Srgrimes
14651592Srgrimes		default:
146676096Smarkm			fatalerror("Unknown state in scanner.");
14671592Srgrimes		}
14681592Srgrimes		state = CMD;
146989935Syar		return (LEXERR);
14701592Srgrimes	}
14711592Srgrimes}
14721592Srgrimes
14731592Srgrimesvoid
14741592Srgrimesupper(s)
14751592Srgrimes	char *s;
14761592Srgrimes{
14771592Srgrimes	while (*s != '\0') {
14781592Srgrimes		if (islower(*s))
14791592Srgrimes			*s = toupper(*s);
14801592Srgrimes		s++;
14811592Srgrimes	}
14821592Srgrimes}
14831592Srgrimes
14841592Srgrimesstatic char *
14851592Srgrimescopy(s)
14861592Srgrimes	char *s;
14871592Srgrimes{
14881592Srgrimes	char *p;
14891592Srgrimes
14901592Srgrimes	p = malloc((unsigned) strlen(s) + 1);
14911592Srgrimes	if (p == NULL)
149276096Smarkm		fatalerror("Ran out of memory.");
14931592Srgrimes	(void) strcpy(p, s);
14941592Srgrimes	return (p);
14951592Srgrimes}
14961592Srgrimes
14971592Srgrimesstatic void
14981592Srgrimeshelp(ctab, s)
14991592Srgrimes	struct tab *ctab;
15001592Srgrimes	char *s;
15011592Srgrimes{
15021592Srgrimes	struct tab *c;
15031592Srgrimes	int width, NCMDS;
15041592Srgrimes	char *type;
15051592Srgrimes
15061592Srgrimes	if (ctab == sitetab)
15071592Srgrimes		type = "SITE ";
15081592Srgrimes	else
15091592Srgrimes		type = "";
15101592Srgrimes	width = 0, NCMDS = 0;
15111592Srgrimes	for (c = ctab; c->name != NULL; c++) {
15121592Srgrimes		int len = strlen(c->name);
15131592Srgrimes
15141592Srgrimes		if (len > width)
15151592Srgrimes			width = len;
15161592Srgrimes		NCMDS++;
15171592Srgrimes	}
15181592Srgrimes	width = (width + 8) &~ 7;
15191592Srgrimes	if (s == 0) {
15201592Srgrimes		int i, j, w;
15211592Srgrimes		int columns, lines;
15221592Srgrimes
15231592Srgrimes		lreply(214, "The following %scommands are recognized %s.",
15241592Srgrimes		    type, "(* =>'s unimplemented)");
15251592Srgrimes		columns = 76 / width;
15261592Srgrimes		if (columns == 0)
15271592Srgrimes			columns = 1;
15281592Srgrimes		lines = (NCMDS + columns - 1) / columns;
15291592Srgrimes		for (i = 0; i < lines; i++) {
15301592Srgrimes			printf("   ");
15311592Srgrimes			for (j = 0; j < columns; j++) {
15321592Srgrimes				c = ctab + j * lines + i;
15331592Srgrimes				printf("%s%c", c->name,
15341592Srgrimes					c->implemented ? ' ' : '*');
15351592Srgrimes				if (c + lines >= &ctab[NCMDS])
15361592Srgrimes					break;
15371592Srgrimes				w = strlen(c->name) + 1;
15381592Srgrimes				while (w < width) {
15391592Srgrimes					putchar(' ');
15401592Srgrimes					w++;
15411592Srgrimes				}
15421592Srgrimes			}
15431592Srgrimes			printf("\r\n");
15441592Srgrimes		}
15451592Srgrimes		(void) fflush(stdout);
15461592Srgrimes		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
15471592Srgrimes		return;
15481592Srgrimes	}
15491592Srgrimes	upper(s);
15501592Srgrimes	c = lookup(ctab, s);
15511592Srgrimes	if (c == (struct tab *)0) {
15521592Srgrimes		reply(502, "Unknown command %s.", s);
15531592Srgrimes		return;
15541592Srgrimes	}
15551592Srgrimes	if (c->implemented)
15561592Srgrimes		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
15571592Srgrimes	else
15581592Srgrimes		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
15591592Srgrimes		    c->name, c->help);
15601592Srgrimes}
15611592Srgrimes
15621592Srgrimesstatic void
15631592Srgrimessizecmd(filename)
15641592Srgrimes	char *filename;
15651592Srgrimes{
15661592Srgrimes	switch (type) {
15671592Srgrimes	case TYPE_L:
15681592Srgrimes	case TYPE_I: {
15691592Srgrimes		struct stat stbuf;
157063350Sdes		if (stat(filename, &stbuf) < 0)
157163350Sdes			perror_reply(550, filename);
157263350Sdes		else if (!S_ISREG(stbuf.st_mode))
15731592Srgrimes			reply(550, "%s: not a plain file.", filename);
15741592Srgrimes		else
15751592Srgrimes			reply(213, "%qu", stbuf.st_size);
15761592Srgrimes		break; }
15771592Srgrimes	case TYPE_A: {
15781592Srgrimes		FILE *fin;
15791592Srgrimes		int c;
15801592Srgrimes		off_t count;
15811592Srgrimes		struct stat stbuf;
15821592Srgrimes		fin = fopen(filename, "r");
15831592Srgrimes		if (fin == NULL) {
15841592Srgrimes			perror_reply(550, filename);
15851592Srgrimes			return;
15861592Srgrimes		}
158763350Sdes		if (fstat(fileno(fin), &stbuf) < 0) {
158863350Sdes			perror_reply(550, filename);
158963350Sdes			(void) fclose(fin);
159063350Sdes			return;
159163350Sdes		} else if (!S_ISREG(stbuf.st_mode)) {
15921592Srgrimes			reply(550, "%s: not a plain file.", filename);
15931592Srgrimes			(void) fclose(fin);
15941592Srgrimes			return;
15951592Srgrimes		}
15961592Srgrimes
15971592Srgrimes		count = 0;
15981592Srgrimes		while((c=getc(fin)) != EOF) {
15991592Srgrimes			if (c == '\n')	/* will get expanded to \r\n */
16001592Srgrimes				count++;
16011592Srgrimes			count++;
16021592Srgrimes		}
16031592Srgrimes		(void) fclose(fin);
16041592Srgrimes
16051592Srgrimes		reply(213, "%qd", count);
16061592Srgrimes		break; }
16071592Srgrimes	default:
16081592Srgrimes		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
16091592Srgrimes	}
16101592Srgrimes}
161156668Sshin
161256668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
161356668Sshinstatic int
161456668Sshinport_check(pcmd)
161556668Sshin	const char *pcmd;
161656668Sshin{
161756668Sshin	if (his_addr.su_family == AF_INET) {
161856668Sshin		if (data_dest.su_family != AF_INET) {
161956668Sshin			usedefault = 1;
162056668Sshin			reply(500, "Invalid address rejected.");
162156668Sshin			return 1;
162256668Sshin		}
162356668Sshin		if (paranoid &&
162456668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
162556668Sshin		     memcmp(&data_dest.su_sin.sin_addr,
162656668Sshin			    &his_addr.su_sin.sin_addr,
162756668Sshin			    sizeof(data_dest.su_sin.sin_addr)))) {
162856668Sshin			usedefault = 1;
162956668Sshin			reply(500, "Illegal PORT range rejected.");
163056668Sshin		} else {
163156668Sshin			usedefault = 0;
163256668Sshin			if (pdata >= 0) {
163356668Sshin				(void) close(pdata);
163456668Sshin				pdata = -1;
163556668Sshin			}
163656668Sshin			reply(200, "%s command successful.", pcmd);
163756668Sshin		}
163856668Sshin		return 1;
163956668Sshin	}
164056668Sshin	return 0;
164156668Sshin}
164256668Sshin
164370102Sphkstatic int
164470102Sphkcheck_login1()
164570102Sphk{
164670102Sphk	if (logged_in)
164770102Sphk		return 1;
164870102Sphk	else {
164970102Sphk		reply(530, "Please login with USER and PASS.");
165070102Sphk		return 0;
165170102Sphk	}
165270102Sphk}
165370102Sphk
165456668Sshin#ifdef INET6
165556668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
165656668Sshinstatic int
165756668Sshinport_check_v6(pcmd)
165856668Sshin	const char *pcmd;
165956668Sshin{
166056668Sshin	if (his_addr.su_family == AF_INET6) {
166156668Sshin		if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
166256668Sshin			/* Convert data_dest into v4 mapped sockaddr.*/
166356668Sshin			v4map_data_dest();
166456668Sshin		if (data_dest.su_family != AF_INET6) {
166556668Sshin			usedefault = 1;
166656668Sshin			reply(500, "Invalid address rejected.");
166756668Sshin			return 1;
166856668Sshin		}
166956668Sshin		if (paranoid &&
167056668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
167156668Sshin		     memcmp(&data_dest.su_sin6.sin6_addr,
167256668Sshin			    &his_addr.su_sin6.sin6_addr,
167356668Sshin			    sizeof(data_dest.su_sin6.sin6_addr)))) {
167456668Sshin			usedefault = 1;
167556668Sshin			reply(500, "Illegal PORT range rejected.");
167656668Sshin		} else {
167756668Sshin			usedefault = 0;
167856668Sshin			if (pdata >= 0) {
167956668Sshin				(void) close(pdata);
168056668Sshin				pdata = -1;
168156668Sshin			}
168256668Sshin			reply(200, "%s command successful.", pcmd);
168356668Sshin		}
168456668Sshin		return 1;
168556668Sshin	}
168656668Sshin	return 0;
168756668Sshin}
168856668Sshin
168956668Sshinstatic void
169056668Sshinv4map_data_dest()
169156668Sshin{
169256668Sshin	struct in_addr savedaddr;
169356668Sshin	int savedport;
169456668Sshin
169556668Sshin	if (data_dest.su_family != AF_INET) {
169656668Sshin		usedefault = 1;
169756668Sshin		reply(500, "Invalid address rejected.");
169856668Sshin		return;
169956668Sshin	}
170056668Sshin
170156668Sshin	savedaddr = data_dest.su_sin.sin_addr;
170256668Sshin	savedport = data_dest.su_port;
170356668Sshin
170456668Sshin	memset(&data_dest, 0, sizeof(data_dest));
170556668Sshin	data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
170656668Sshin	data_dest.su_sin6.sin6_family = AF_INET6;
170756668Sshin	data_dest.su_sin6.sin6_port = savedport;
170856668Sshin	memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
170956668Sshin	memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
171056668Sshin	       (caddr_t)&savedaddr, sizeof(savedaddr));
171156668Sshin}
171256668Sshin#endif
1713