ftpcmd.y revision 82460
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 82460 2001-08-28 11:59:21Z nik $";
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 <setjmp.h>
641592Srgrimes#include <signal.h>
651592Srgrimes#include <stdio.h>
661592Srgrimes#include <stdlib.h>
671592Srgrimes#include <string.h>
681592Srgrimes#include <syslog.h>
691592Srgrimes#include <time.h>
701592Srgrimes#include <unistd.h>
7113139Speter#include <libutil.h>
7275535Sphk#include <md5.h>
731592Srgrimes
741592Srgrimes#include "extern.h"
751592Srgrimes
7656668Sshinextern	union sockunion data_dest, his_addr;
771592Srgrimesextern	int logged_in;
781592Srgrimesextern	struct passwd *pw;
791592Srgrimesextern	int guest;
8017435Spstextern 	int paranoid;
811592Srgrimesextern	int logging;
821592Srgrimesextern	int type;
831592Srgrimesextern	int form;
8476096Smarkmextern	int ftpdebug;
851592Srgrimesextern	int timeout;
861592Srgrimesextern	int maxtimeout;
871592Srgrimesextern  int pdata;
8827650Sdavidnextern	char *hostname;
8927650Sdavidnextern	char remotehost[];
901592Srgrimesextern	char proctitle[];
911592Srgrimesextern	int usedefault;
921592Srgrimesextern  int transflag;
931592Srgrimesextern  char tmpline[];
9470102Sphkextern	int readonly;
9570102Sphkextern	int noepsv;
9682460Snikextern	int noretr;
971592Srgrimes
981592Srgrimesoff_t	restart_point;
991592Srgrimes
1001592Srgrimesstatic	int cmd_type;
1011592Srgrimesstatic	int cmd_form;
1021592Srgrimesstatic	int cmd_bytesz;
1031592Srgrimeschar	cbuf[512];
1041592Srgrimeschar	*fromname;
1051592Srgrimes
10656668Sshinextern int epsvall;
10756668Sshin
1081592Srgrimes%}
1091592Srgrimes
1101592Srgrimes%union {
1111592Srgrimes	int	i;
1121592Srgrimes	char   *s;
1131592Srgrimes}
1141592Srgrimes
1151592Srgrimes%token
1161592Srgrimes	A	B	C	E	F	I
1171592Srgrimes	L	N	P	R	S	T
11856668Sshin	ALL
1191592Srgrimes
1201592Srgrimes	SP	CRLF	COMMA
1211592Srgrimes
1221592Srgrimes	USER	PASS	ACCT	REIN	QUIT	PORT
1231592Srgrimes	PASV	TYPE	STRU	MODE	RETR	STOR
1241592Srgrimes	APPE	MLFL	MAIL	MSND	MSOM	MSAM
1251592Srgrimes	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
1261592Srgrimes	ABOR	DELE	CWD	LIST	NLST	SITE
1271592Srgrimes	STAT	HELP	NOOP	MKD	RMD	PWD
1281592Srgrimes	CDUP	STOU	SMNT	SYST	SIZE	MDTM
12956668Sshin	LPRT	LPSV	EPRT	EPSV
1301592Srgrimes
13175535Sphk	UMASK	IDLE	CHMOD	MDFIVE
1321592Srgrimes
1331592Srgrimes	LEXERR
1341592Srgrimes
1351592Srgrimes%token	<s> STRING
1361592Srgrimes%token	<i> NUMBER
1371592Srgrimes
1381592Srgrimes%type	<i> check_login octal_number byte_size
13970102Sphk%type	<i> check_login_ro octal_number byte_size
14070102Sphk%type	<i> check_login_epsv octal_number byte_size
1411592Srgrimes%type	<i> struct_code mode_code type_code form_code
14275567Speter%type	<s> pathstring pathname password username
14356668Sshin%type	<s> ALL
1441592Srgrimes
1451592Srgrimes%start	cmd_list
1461592Srgrimes
1471592Srgrimes%%
1481592Srgrimes
1491592Srgrimescmd_list
1501592Srgrimes	: /* empty */
1511592Srgrimes	| cmd_list cmd
1521592Srgrimes		{
1531592Srgrimes			fromname = (char *) 0;
1541592Srgrimes			restart_point = (off_t) 0;
1551592Srgrimes		}
1561592Srgrimes	| cmd_list rcmd
1571592Srgrimes	;
1581592Srgrimes
1591592Srgrimescmd
1601592Srgrimes	: USER SP username CRLF
1611592Srgrimes		{
1621592Srgrimes			user($3);
1631592Srgrimes			free($3);
1641592Srgrimes		}
1651592Srgrimes	| PASS SP password CRLF
1661592Srgrimes		{
1671592Srgrimes			pass($3);
1681592Srgrimes			free($3);
1691592Srgrimes		}
17075556Sgreen	| PASS CRLF
17175556Sgreen		{
17275556Sgreen			pass("");
17375556Sgreen		}
17417433Spst	| PORT check_login SP host_port CRLF
1751592Srgrimes		{
17656668Sshin			if (epsvall) {
17756668Sshin				reply(501, "no PORT allowed after EPSV ALL");
17856668Sshin				goto port_done;
17956668Sshin			}
18056668Sshin			if (!$2)
18156668Sshin				goto port_done;
18256668Sshin			if (port_check("PORT") == 1)
18356668Sshin				goto port_done;
18456668Sshin#ifdef INET6
18556668Sshin			if ((his_addr.su_family != AF_INET6 ||
18656668Sshin			     !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
18756668Sshin				/* shoud never happen */
18856668Sshin				usedefault = 1;
18956668Sshin				reply(500, "Invalid address rejected.");
19056668Sshin				goto port_done;
19156668Sshin			}
19256668Sshin			port_check_v6("pcmd");
19356668Sshin#endif
19456668Sshin		port_done:
19556668Sshin		}
19656668Sshin	| LPRT check_login SP host_long_port CRLF
19756668Sshin		{
19856668Sshin			if (epsvall) {
19956668Sshin				reply(501, "no LPRT allowed after EPSV ALL");
20056668Sshin				goto lprt_done;
20156668Sshin			}
20256668Sshin			if (!$2)
20356668Sshin				goto lprt_done;
20456668Sshin			if (port_check("LPRT") == 1)
20556668Sshin				goto lprt_done;
20656668Sshin#ifdef INET6
20756668Sshin			if (his_addr.su_family != AF_INET6) {
20856668Sshin				usedefault = 1;
20956668Sshin				reply(500, "Invalid address rejected.");
21056668Sshin				goto lprt_done;
21156668Sshin			}
21256668Sshin			if (port_check_v6("LPRT") == 1)
21356668Sshin				goto lprt_done;
21456668Sshin#endif
21556668Sshin		lprt_done:
21656668Sshin		}
21756668Sshin	| EPRT check_login SP STRING CRLF
21856668Sshin		{
21956668Sshin			char delim;
22056668Sshin			char *tmp = NULL;
22156668Sshin			char *p, *q;
22256668Sshin			char *result[3];
22356668Sshin			struct addrinfo hints;
22456668Sshin			struct addrinfo *res;
22556668Sshin			int i;
22656668Sshin
22756668Sshin			if (epsvall) {
22856668Sshin				reply(501, "no EPRT allowed after EPSV ALL");
22956668Sshin				goto eprt_done;
23056668Sshin			}
23156668Sshin			if (!$2)
23256668Sshin				goto eprt_done;
23356668Sshin
23456668Sshin			memset(&data_dest, 0, sizeof(data_dest));
23556668Sshin			tmp = strdup($4);
23676096Smarkm			if (ftpdebug)
23756668Sshin				syslog(LOG_DEBUG, "%s", tmp);
23856668Sshin			if (!tmp) {
23976096Smarkm				fatalerror("not enough core");
24056668Sshin				/*NOTREACHED*/
24156668Sshin			}
24256668Sshin			p = tmp;
24356668Sshin			delim = p[0];
24456668Sshin			p++;
24556668Sshin			memset(result, 0, sizeof(result));
24656668Sshin			for (i = 0; i < 3; i++) {
24756668Sshin				q = strchr(p, delim);
24856668Sshin				if (!q || *q != delim) {
24956668Sshin		parsefail:
25056668Sshin					reply(500,
25156668Sshin						"Invalid argument, rejected.");
25256668Sshin					if (tmp)
25356668Sshin						free(tmp);
25417433Spst					usedefault = 1;
25556668Sshin					goto eprt_done;
25617433Spst				}
25756668Sshin				*q++ = '\0';
25856668Sshin				result[i] = p;
25976096Smarkm				if (ftpdebug)
26056668Sshin					syslog(LOG_DEBUG, "%d: %s", i, p);
26156668Sshin				p = q;
2621592Srgrimes			}
26356668Sshin
26456668Sshin			/* some more sanity check */
26556668Sshin			p = result[0];
26656668Sshin			while (*p) {
26756668Sshin				if (!isdigit(*p))
26856668Sshin					goto parsefail;
26956668Sshin				p++;
27056668Sshin			}
27156668Sshin			p = result[2];
27256668Sshin			while (*p) {
27356668Sshin				if (!isdigit(*p))
27456668Sshin					goto parsefail;
27556668Sshin				p++;
27656668Sshin			}
27756668Sshin
27856668Sshin			/* grab address */
27956668Sshin			memset(&hints, 0, sizeof(hints));
28056668Sshin			if (atoi(result[0]) == 1)
28156668Sshin				hints.ai_family = PF_INET;
28256668Sshin#ifdef INET6
28356668Sshin			else if (atoi(result[0]) == 2)
28456668Sshin				hints.ai_family = PF_INET6;
28556668Sshin#endif
28656668Sshin			else
28756668Sshin				hints.ai_family = PF_UNSPEC;	/*XXX*/
28856668Sshin			hints.ai_socktype = SOCK_STREAM;
28956668Sshin			i = getaddrinfo(result[1], result[2], &hints, &res);
29056668Sshin			if (i)
29156668Sshin				goto parsefail;
29256668Sshin			memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
29356668Sshin#ifdef INET6
29456668Sshin			if (his_addr.su_family == AF_INET6
29556668Sshin			    && data_dest.su_family == AF_INET6) {
29656668Sshin				/* XXX more sanity checks! */
29756668Sshin				data_dest.su_sin6.sin6_scope_id =
29856668Sshin					his_addr.su_sin6.sin6_scope_id;
29956668Sshin			}
30056668Sshin#endif
30156668Sshin			free(tmp);
30256668Sshin			tmp = NULL;
30356668Sshin
30456668Sshin			if (port_check("EPRT") == 1)
30556668Sshin				goto eprt_done;
30656668Sshin#ifdef INET6
30756668Sshin			if (his_addr.su_family != AF_INET6) {
30856668Sshin				usedefault = 1;
30956668Sshin				reply(500, "Invalid address rejected.");
31056668Sshin				goto eprt_done;
31156668Sshin			}
31256668Sshin			if (port_check_v6("EPRT") == 1)
31356668Sshin				goto eprt_done;
31456668Sshin#endif
31556668Sshin		eprt_done:;
3161592Srgrimes		}
31717433Spst	| PASV check_login CRLF
3181592Srgrimes		{
31956668Sshin			if (epsvall)
32056668Sshin				reply(501, "no PASV allowed after EPSV ALL");
32156668Sshin			else if ($2)
32217433Spst				passive();
3231592Srgrimes		}
32456668Sshin	| LPSV check_login CRLF
32556668Sshin		{
32656668Sshin			if (epsvall)
32756668Sshin				reply(501, "no LPSV allowed after EPSV ALL");
32856668Sshin			else if ($2)
32956668Sshin				long_passive("LPSV", PF_UNSPEC);
33056668Sshin		}
33170102Sphk	| EPSV check_login_epsv SP NUMBER CRLF
33256668Sshin		{
33356668Sshin			if ($2) {
33456668Sshin				int pf;
33556668Sshin				switch ($4) {
33656668Sshin				case 1:
33756668Sshin					pf = PF_INET;
33856668Sshin					break;
33956668Sshin#ifdef INET6
34056668Sshin				case 2:
34156668Sshin					pf = PF_INET6;
34256668Sshin					break;
34356668Sshin#endif
34456668Sshin				default:
34556668Sshin					pf = -1;	/*junk value*/
34656668Sshin					break;
34756668Sshin				}
34856668Sshin				long_passive("EPSV", pf);
34956668Sshin			}
35056668Sshin		}
35170102Sphk	| EPSV check_login_epsv SP ALL CRLF
35256668Sshin		{
35356668Sshin			if ($2) {
35456668Sshin				reply(200,
35556668Sshin				      "EPSV ALL command successful.");
35656668Sshin				epsvall++;
35756668Sshin			}
35856668Sshin		}
35970102Sphk	| EPSV check_login_epsv CRLF
36056668Sshin		{
36156668Sshin			if ($2)
36256668Sshin				long_passive("EPSV", PF_UNSPEC);
36356668Sshin		}
36471278Sjedgar	| TYPE check_login SP type_code CRLF
3651592Srgrimes		{
36671278Sjedgar			if ($2) {
36771278Sjedgar				switch (cmd_type) {
3681592Srgrimes
36971278Sjedgar				case TYPE_A:
37071278Sjedgar					if (cmd_form == FORM_N) {
37171278Sjedgar						reply(200, "Type set to A.");
37271278Sjedgar						type = cmd_type;
37371278Sjedgar						form = cmd_form;
37471278Sjedgar					} else
37571278Sjedgar						reply(504, "Form must be N.");
37671278Sjedgar					break;
3771592Srgrimes
37871278Sjedgar				case TYPE_E:
37971278Sjedgar					reply(504, "Type E not implemented.");
38071278Sjedgar					break;
3811592Srgrimes
38271278Sjedgar				case TYPE_I:
38371278Sjedgar					reply(200, "Type set to I.");
38471278Sjedgar					type = cmd_type;
38571278Sjedgar					break;
3861592Srgrimes
38771278Sjedgar				case TYPE_L:
3881592Srgrimes#if NBBY == 8
38971278Sjedgar					if (cmd_bytesz == 8) {
39071278Sjedgar						reply(200,
39171278Sjedgar						    "Type set to L (byte size 8).");
39271278Sjedgar						type = cmd_type;
39371278Sjedgar					} else
39471278Sjedgar						reply(504, "Byte size must be 8.");
3951592Srgrimes#else /* NBBY == 8 */
39671278Sjedgar					UNIMPLEMENTED for NBBY != 8
3971592Srgrimes#endif /* NBBY == 8 */
39871278Sjedgar				}
3991592Srgrimes			}
4001592Srgrimes		}
40171278Sjedgar	| STRU check_login SP struct_code CRLF
4021592Srgrimes		{
40371278Sjedgar			if ($2) {
40471278Sjedgar				switch ($4) {
4051592Srgrimes
40671278Sjedgar				case STRU_F:
40771278Sjedgar					reply(200, "STRU F ok.");
40871278Sjedgar					break;
4091592Srgrimes
41071278Sjedgar				default:
41171278Sjedgar					reply(504, "Unimplemented STRU type.");
41271278Sjedgar				}
4131592Srgrimes			}
4141592Srgrimes		}
41571278Sjedgar	| MODE check_login SP mode_code CRLF
4161592Srgrimes		{
41771278Sjedgar			if ($2) {
41871278Sjedgar				switch ($4) {
4191592Srgrimes
42071278Sjedgar				case MODE_S:
42171278Sjedgar					reply(200, "MODE S ok.");
42271278Sjedgar					break;
42371278Sjedgar
42471278Sjedgar				default:
42571278Sjedgar					reply(502, "Unimplemented MODE type.");
42671278Sjedgar				}
4271592Srgrimes			}
4281592Srgrimes		}
42971278Sjedgar	| ALLO check_login SP NUMBER CRLF
4301592Srgrimes		{
43171278Sjedgar			if ($2) {
43271278Sjedgar				reply(202, "ALLO command ignored.");
43371278Sjedgar			}
4341592Srgrimes		}
43571278Sjedgar	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
4361592Srgrimes		{
43771278Sjedgar			if ($2) {
43871278Sjedgar				reply(202, "ALLO command ignored.");
43971278Sjedgar			}
4401592Srgrimes		}
4411592Srgrimes	| RETR check_login SP pathname CRLF
4421592Srgrimes		{
44382460Snik			if (noretr)
44482460Snik				reply(500, "RETR command is disabled");
44582460Snik			else if ($2 && $4 != NULL)
4461592Srgrimes				retrieve((char *) 0, $4);
44782460Snik
4481592Srgrimes			if ($4 != NULL)
4491592Srgrimes				free($4);
4501592Srgrimes		}
45170102Sphk	| STOR check_login_ro SP pathname CRLF
4521592Srgrimes		{
4531592Srgrimes			if ($2 && $4 != NULL)
4541592Srgrimes				store($4, "w", 0);
4551592Srgrimes			if ($4 != NULL)
4561592Srgrimes				free($4);
4571592Srgrimes		}
45870102Sphk	| APPE check_login_ro SP pathname CRLF
4591592Srgrimes		{
4601592Srgrimes			if ($2 && $4 != NULL)
4611592Srgrimes				store($4, "a", 0);
4621592Srgrimes			if ($4 != NULL)
4631592Srgrimes				free($4);
4641592Srgrimes		}
4651592Srgrimes	| NLST check_login CRLF
4661592Srgrimes		{
4671592Srgrimes			if ($2)
4681592Srgrimes				send_file_list(".");
4691592Srgrimes		}
4701592Srgrimes	| NLST check_login SP STRING CRLF
4711592Srgrimes		{
4721592Srgrimes			if ($2 && $4 != NULL)
4731592Srgrimes				send_file_list($4);
4741592Srgrimes			if ($4 != NULL)
4751592Srgrimes				free($4);
4761592Srgrimes		}
4771592Srgrimes	| LIST check_login CRLF
4781592Srgrimes		{
4791592Srgrimes			if ($2)
4801592Srgrimes				retrieve("/bin/ls -lgA", "");
4811592Srgrimes		}
48275567Speter	| LIST check_login SP pathstring CRLF
4831592Srgrimes		{
4841592Srgrimes			if ($2 && $4 != NULL)
4851592Srgrimes				retrieve("/bin/ls -lgA %s", $4);
4861592Srgrimes			if ($4 != NULL)
4871592Srgrimes				free($4);
4881592Srgrimes		}
4891592Srgrimes	| STAT check_login SP pathname CRLF
4901592Srgrimes		{
4911592Srgrimes			if ($2 && $4 != NULL)
4921592Srgrimes				statfilecmd($4);
4931592Srgrimes			if ($4 != NULL)
4941592Srgrimes				free($4);
4951592Srgrimes		}
49671278Sjedgar	| STAT check_login CRLF
4971592Srgrimes		{
49871278Sjedgar			if ($2) {
49971278Sjedgar				statcmd();
50071278Sjedgar			}
5011592Srgrimes		}
50270102Sphk	| DELE check_login_ro SP pathname CRLF
5031592Srgrimes		{
5041592Srgrimes			if ($2 && $4 != NULL)
5051592Srgrimes				delete($4);
5061592Srgrimes			if ($4 != NULL)
5071592Srgrimes				free($4);
5081592Srgrimes		}
50970102Sphk	| RNTO check_login_ro SP pathname CRLF
5101592Srgrimes		{
51117433Spst			if ($2) {
51217433Spst				if (fromname) {
51317433Spst					renamecmd(fromname, $4);
51417433Spst					free(fromname);
51517433Spst					fromname = (char *) 0;
51617433Spst				} else {
51717433Spst					reply(503, "Bad sequence of commands.");
51817433Spst				}
5191592Srgrimes			}
52017433Spst			free($4);
5211592Srgrimes		}
52271278Sjedgar	| ABOR check_login CRLF
5231592Srgrimes		{
52471278Sjedgar			if ($2)
52571278Sjedgar				reply(225, "ABOR command successful.");
5261592Srgrimes		}
5271592Srgrimes	| CWD check_login CRLF
5281592Srgrimes		{
52969234Sdanny			if ($2) {
53069234Sdanny				if (guest)
53169234Sdanny					cwd("/");
53269234Sdanny				else
53369234Sdanny					cwd(pw->pw_dir);
53469234Sdanny			}
5351592Srgrimes		}
5361592Srgrimes	| CWD check_login SP pathname CRLF
5371592Srgrimes		{
5381592Srgrimes			if ($2 && $4 != NULL)
5391592Srgrimes				cwd($4);
5401592Srgrimes			if ($4 != NULL)
5411592Srgrimes				free($4);
5421592Srgrimes		}
5431592Srgrimes	| HELP CRLF
5441592Srgrimes		{
5451592Srgrimes			help(cmdtab, (char *) 0);
5461592Srgrimes		}
5471592Srgrimes	| HELP SP STRING CRLF
5481592Srgrimes		{
5491592Srgrimes			char *cp = $3;
5501592Srgrimes
5511592Srgrimes			if (strncasecmp(cp, "SITE", 4) == 0) {
5521592Srgrimes				cp = $3 + 4;
5531592Srgrimes				if (*cp == ' ')
5541592Srgrimes					cp++;
5551592Srgrimes				if (*cp)
5561592Srgrimes					help(sitetab, cp);
5571592Srgrimes				else
5581592Srgrimes					help(sitetab, (char *) 0);
5591592Srgrimes			} else
5601592Srgrimes				help(cmdtab, $3);
5611592Srgrimes		}
5621592Srgrimes	| NOOP CRLF
5631592Srgrimes		{
5641592Srgrimes			reply(200, "NOOP command successful.");
5651592Srgrimes		}
56670102Sphk	| MKD check_login_ro SP pathname CRLF
5671592Srgrimes		{
5681592Srgrimes			if ($2 && $4 != NULL)
5691592Srgrimes				makedir($4);
5701592Srgrimes			if ($4 != NULL)
5711592Srgrimes				free($4);
5721592Srgrimes		}
57370102Sphk	| RMD check_login_ro SP pathname CRLF
5741592Srgrimes		{
5751592Srgrimes			if ($2 && $4 != NULL)
5761592Srgrimes				removedir($4);
5771592Srgrimes			if ($4 != NULL)
5781592Srgrimes				free($4);
5791592Srgrimes		}
5801592Srgrimes	| PWD check_login CRLF
5811592Srgrimes		{
5821592Srgrimes			if ($2)
5831592Srgrimes				pwd();
5841592Srgrimes		}
5851592Srgrimes	| CDUP check_login CRLF
5861592Srgrimes		{
5871592Srgrimes			if ($2)
5881592Srgrimes				cwd("..");
5891592Srgrimes		}
5901592Srgrimes	| SITE SP HELP CRLF
5911592Srgrimes		{
5921592Srgrimes			help(sitetab, (char *) 0);
5931592Srgrimes		}
5941592Srgrimes	| SITE SP HELP SP STRING CRLF
5951592Srgrimes		{
5961592Srgrimes			help(sitetab, $5);
5971592Srgrimes		}
59875535Sphk	| SITE SP MDFIVE check_login SP pathname CRLF
59975535Sphk		{
60075535Sphk			char p[64], *q;
60175535Sphk
60275535Sphk			if ($4) {
60375535Sphk				q = MD5File($6, p);
60475535Sphk				if (q != NULL)
60575535Sphk					reply(200, "MD5(%s) = %s", $6, p);
60675535Sphk				else
60775535Sphk					perror_reply(550, $6);
60875535Sphk			}
60975535Sphk		}
6101592Srgrimes	| SITE SP UMASK check_login CRLF
6111592Srgrimes		{
6121592Srgrimes			int oldmask;
6131592Srgrimes
6141592Srgrimes			if ($4) {
6151592Srgrimes				oldmask = umask(0);
6161592Srgrimes				(void) umask(oldmask);
6171592Srgrimes				reply(200, "Current UMASK is %03o", oldmask);
6181592Srgrimes			}
6191592Srgrimes		}
6201592Srgrimes	| SITE SP UMASK check_login SP octal_number CRLF
6211592Srgrimes		{
6221592Srgrimes			int oldmask;
6231592Srgrimes
6241592Srgrimes			if ($4) {
6251592Srgrimes				if (($6 == -1) || ($6 > 0777)) {
6261592Srgrimes					reply(501, "Bad UMASK value");
6271592Srgrimes				} else {
6281592Srgrimes					oldmask = umask($6);
6291592Srgrimes					reply(200,
6301592Srgrimes					    "UMASK set to %03o (was %03o)",
6311592Srgrimes					    $6, oldmask);
6321592Srgrimes				}
6331592Srgrimes			}
6341592Srgrimes		}
63570102Sphk	| SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
6361592Srgrimes		{
6371592Srgrimes			if ($4 && ($8 != NULL)) {
6381592Srgrimes				if ($6 > 0777)
6391592Srgrimes					reply(501,
6401592Srgrimes				"CHMOD: Mode value must be between 0 and 0777");
6411592Srgrimes				else if (chmod($8, $6) < 0)
6421592Srgrimes					perror_reply(550, $8);
6431592Srgrimes				else
6441592Srgrimes					reply(200, "CHMOD command successful.");
6451592Srgrimes			}
6461592Srgrimes			if ($8 != NULL)
6471592Srgrimes				free($8);
6481592Srgrimes		}
64971278Sjedgar	| SITE SP check_login IDLE CRLF
6501592Srgrimes		{
65171278Sjedgar			if ($3)
65271278Sjedgar				reply(200,
65371278Sjedgar			    	    "Current IDLE time limit is %d seconds; max %d",
65471278Sjedgar				    timeout, maxtimeout);
6551592Srgrimes		}
65671278Sjedgar	| SITE SP check_login IDLE SP NUMBER CRLF
6571592Srgrimes		{
65871278Sjedgar			if ($3) {
65971278Sjedgar				if ($6 < 30 || $6 > maxtimeout) {
66071278Sjedgar					reply(501,
66171278Sjedgar					    "Maximum IDLE time must be between 30 and %d seconds",
66271278Sjedgar					    maxtimeout);
66371278Sjedgar				} else {
66471278Sjedgar					timeout = $6;
66571278Sjedgar					(void) alarm((unsigned) timeout);
66671278Sjedgar					reply(200,
66771278Sjedgar					    "Maximum IDLE time set to %d seconds",
66871278Sjedgar					    timeout);
66971278Sjedgar				}
6701592Srgrimes			}
6711592Srgrimes		}
67270102Sphk	| STOU check_login_ro SP pathname CRLF
6731592Srgrimes		{
6741592Srgrimes			if ($2 && $4 != NULL)
6751592Srgrimes				store($4, "w", 1);
6761592Srgrimes			if ($4 != NULL)
6771592Srgrimes				free($4);
6781592Srgrimes		}
67971278Sjedgar	| SYST check_login CRLF
6801592Srgrimes		{
68171278Sjedgar			if ($2)
6821592Srgrimes#ifdef unix
6831592Srgrimes#ifdef BSD
6841592Srgrimes			reply(215, "UNIX Type: L%d Version: BSD-%d",
6851592Srgrimes				NBBY, BSD);
6861592Srgrimes#else /* BSD */
6871592Srgrimes			reply(215, "UNIX Type: L%d", NBBY);
6881592Srgrimes#endif /* BSD */
6891592Srgrimes#else /* unix */
6901592Srgrimes			reply(215, "UNKNOWN Type: L%d", NBBY);
6911592Srgrimes#endif /* unix */
6921592Srgrimes		}
6931592Srgrimes
6941592Srgrimes		/*
6951592Srgrimes		 * SIZE is not in RFC959, but Postel has blessed it and
6961592Srgrimes		 * it will be in the updated RFC.
6971592Srgrimes		 *
6981592Srgrimes		 * Return size of file in a format suitable for
6991592Srgrimes		 * using with RESTART (we just count bytes).
7001592Srgrimes		 */
7011592Srgrimes	| SIZE check_login SP pathname CRLF
7021592Srgrimes		{
7031592Srgrimes			if ($2 && $4 != NULL)
7041592Srgrimes				sizecmd($4);
7051592Srgrimes			if ($4 != NULL)
7061592Srgrimes				free($4);
7071592Srgrimes		}
7081592Srgrimes
7091592Srgrimes		/*
7101592Srgrimes		 * MDTM is not in RFC959, but Postel has blessed it and
7111592Srgrimes		 * it will be in the updated RFC.
7121592Srgrimes		 *
7131592Srgrimes		 * Return modification time of file as an ISO 3307
7141592Srgrimes		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
7151592Srgrimes		 * where xxx is the fractional second (of any precision,
7161592Srgrimes		 * not necessarily 3 digits)
7171592Srgrimes		 */
7181592Srgrimes	| MDTM check_login SP pathname CRLF
7191592Srgrimes		{
7201592Srgrimes			if ($2 && $4 != NULL) {
7211592Srgrimes				struct stat stbuf;
7221592Srgrimes				if (stat($4, &stbuf) < 0)
7231592Srgrimes					reply(550, "%s: %s",
7241592Srgrimes					    $4, strerror(errno));
7251592Srgrimes				else if (!S_ISREG(stbuf.st_mode)) {
7261592Srgrimes					reply(550, "%s: not a plain file.", $4);
7271592Srgrimes				} else {
7281592Srgrimes					struct tm *t;
7291592Srgrimes					t = gmtime(&stbuf.st_mtime);
7301592Srgrimes					reply(213,
73117435Spst					    "%04d%02d%02d%02d%02d%02d",
73217435Spst					    1900 + t->tm_year,
73317435Spst					    t->tm_mon+1, t->tm_mday,
7341592Srgrimes					    t->tm_hour, t->tm_min, t->tm_sec);
7351592Srgrimes				}
7361592Srgrimes			}
7371592Srgrimes			if ($4 != NULL)
7381592Srgrimes				free($4);
7391592Srgrimes		}
7401592Srgrimes	| QUIT CRLF
7411592Srgrimes		{
7421592Srgrimes			reply(221, "Goodbye.");
7431592Srgrimes			dologout(0);
7441592Srgrimes		}
7451592Srgrimes	| error CRLF
7461592Srgrimes		{
7471592Srgrimes			yyerrok;
7481592Srgrimes		}
7491592Srgrimes	;
7501592Srgrimesrcmd
75170102Sphk	: RNFR check_login_ro SP pathname CRLF
7521592Srgrimes		{
7531592Srgrimes			char *renamefrom();
7541592Srgrimes
7551592Srgrimes			restart_point = (off_t) 0;
7561592Srgrimes			if ($2 && $4) {
7571592Srgrimes				fromname = renamefrom($4);
7581592Srgrimes				if (fromname == (char *) 0 && $4) {
7591592Srgrimes					free($4);
7601592Srgrimes				}
7611592Srgrimes			}
7621592Srgrimes		}
76371278Sjedgar	| REST check_login SP byte_size CRLF
7641592Srgrimes		{
76571278Sjedgar			if ($2) {
76671278Sjedgar				fromname = (char *) 0;
76771278Sjedgar				restart_point = $4;  /* XXX $4 is only "int" */
76871278Sjedgar				reply(350, "Restarting at %qd. %s",
76971278Sjedgar				    restart_point,
77071278Sjedgar				    "Send STORE or RETRIEVE to initiate transfer.");
77171278Sjedgar			}
7721592Srgrimes		}
7731592Srgrimes	;
7741592Srgrimes
7751592Srgrimesusername
7761592Srgrimes	: STRING
7771592Srgrimes	;
7781592Srgrimes
7791592Srgrimespassword
7801592Srgrimes	: /* empty */
7811592Srgrimes		{
7821592Srgrimes			$$ = (char *)calloc(1, sizeof(char));
7831592Srgrimes		}
7841592Srgrimes	| STRING
7851592Srgrimes	;
7861592Srgrimes
7871592Srgrimesbyte_size
7881592Srgrimes	: NUMBER
7891592Srgrimes	;
7901592Srgrimes
7911592Srgrimeshost_port
7921592Srgrimes	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
7931592Srgrimes		NUMBER COMMA NUMBER
7941592Srgrimes		{
7951592Srgrimes			char *a, *p;
7961592Srgrimes
79756668Sshin			data_dest.su_len = sizeof(struct sockaddr_in);
79856668Sshin			data_dest.su_family = AF_INET;
79956668Sshin			p = (char *)&data_dest.su_sin.sin_port;
80017435Spst			p[0] = $9; p[1] = $11;
80156668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
8021592Srgrimes			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
8031592Srgrimes		}
8041592Srgrimes	;
8051592Srgrimes
80656668Sshinhost_long_port
80756668Sshin	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
80856668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
80956668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
81056668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
81156668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
81256668Sshin		NUMBER
81356668Sshin		{
81456668Sshin			char *a, *p;
81556668Sshin
81656668Sshin			memset(&data_dest, 0, sizeof(data_dest));
81756668Sshin			data_dest.su_len = sizeof(struct sockaddr_in6);
81856668Sshin			data_dest.su_family = AF_INET6;
81956668Sshin			p = (char *)&data_dest.su_port;
82056668Sshin			p[0] = $39; p[1] = $41;
82156668Sshin			a = (char *)&data_dest.su_sin6.sin6_addr;
82256668Sshin			 a[0] =  $5;  a[1] =  $7;  a[2] =  $9;  a[3] = $11;
82356668Sshin			 a[4] = $13;  a[5] = $15;  a[6] = $17;  a[7] = $19;
82456668Sshin			 a[8] = $21;  a[9] = $23; a[10] = $25; a[11] = $27;
82556668Sshin			a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
82656668Sshin			if (his_addr.su_family == AF_INET6) {
82756668Sshin				/* XXX more sanity checks! */
82856668Sshin				data_dest.su_sin6.sin6_scope_id =
82956668Sshin					his_addr.su_sin6.sin6_scope_id;
83056668Sshin			}
83156668Sshin			if ($1 != 6 || $3 != 16 || $37 != 2)
83256668Sshin				memset(&data_dest, 0, sizeof(data_dest));
83356668Sshin		}
83456668Sshin	| NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
83556668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
83656668Sshin		NUMBER
83756668Sshin		{
83856668Sshin			char *a, *p;
83956668Sshin
84056668Sshin			memset(&data_dest, 0, sizeof(data_dest));
84156668Sshin			data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
84256668Sshin			data_dest.su_family = AF_INET;
84356668Sshin			p = (char *)&data_dest.su_port;
84456668Sshin			p[0] = $15; p[1] = $17;
84556668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
84656668Sshin			a[0] =  $5;  a[1] =  $7;  a[2] =  $9;  a[3] = $11;
84756668Sshin			if ($1 != 4 || $3 != 4 || $13 != 2)
84856668Sshin				memset(&data_dest, 0, sizeof(data_dest));
84956668Sshin		}
85056668Sshin	;
85156668Sshin
8521592Srgrimesform_code
8531592Srgrimes	: N
8541592Srgrimes		{
8551592Srgrimes			$$ = FORM_N;
8561592Srgrimes		}
8571592Srgrimes	| T
8581592Srgrimes		{
8591592Srgrimes			$$ = FORM_T;
8601592Srgrimes		}
8611592Srgrimes	| C
8621592Srgrimes		{
8631592Srgrimes			$$ = FORM_C;
8641592Srgrimes		}
8651592Srgrimes	;
8661592Srgrimes
8671592Srgrimestype_code
8681592Srgrimes	: A
8691592Srgrimes		{
8701592Srgrimes			cmd_type = TYPE_A;
8711592Srgrimes			cmd_form = FORM_N;
8721592Srgrimes		}
8731592Srgrimes	| A SP form_code
8741592Srgrimes		{
8751592Srgrimes			cmd_type = TYPE_A;
8761592Srgrimes			cmd_form = $3;
8771592Srgrimes		}
8781592Srgrimes	| E
8791592Srgrimes		{
8801592Srgrimes			cmd_type = TYPE_E;
8811592Srgrimes			cmd_form = FORM_N;
8821592Srgrimes		}
8831592Srgrimes	| E SP form_code
8841592Srgrimes		{
8851592Srgrimes			cmd_type = TYPE_E;
8861592Srgrimes			cmd_form = $3;
8871592Srgrimes		}
8881592Srgrimes	| I
8891592Srgrimes		{
8901592Srgrimes			cmd_type = TYPE_I;
8911592Srgrimes		}
8921592Srgrimes	| L
8931592Srgrimes		{
8941592Srgrimes			cmd_type = TYPE_L;
8951592Srgrimes			cmd_bytesz = NBBY;
8961592Srgrimes		}
8971592Srgrimes	| L SP byte_size
8981592Srgrimes		{
8991592Srgrimes			cmd_type = TYPE_L;
9001592Srgrimes			cmd_bytesz = $3;
9011592Srgrimes		}
9021592Srgrimes		/* this is for a bug in the BBN ftp */
9031592Srgrimes	| L byte_size
9041592Srgrimes		{
9051592Srgrimes			cmd_type = TYPE_L;
9061592Srgrimes			cmd_bytesz = $2;
9071592Srgrimes		}
9081592Srgrimes	;
9091592Srgrimes
9101592Srgrimesstruct_code
9111592Srgrimes	: F
9121592Srgrimes		{
9131592Srgrimes			$$ = STRU_F;
9141592Srgrimes		}
9151592Srgrimes	| R
9161592Srgrimes		{
9171592Srgrimes			$$ = STRU_R;
9181592Srgrimes		}
9191592Srgrimes	| P
9201592Srgrimes		{
9211592Srgrimes			$$ = STRU_P;
9221592Srgrimes		}
9231592Srgrimes	;
9241592Srgrimes
9251592Srgrimesmode_code
9261592Srgrimes	: S
9271592Srgrimes		{
9281592Srgrimes			$$ = MODE_S;
9291592Srgrimes		}
9301592Srgrimes	| B
9311592Srgrimes		{
9321592Srgrimes			$$ = MODE_B;
9331592Srgrimes		}
9341592Srgrimes	| C
9351592Srgrimes		{
9361592Srgrimes			$$ = MODE_C;
9371592Srgrimes		}
9381592Srgrimes	;
9391592Srgrimes
9401592Srgrimespathname
9411592Srgrimes	: pathstring
9421592Srgrimes		{
9431592Srgrimes			/*
9441592Srgrimes			 * Problem: this production is used for all pathname
9451592Srgrimes			 * processing, but only gives a 550 error reply.
9461592Srgrimes			 * This is a valid reply in some cases but not in others.
9471592Srgrimes			 */
94875567Speter			if (logged_in && $1) {
9491592Srgrimes				glob_t gl;
9501592Srgrimes				int flags =
9511592Srgrimes				 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
9521592Srgrimes
9531592Srgrimes				memset(&gl, 0, sizeof(gl));
95475560Sjedgar				flags |= GLOB_MAXPATH;
95575560Sjedgar				gl.gl_matchc = MAXGLOBARGS;
9561592Srgrimes				if (glob($1, flags, NULL, &gl) ||
9571592Srgrimes				    gl.gl_pathc == 0) {
9581592Srgrimes					reply(550, "not found");
9591592Srgrimes					$$ = NULL;
96075567Speter				} else if (gl.gl_pathc > 1) {
96175567Speter					reply(550, "ambiguous");
96275567Speter					$$ = NULL;
9631592Srgrimes				} else {
9641592Srgrimes					$$ = strdup(gl.gl_pathv[0]);
9651592Srgrimes				}
9661592Srgrimes				globfree(&gl);
9671592Srgrimes				free($1);
9681592Srgrimes			} else
9691592Srgrimes				$$ = $1;
9701592Srgrimes		}
9711592Srgrimes	;
9721592Srgrimes
9731592Srgrimespathstring
9741592Srgrimes	: STRING
9751592Srgrimes	;
9761592Srgrimes
9771592Srgrimesoctal_number
9781592Srgrimes	: NUMBER
9791592Srgrimes		{
9801592Srgrimes			int ret, dec, multby, digit;
9811592Srgrimes
9821592Srgrimes			/*
9831592Srgrimes			 * Convert a number that was read as decimal number
9841592Srgrimes			 * to what it would be if it had been read as octal.
9851592Srgrimes			 */
9861592Srgrimes			dec = $1;
9871592Srgrimes			multby = 1;
9881592Srgrimes			ret = 0;
9891592Srgrimes			while (dec) {
9901592Srgrimes				digit = dec%10;
9911592Srgrimes				if (digit > 7) {
9921592Srgrimes					ret = -1;
9931592Srgrimes					break;
9941592Srgrimes				}
9951592Srgrimes				ret += digit * multby;
9961592Srgrimes				multby *= 8;
9971592Srgrimes				dec /= 10;
9981592Srgrimes			}
9991592Srgrimes			$$ = ret;
10001592Srgrimes		}
10011592Srgrimes	;
10021592Srgrimes
10031592Srgrimes
10041592Srgrimescheck_login
10051592Srgrimes	: /* empty */
10061592Srgrimes		{
100770102Sphk		$$ = check_login1();
10081592Srgrimes		}
10091592Srgrimes	;
10101592Srgrimes
101170102Sphkcheck_login_epsv
101270102Sphk	: /* empty */
101370102Sphk		{
101470102Sphk		if (noepsv) {
101570102Sphk			reply(500, "EPSV command disabled");
101670102Sphk			$$ = 0;
101770102Sphk		}
101870102Sphk		else
101970102Sphk			$$ = check_login1();
102070102Sphk		}
102170102Sphk	;
102270102Sphk
102370102Sphkcheck_login_ro
102470102Sphk	: /* empty */
102570102Sphk		{
102670102Sphk		if (readonly) {
102772710Sdes			reply(550, "Permission denied.");
102870102Sphk			$$ = 0;
102970102Sphk		}
103070102Sphk		else
103170102Sphk			$$ = check_login1();
103270102Sphk		}
103370102Sphk	;
103470102Sphk
10351592Srgrimes%%
10361592Srgrimes
10371592Srgrimesextern jmp_buf errcatch;
10381592Srgrimes
10391592Srgrimes#define	CMD	0	/* beginning of command */
10401592Srgrimes#define	ARGS	1	/* expect miscellaneous arguments */
10411592Srgrimes#define	STR1	2	/* expect SP followed by STRING */
10421592Srgrimes#define	STR2	3	/* expect STRING */
10431592Srgrimes#define	OSTR	4	/* optional SP then STRING */
104475556Sgreen#define	ZSTR1	5	/* optional SP then optional STRING */
10451592Srgrimes#define	ZSTR2	6	/* optional STRING after SP */
10461592Srgrimes#define	SITECMD	7	/* SITE command */
10471592Srgrimes#define	NSTR	8	/* Number followed by a string */
10481592Srgrimes
104975560Sjedgar#define	MAXGLOBARGS	1000
105075560Sjedgar
10511592Srgrimesstruct tab {
10521592Srgrimes	char	*name;
10531592Srgrimes	short	token;
10541592Srgrimes	short	state;
10551592Srgrimes	short	implemented;	/* 1 if command is implemented */
10561592Srgrimes	char	*help;
10571592Srgrimes};
10581592Srgrimes
10591592Srgrimesstruct tab cmdtab[] = {		/* In order defined in RFC 765 */
10601592Srgrimes	{ "USER", USER, STR1, 1,	"<sp> username" },
106175556Sgreen	{ "PASS", PASS, ZSTR1, 1,	"[<sp> [password]]" },
10621592Srgrimes	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
10631592Srgrimes	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
10641592Srgrimes	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
10651592Srgrimes	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
10661592Srgrimes	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
106756668Sshin	{ "LPRT", LPRT, ARGS, 1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
106856668Sshin	{ "EPRT", EPRT, STR1, 1,	"<sp> |af|addr|port|" },
10691592Srgrimes	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
107056668Sshin	{ "LPSV", LPSV, ARGS, 1,	"(set server in passive mode)" },
107156668Sshin	{ "EPSV", EPSV, ARGS, 1,	"[<sp> af|ALL]" },
10721592Srgrimes	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
10731592Srgrimes	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
10741592Srgrimes	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
10751592Srgrimes	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
10761592Srgrimes	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
10771592Srgrimes	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
10781592Srgrimes	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
10791592Srgrimes	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
10801592Srgrimes	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
10811592Srgrimes	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
10821592Srgrimes	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
10831592Srgrimes	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
10841592Srgrimes	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
10851592Srgrimes	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
10861592Srgrimes	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
10871592Srgrimes	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
10881592Srgrimes	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
10891592Srgrimes	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
10901592Srgrimes	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
10911592Srgrimes	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
10921592Srgrimes	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
10931592Srgrimes	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
10941592Srgrimes	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
10951592Srgrimes	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
10961592Srgrimes	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
10971592Srgrimes	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
10981592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
10991592Srgrimes	{ "NOOP", NOOP, ARGS, 1,	"" },
11001592Srgrimes	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
11011592Srgrimes	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
11021592Srgrimes	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
11031592Srgrimes	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
11041592Srgrimes	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
11051592Srgrimes	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
11061592Srgrimes	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
11071592Srgrimes	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
11081592Srgrimes	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
11091592Srgrimes	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
11101592Srgrimes	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
11111592Srgrimes	{ NULL,   0,    0,    0,	0 }
11121592Srgrimes};
11131592Srgrimes
11141592Srgrimesstruct tab sitetab[] = {
111575535Sphk	{ "MD5", MDFIVE, STR1, 1,	"[ <sp> file-name ]" },
11161592Srgrimes	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
11171592Srgrimes	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
11181592Srgrimes	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
11191592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
11201592Srgrimes	{ NULL,   0,    0,    0,	0 }
11211592Srgrimes};
11221592Srgrimes
11231592Srgrimesstatic char	*copy __P((char *));
11241592Srgrimesstatic void	 help __P((struct tab *, char *));
11251592Srgrimesstatic struct tab *
11261592Srgrimes		 lookup __P((struct tab *, char *));
112756668Sshinstatic int	 port_check __P((const char *));
112856668Sshinstatic int	 port_check_v6 __P((const char *));
11291592Srgrimesstatic void	 sizecmd __P((char *));
11301592Srgrimesstatic void	 toolong __P((int));
113156668Sshinstatic void	 v4map_data_dest __P((void));
11321592Srgrimesstatic int	 yylex __P((void));
11331592Srgrimes
11341592Srgrimesstatic struct tab *
11351592Srgrimeslookup(p, cmd)
11361592Srgrimes	struct tab *p;
11371592Srgrimes	char *cmd;
11381592Srgrimes{
11391592Srgrimes
11401592Srgrimes	for (; p->name != NULL; p++)
11411592Srgrimes		if (strcmp(cmd, p->name) == 0)
11421592Srgrimes			return (p);
11431592Srgrimes	return (0);
11441592Srgrimes}
11451592Srgrimes
11461592Srgrimes#include <arpa/telnet.h>
11471592Srgrimes
11481592Srgrimes/*
11491592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes.
11501592Srgrimes */
11511592Srgrimeschar *
11521592Srgrimesgetline(s, n, iop)
11531592Srgrimes	char *s;
11541592Srgrimes	int n;
11551592Srgrimes	FILE *iop;
11561592Srgrimes{
11571592Srgrimes	int c;
11581592Srgrimes	register char *cs;
11591592Srgrimes
11601592Srgrimes	cs = s;
11611592Srgrimes/* tmpline may contain saved command from urgent mode interruption */
11621592Srgrimes	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
11631592Srgrimes		*cs++ = tmpline[c];
11641592Srgrimes		if (tmpline[c] == '\n') {
11651592Srgrimes			*cs++ = '\0';
116676096Smarkm			if (ftpdebug)
11671592Srgrimes				syslog(LOG_DEBUG, "command: %s", s);
11681592Srgrimes			tmpline[0] = '\0';
11691592Srgrimes			return(s);
11701592Srgrimes		}
11711592Srgrimes		if (c == 0)
11721592Srgrimes			tmpline[0] = '\0';
11731592Srgrimes	}
11741592Srgrimes	while ((c = getc(iop)) != EOF) {
11751592Srgrimes		c &= 0377;
11761592Srgrimes		if (c == IAC) {
11771592Srgrimes		    if ((c = getc(iop)) != EOF) {
11781592Srgrimes			c &= 0377;
11791592Srgrimes			switch (c) {
11801592Srgrimes			case WILL:
11811592Srgrimes			case WONT:
11821592Srgrimes				c = getc(iop);
11831592Srgrimes				printf("%c%c%c", IAC, DONT, 0377&c);
11841592Srgrimes				(void) fflush(stdout);
11851592Srgrimes				continue;
11861592Srgrimes			case DO:
11871592Srgrimes			case DONT:
11881592Srgrimes				c = getc(iop);
11891592Srgrimes				printf("%c%c%c", IAC, WONT, 0377&c);
11901592Srgrimes				(void) fflush(stdout);
11911592Srgrimes				continue;
11921592Srgrimes			case IAC:
11931592Srgrimes				break;
11941592Srgrimes			default:
11951592Srgrimes				continue;	/* ignore command */
11961592Srgrimes			}
11971592Srgrimes		    }
11981592Srgrimes		}
11991592Srgrimes		*cs++ = c;
12001592Srgrimes		if (--n <= 0 || c == '\n')
12011592Srgrimes			break;
12021592Srgrimes	}
12031592Srgrimes	if (c == EOF && cs == s)
12041592Srgrimes		return (NULL);
12051592Srgrimes	*cs++ = '\0';
120676096Smarkm	if (ftpdebug) {
12071592Srgrimes		if (!guest && strncasecmp("pass ", s, 5) == 0) {
12081592Srgrimes			/* Don't syslog passwords */
12091592Srgrimes			syslog(LOG_DEBUG, "command: %.5s ???", s);
12101592Srgrimes		} else {
12111592Srgrimes			register char *cp;
12121592Srgrimes			register int len;
12131592Srgrimes
12141592Srgrimes			/* Don't syslog trailing CR-LF */
12151592Srgrimes			len = strlen(s);
12161592Srgrimes			cp = s + len - 1;
12171592Srgrimes			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
12181592Srgrimes				--cp;
12191592Srgrimes				--len;
12201592Srgrimes			}
12211592Srgrimes			syslog(LOG_DEBUG, "command: %.*s", len, s);
12221592Srgrimes		}
12231592Srgrimes	}
12241592Srgrimes	return (s);
12251592Srgrimes}
12261592Srgrimes
12271592Srgrimesstatic void
12281592Srgrimestoolong(signo)
12291592Srgrimes	int signo;
12301592Srgrimes{
12311592Srgrimes
12321592Srgrimes	reply(421,
12331592Srgrimes	    "Timeout (%d seconds): closing control connection.", timeout);
12341592Srgrimes	if (logging)
12351592Srgrimes		syslog(LOG_INFO, "User %s timed out after %d seconds",
12361592Srgrimes		    (pw ? pw -> pw_name : "unknown"), timeout);
12371592Srgrimes	dologout(1);
12381592Srgrimes}
12391592Srgrimes
12401592Srgrimesstatic int
12411592Srgrimesyylex()
12421592Srgrimes{
12431592Srgrimes	static int cpos, state;
12441592Srgrimes	char *cp, *cp2;
12451592Srgrimes	struct tab *p;
12461592Srgrimes	int n;
12471592Srgrimes	char c;
12481592Srgrimes
12491592Srgrimes	for (;;) {
12501592Srgrimes		switch (state) {
12511592Srgrimes
12521592Srgrimes		case CMD:
12531592Srgrimes			(void) signal(SIGALRM, toolong);
12541592Srgrimes			(void) alarm((unsigned) timeout);
12551592Srgrimes			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
12561592Srgrimes				reply(221, "You could at least say goodbye.");
12571592Srgrimes				dologout(0);
12581592Srgrimes			}
12591592Srgrimes			(void) alarm(0);
12601592Srgrimes#ifdef SETPROCTITLE
126129574Sphk			if (strncasecmp(cbuf, "PASS", 4) != 0)
12621592Srgrimes				setproctitle("%s: %s", proctitle, cbuf);
12631592Srgrimes#endif /* SETPROCTITLE */
12641592Srgrimes			if ((cp = strchr(cbuf, '\r'))) {
12651592Srgrimes				*cp++ = '\n';
12661592Srgrimes				*cp = '\0';
12671592Srgrimes			}
12681592Srgrimes			if ((cp = strpbrk(cbuf, " \n")))
12691592Srgrimes				cpos = cp - cbuf;
12701592Srgrimes			if (cpos == 0)
12711592Srgrimes				cpos = 4;
12721592Srgrimes			c = cbuf[cpos];
12731592Srgrimes			cbuf[cpos] = '\0';
12741592Srgrimes			upper(cbuf);
12751592Srgrimes			p = lookup(cmdtab, cbuf);
12761592Srgrimes			cbuf[cpos] = c;
12773776Spst			if (p != 0) {
12781592Srgrimes				if (p->implemented == 0) {
12791592Srgrimes					nack(p->name);
12801592Srgrimes					longjmp(errcatch,0);
12811592Srgrimes					/* NOTREACHED */
12821592Srgrimes				}
12831592Srgrimes				state = p->state;
12841592Srgrimes				yylval.s = p->name;
12851592Srgrimes				return (p->token);
12861592Srgrimes			}
12871592Srgrimes			break;
12881592Srgrimes
12891592Srgrimes		case SITECMD:
12901592Srgrimes			if (cbuf[cpos] == ' ') {
12911592Srgrimes				cpos++;
12921592Srgrimes				return (SP);
12931592Srgrimes			}
12941592Srgrimes			cp = &cbuf[cpos];
12951592Srgrimes			if ((cp2 = strpbrk(cp, " \n")))
12961592Srgrimes				cpos = cp2 - cbuf;
12971592Srgrimes			c = cbuf[cpos];
12981592Srgrimes			cbuf[cpos] = '\0';
12991592Srgrimes			upper(cp);
13001592Srgrimes			p = lookup(sitetab, cp);
13011592Srgrimes			cbuf[cpos] = c;
13023777Spst			if (guest == 0 && p != 0) {
13031592Srgrimes				if (p->implemented == 0) {
13041592Srgrimes					state = CMD;
13051592Srgrimes					nack(p->name);
13061592Srgrimes					longjmp(errcatch,0);
13071592Srgrimes					/* NOTREACHED */
13081592Srgrimes				}
13091592Srgrimes				state = p->state;
13101592Srgrimes				yylval.s = p->name;
13111592Srgrimes				return (p->token);
13121592Srgrimes			}
13131592Srgrimes			state = CMD;
13141592Srgrimes			break;
13151592Srgrimes
131675556Sgreen		case ZSTR1:
13171592Srgrimes		case OSTR:
13181592Srgrimes			if (cbuf[cpos] == '\n') {
13191592Srgrimes				state = CMD;
13201592Srgrimes				return (CRLF);
13211592Srgrimes			}
13221592Srgrimes			/* FALLTHROUGH */
13231592Srgrimes
13241592Srgrimes		case STR1:
13251592Srgrimes		dostr1:
13261592Srgrimes			if (cbuf[cpos] == ' ') {
13271592Srgrimes				cpos++;
132851979Salfred				state = state == OSTR ? STR2 : state+1;
13291592Srgrimes				return (SP);
13301592Srgrimes			}
13311592Srgrimes			break;
13321592Srgrimes
13331592Srgrimes		case ZSTR2:
13341592Srgrimes			if (cbuf[cpos] == '\n') {
13351592Srgrimes				state = CMD;
13361592Srgrimes				return (CRLF);
13371592Srgrimes			}
13381592Srgrimes			/* FALLTHROUGH */
13391592Srgrimes
13401592Srgrimes		case STR2:
13411592Srgrimes			cp = &cbuf[cpos];
13421592Srgrimes			n = strlen(cp);
13431592Srgrimes			cpos += n - 1;
13441592Srgrimes			/*
13451592Srgrimes			 * Make sure the string is nonempty and \n terminated.
13461592Srgrimes			 */
13471592Srgrimes			if (n > 1 && cbuf[cpos] == '\n') {
13481592Srgrimes				cbuf[cpos] = '\0';
13491592Srgrimes				yylval.s = copy(cp);
13501592Srgrimes				cbuf[cpos] = '\n';
13511592Srgrimes				state = ARGS;
13521592Srgrimes				return (STRING);
13531592Srgrimes			}
13541592Srgrimes			break;
13551592Srgrimes
13561592Srgrimes		case NSTR:
13571592Srgrimes			if (cbuf[cpos] == ' ') {
13581592Srgrimes				cpos++;
13591592Srgrimes				return (SP);
13601592Srgrimes			}
13611592Srgrimes			if (isdigit(cbuf[cpos])) {
13621592Srgrimes				cp = &cbuf[cpos];
13631592Srgrimes				while (isdigit(cbuf[++cpos]))
13641592Srgrimes					;
13651592Srgrimes				c = cbuf[cpos];
13661592Srgrimes				cbuf[cpos] = '\0';
13671592Srgrimes				yylval.i = atoi(cp);
13681592Srgrimes				cbuf[cpos] = c;
13691592Srgrimes				state = STR1;
13701592Srgrimes				return (NUMBER);
13711592Srgrimes			}
13721592Srgrimes			state = STR1;
13731592Srgrimes			goto dostr1;
13741592Srgrimes
13751592Srgrimes		case ARGS:
13761592Srgrimes			if (isdigit(cbuf[cpos])) {
13771592Srgrimes				cp = &cbuf[cpos];
13781592Srgrimes				while (isdigit(cbuf[++cpos]))
13791592Srgrimes					;
13801592Srgrimes				c = cbuf[cpos];
13811592Srgrimes				cbuf[cpos] = '\0';
13821592Srgrimes				yylval.i = atoi(cp);
13831592Srgrimes				cbuf[cpos] = c;
13841592Srgrimes				return (NUMBER);
13851592Srgrimes			}
138656668Sshin			if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
138756668Sshin			 && !isalnum(cbuf[cpos + 3])) {
138856668Sshin				cpos += 3;
138956668Sshin				return ALL;
139056668Sshin			}
13911592Srgrimes			switch (cbuf[cpos++]) {
13921592Srgrimes
13931592Srgrimes			case '\n':
13941592Srgrimes				state = CMD;
13951592Srgrimes				return (CRLF);
13961592Srgrimes
13971592Srgrimes			case ' ':
13981592Srgrimes				return (SP);
13991592Srgrimes
14001592Srgrimes			case ',':
14011592Srgrimes				return (COMMA);
14021592Srgrimes
14031592Srgrimes			case 'A':
14041592Srgrimes			case 'a':
14051592Srgrimes				return (A);
14061592Srgrimes
14071592Srgrimes			case 'B':
14081592Srgrimes			case 'b':
14091592Srgrimes				return (B);
14101592Srgrimes
14111592Srgrimes			case 'C':
14121592Srgrimes			case 'c':
14131592Srgrimes				return (C);
14141592Srgrimes
14151592Srgrimes			case 'E':
14161592Srgrimes			case 'e':
14171592Srgrimes				return (E);
14181592Srgrimes
14191592Srgrimes			case 'F':
14201592Srgrimes			case 'f':
14211592Srgrimes				return (F);
14221592Srgrimes
14231592Srgrimes			case 'I':
14241592Srgrimes			case 'i':
14251592Srgrimes				return (I);
14261592Srgrimes
14271592Srgrimes			case 'L':
14281592Srgrimes			case 'l':
14291592Srgrimes				return (L);
14301592Srgrimes
14311592Srgrimes			case 'N':
14321592Srgrimes			case 'n':
14331592Srgrimes				return (N);
14341592Srgrimes
14351592Srgrimes			case 'P':
14361592Srgrimes			case 'p':
14371592Srgrimes				return (P);
14381592Srgrimes
14391592Srgrimes			case 'R':
14401592Srgrimes			case 'r':
14411592Srgrimes				return (R);
14421592Srgrimes
14431592Srgrimes			case 'S':
14441592Srgrimes			case 's':
14451592Srgrimes				return (S);
14461592Srgrimes
14471592Srgrimes			case 'T':
14481592Srgrimes			case 't':
14491592Srgrimes				return (T);
14501592Srgrimes
14511592Srgrimes			}
14521592Srgrimes			break;
14531592Srgrimes
14541592Srgrimes		default:
145576096Smarkm			fatalerror("Unknown state in scanner.");
14561592Srgrimes		}
14571592Srgrimes		yyerror((char *) 0);
14581592Srgrimes		state = CMD;
14591592Srgrimes		longjmp(errcatch,0);
14601592Srgrimes	}
14611592Srgrimes}
14621592Srgrimes
14631592Srgrimesvoid
14641592Srgrimesupper(s)
14651592Srgrimes	char *s;
14661592Srgrimes{
14671592Srgrimes	while (*s != '\0') {
14681592Srgrimes		if (islower(*s))
14691592Srgrimes			*s = toupper(*s);
14701592Srgrimes		s++;
14711592Srgrimes	}
14721592Srgrimes}
14731592Srgrimes
14741592Srgrimesstatic char *
14751592Srgrimescopy(s)
14761592Srgrimes	char *s;
14771592Srgrimes{
14781592Srgrimes	char *p;
14791592Srgrimes
14801592Srgrimes	p = malloc((unsigned) strlen(s) + 1);
14811592Srgrimes	if (p == NULL)
148276096Smarkm		fatalerror("Ran out of memory.");
14831592Srgrimes	(void) strcpy(p, s);
14841592Srgrimes	return (p);
14851592Srgrimes}
14861592Srgrimes
14871592Srgrimesstatic void
14881592Srgrimeshelp(ctab, s)
14891592Srgrimes	struct tab *ctab;
14901592Srgrimes	char *s;
14911592Srgrimes{
14921592Srgrimes	struct tab *c;
14931592Srgrimes	int width, NCMDS;
14941592Srgrimes	char *type;
14951592Srgrimes
14961592Srgrimes	if (ctab == sitetab)
14971592Srgrimes		type = "SITE ";
14981592Srgrimes	else
14991592Srgrimes		type = "";
15001592Srgrimes	width = 0, NCMDS = 0;
15011592Srgrimes	for (c = ctab; c->name != NULL; c++) {
15021592Srgrimes		int len = strlen(c->name);
15031592Srgrimes
15041592Srgrimes		if (len > width)
15051592Srgrimes			width = len;
15061592Srgrimes		NCMDS++;
15071592Srgrimes	}
15081592Srgrimes	width = (width + 8) &~ 7;
15091592Srgrimes	if (s == 0) {
15101592Srgrimes		int i, j, w;
15111592Srgrimes		int columns, lines;
15121592Srgrimes
15131592Srgrimes		lreply(214, "The following %scommands are recognized %s.",
15141592Srgrimes		    type, "(* =>'s unimplemented)");
15151592Srgrimes		columns = 76 / width;
15161592Srgrimes		if (columns == 0)
15171592Srgrimes			columns = 1;
15181592Srgrimes		lines = (NCMDS + columns - 1) / columns;
15191592Srgrimes		for (i = 0; i < lines; i++) {
15201592Srgrimes			printf("   ");
15211592Srgrimes			for (j = 0; j < columns; j++) {
15221592Srgrimes				c = ctab + j * lines + i;
15231592Srgrimes				printf("%s%c", c->name,
15241592Srgrimes					c->implemented ? ' ' : '*');
15251592Srgrimes				if (c + lines >= &ctab[NCMDS])
15261592Srgrimes					break;
15271592Srgrimes				w = strlen(c->name) + 1;
15281592Srgrimes				while (w < width) {
15291592Srgrimes					putchar(' ');
15301592Srgrimes					w++;
15311592Srgrimes				}
15321592Srgrimes			}
15331592Srgrimes			printf("\r\n");
15341592Srgrimes		}
15351592Srgrimes		(void) fflush(stdout);
15361592Srgrimes		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
15371592Srgrimes		return;
15381592Srgrimes	}
15391592Srgrimes	upper(s);
15401592Srgrimes	c = lookup(ctab, s);
15411592Srgrimes	if (c == (struct tab *)0) {
15421592Srgrimes		reply(502, "Unknown command %s.", s);
15431592Srgrimes		return;
15441592Srgrimes	}
15451592Srgrimes	if (c->implemented)
15461592Srgrimes		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
15471592Srgrimes	else
15481592Srgrimes		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
15491592Srgrimes		    c->name, c->help);
15501592Srgrimes}
15511592Srgrimes
15521592Srgrimesstatic void
15531592Srgrimessizecmd(filename)
15541592Srgrimes	char *filename;
15551592Srgrimes{
15561592Srgrimes	switch (type) {
15571592Srgrimes	case TYPE_L:
15581592Srgrimes	case TYPE_I: {
15591592Srgrimes		struct stat stbuf;
156063350Sdes		if (stat(filename, &stbuf) < 0)
156163350Sdes			perror_reply(550, filename);
156263350Sdes		else if (!S_ISREG(stbuf.st_mode))
15631592Srgrimes			reply(550, "%s: not a plain file.", filename);
15641592Srgrimes		else
15651592Srgrimes			reply(213, "%qu", stbuf.st_size);
15661592Srgrimes		break; }
15671592Srgrimes	case TYPE_A: {
15681592Srgrimes		FILE *fin;
15691592Srgrimes		int c;
15701592Srgrimes		off_t count;
15711592Srgrimes		struct stat stbuf;
15721592Srgrimes		fin = fopen(filename, "r");
15731592Srgrimes		if (fin == NULL) {
15741592Srgrimes			perror_reply(550, filename);
15751592Srgrimes			return;
15761592Srgrimes		}
157763350Sdes		if (fstat(fileno(fin), &stbuf) < 0) {
157863350Sdes			perror_reply(550, filename);
157963350Sdes			(void) fclose(fin);
158063350Sdes			return;
158163350Sdes		} else if (!S_ISREG(stbuf.st_mode)) {
15821592Srgrimes			reply(550, "%s: not a plain file.", filename);
15831592Srgrimes			(void) fclose(fin);
15841592Srgrimes			return;
15851592Srgrimes		}
15861592Srgrimes
15871592Srgrimes		count = 0;
15881592Srgrimes		while((c=getc(fin)) != EOF) {
15891592Srgrimes			if (c == '\n')	/* will get expanded to \r\n */
15901592Srgrimes				count++;
15911592Srgrimes			count++;
15921592Srgrimes		}
15931592Srgrimes		(void) fclose(fin);
15941592Srgrimes
15951592Srgrimes		reply(213, "%qd", count);
15961592Srgrimes		break; }
15971592Srgrimes	default:
15981592Srgrimes		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
15991592Srgrimes	}
16001592Srgrimes}
160156668Sshin
160256668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
160356668Sshinstatic int
160456668Sshinport_check(pcmd)
160556668Sshin	const char *pcmd;
160656668Sshin{
160756668Sshin	if (his_addr.su_family == AF_INET) {
160856668Sshin		if (data_dest.su_family != AF_INET) {
160956668Sshin			usedefault = 1;
161056668Sshin			reply(500, "Invalid address rejected.");
161156668Sshin			return 1;
161256668Sshin		}
161356668Sshin		if (paranoid &&
161456668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
161556668Sshin		     memcmp(&data_dest.su_sin.sin_addr,
161656668Sshin			    &his_addr.su_sin.sin_addr,
161756668Sshin			    sizeof(data_dest.su_sin.sin_addr)))) {
161856668Sshin			usedefault = 1;
161956668Sshin			reply(500, "Illegal PORT range rejected.");
162056668Sshin		} else {
162156668Sshin			usedefault = 0;
162256668Sshin			if (pdata >= 0) {
162356668Sshin				(void) close(pdata);
162456668Sshin				pdata = -1;
162556668Sshin			}
162656668Sshin			reply(200, "%s command successful.", pcmd);
162756668Sshin		}
162856668Sshin		return 1;
162956668Sshin	}
163056668Sshin	return 0;
163156668Sshin}
163256668Sshin
163370102Sphkstatic int
163470102Sphkcheck_login1()
163570102Sphk{
163670102Sphk	if (logged_in)
163770102Sphk		return 1;
163870102Sphk	else {
163970102Sphk		reply(530, "Please login with USER and PASS.");
164070102Sphk		return 0;
164170102Sphk	}
164270102Sphk}
164370102Sphk
164456668Sshin#ifdef INET6
164556668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
164656668Sshinstatic int
164756668Sshinport_check_v6(pcmd)
164856668Sshin	const char *pcmd;
164956668Sshin{
165056668Sshin	if (his_addr.su_family == AF_INET6) {
165156668Sshin		if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
165256668Sshin			/* Convert data_dest into v4 mapped sockaddr.*/
165356668Sshin			v4map_data_dest();
165456668Sshin		if (data_dest.su_family != AF_INET6) {
165556668Sshin			usedefault = 1;
165656668Sshin			reply(500, "Invalid address rejected.");
165756668Sshin			return 1;
165856668Sshin		}
165956668Sshin		if (paranoid &&
166056668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
166156668Sshin		     memcmp(&data_dest.su_sin6.sin6_addr,
166256668Sshin			    &his_addr.su_sin6.sin6_addr,
166356668Sshin			    sizeof(data_dest.su_sin6.sin6_addr)))) {
166456668Sshin			usedefault = 1;
166556668Sshin			reply(500, "Illegal PORT range rejected.");
166656668Sshin		} else {
166756668Sshin			usedefault = 0;
166856668Sshin			if (pdata >= 0) {
166956668Sshin				(void) close(pdata);
167056668Sshin				pdata = -1;
167156668Sshin			}
167256668Sshin			reply(200, "%s command successful.", pcmd);
167356668Sshin		}
167456668Sshin		return 1;
167556668Sshin	}
167656668Sshin	return 0;
167756668Sshin}
167856668Sshin
167956668Sshinstatic void
168056668Sshinv4map_data_dest()
168156668Sshin{
168256668Sshin	struct in_addr savedaddr;
168356668Sshin	int savedport;
168456668Sshin
168556668Sshin	if (data_dest.su_family != AF_INET) {
168656668Sshin		usedefault = 1;
168756668Sshin		reply(500, "Invalid address rejected.");
168856668Sshin		return;
168956668Sshin	}
169056668Sshin
169156668Sshin	savedaddr = data_dest.su_sin.sin_addr;
169256668Sshin	savedport = data_dest.su_port;
169356668Sshin
169456668Sshin	memset(&data_dest, 0, sizeof(data_dest));
169556668Sshin	data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
169656668Sshin	data_dest.su_sin6.sin6_family = AF_INET6;
169756668Sshin	data_dest.su_sin6.sin6_port = savedport;
169856668Sshin	memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
169956668Sshin	memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
170056668Sshin	       (caddr_t)&savedaddr, sizeof(savedaddr));
170156668Sshin}
170256668Sshin#endif
1703