ftpcmd.y revision 70102
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 70102 2000-12-16 19:19:19Z phk $";
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>
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;
831592Srgrimesextern	int debug;
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;
951592Srgrimes
961592Srgrimesoff_t	restart_point;
971592Srgrimes
981592Srgrimesstatic	int cmd_type;
991592Srgrimesstatic	int cmd_form;
1001592Srgrimesstatic	int cmd_bytesz;
1011592Srgrimeschar	cbuf[512];
1021592Srgrimeschar	*fromname;
1031592Srgrimes
10456668Sshinextern int epsvall;
10556668Sshin
1061592Srgrimes%}
1071592Srgrimes
1081592Srgrimes%union {
1091592Srgrimes	int	i;
1101592Srgrimes	char   *s;
1111592Srgrimes}
1121592Srgrimes
1131592Srgrimes%token
1141592Srgrimes	A	B	C	E	F	I
1151592Srgrimes	L	N	P	R	S	T
11656668Sshin	ALL
1171592Srgrimes
1181592Srgrimes	SP	CRLF	COMMA
1191592Srgrimes
1201592Srgrimes	USER	PASS	ACCT	REIN	QUIT	PORT
1211592Srgrimes	PASV	TYPE	STRU	MODE	RETR	STOR
1221592Srgrimes	APPE	MLFL	MAIL	MSND	MSOM	MSAM
1231592Srgrimes	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
1241592Srgrimes	ABOR	DELE	CWD	LIST	NLST	SITE
1251592Srgrimes	STAT	HELP	NOOP	MKD	RMD	PWD
1261592Srgrimes	CDUP	STOU	SMNT	SYST	SIZE	MDTM
12756668Sshin	LPRT	LPSV	EPRT	EPSV
1281592Srgrimes
1291592Srgrimes	UMASK	IDLE	CHMOD
1301592Srgrimes
1311592Srgrimes	LEXERR
1321592Srgrimes
1331592Srgrimes%token	<s> STRING
1341592Srgrimes%token	<i> NUMBER
1351592Srgrimes
1361592Srgrimes%type	<i> check_login octal_number byte_size
13770102Sphk%type	<i> check_login_ro octal_number byte_size
13870102Sphk%type	<i> check_login_epsv octal_number byte_size
1391592Srgrimes%type	<i> struct_code mode_code type_code form_code
14056668Sshin%type	<s> pathstring pathname password username ext_arg
14156668Sshin%type	<s> ALL
1421592Srgrimes
1431592Srgrimes%start	cmd_list
1441592Srgrimes
1451592Srgrimes%%
1461592Srgrimes
1471592Srgrimescmd_list
1481592Srgrimes	: /* empty */
1491592Srgrimes	| cmd_list cmd
1501592Srgrimes		{
1511592Srgrimes			fromname = (char *) 0;
1521592Srgrimes			restart_point = (off_t) 0;
1531592Srgrimes		}
1541592Srgrimes	| cmd_list rcmd
1551592Srgrimes	;
1561592Srgrimes
1571592Srgrimescmd
1581592Srgrimes	: USER SP username CRLF
1591592Srgrimes		{
1601592Srgrimes			user($3);
1611592Srgrimes			free($3);
1621592Srgrimes		}
1631592Srgrimes	| PASS SP password CRLF
1641592Srgrimes		{
1651592Srgrimes			pass($3);
1661592Srgrimes			free($3);
1671592Srgrimes		}
16817433Spst	| PORT check_login SP host_port CRLF
1691592Srgrimes		{
17056668Sshin			if (epsvall) {
17156668Sshin				reply(501, "no PORT allowed after EPSV ALL");
17256668Sshin				goto port_done;
17356668Sshin			}
17456668Sshin			if (!$2)
17556668Sshin				goto port_done;
17656668Sshin			if (port_check("PORT") == 1)
17756668Sshin				goto port_done;
17856668Sshin#ifdef INET6
17956668Sshin			if ((his_addr.su_family != AF_INET6 ||
18056668Sshin			     !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
18156668Sshin				/* shoud never happen */
18256668Sshin				usedefault = 1;
18356668Sshin				reply(500, "Invalid address rejected.");
18456668Sshin				goto port_done;
18556668Sshin			}
18656668Sshin			port_check_v6("pcmd");
18756668Sshin#endif
18856668Sshin		port_done:
18956668Sshin		}
19056668Sshin	| LPRT check_login SP host_long_port CRLF
19156668Sshin		{
19256668Sshin			if (epsvall) {
19356668Sshin				reply(501, "no LPRT allowed after EPSV ALL");
19456668Sshin				goto lprt_done;
19556668Sshin			}
19656668Sshin			if (!$2)
19756668Sshin				goto lprt_done;
19856668Sshin			if (port_check("LPRT") == 1)
19956668Sshin				goto lprt_done;
20056668Sshin#ifdef INET6
20156668Sshin			if (his_addr.su_family != AF_INET6) {
20256668Sshin				usedefault = 1;
20356668Sshin				reply(500, "Invalid address rejected.");
20456668Sshin				goto lprt_done;
20556668Sshin			}
20656668Sshin			if (port_check_v6("LPRT") == 1)
20756668Sshin				goto lprt_done;
20856668Sshin#endif
20956668Sshin		lprt_done:
21056668Sshin		}
21156668Sshin	| EPRT check_login SP STRING CRLF
21256668Sshin		{
21356668Sshin			char delim;
21456668Sshin			char *tmp = NULL;
21556668Sshin			char *p, *q;
21656668Sshin			char *result[3];
21756668Sshin			struct addrinfo hints;
21856668Sshin			struct addrinfo *res;
21956668Sshin			int i;
22056668Sshin
22156668Sshin			if (epsvall) {
22256668Sshin				reply(501, "no EPRT allowed after EPSV ALL");
22356668Sshin				goto eprt_done;
22456668Sshin			}
22556668Sshin			if (!$2)
22656668Sshin				goto eprt_done;
22756668Sshin
22856668Sshin			memset(&data_dest, 0, sizeof(data_dest));
22956668Sshin			tmp = strdup($4);
23056668Sshin			if (debug)
23156668Sshin				syslog(LOG_DEBUG, "%s", tmp);
23256668Sshin			if (!tmp) {
23356668Sshin				fatal("not enough core");
23456668Sshin				/*NOTREACHED*/
23556668Sshin			}
23656668Sshin			p = tmp;
23756668Sshin			delim = p[0];
23856668Sshin			p++;
23956668Sshin			memset(result, 0, sizeof(result));
24056668Sshin			for (i = 0; i < 3; i++) {
24156668Sshin				q = strchr(p, delim);
24256668Sshin				if (!q || *q != delim) {
24356668Sshin		parsefail:
24456668Sshin					reply(500,
24556668Sshin						"Invalid argument, rejected.");
24656668Sshin					if (tmp)
24756668Sshin						free(tmp);
24817433Spst					usedefault = 1;
24956668Sshin					goto eprt_done;
25017433Spst				}
25156668Sshin				*q++ = '\0';
25256668Sshin				result[i] = p;
25356668Sshin				if (debug)
25456668Sshin					syslog(LOG_DEBUG, "%d: %s", i, p);
25556668Sshin				p = q;
2561592Srgrimes			}
25756668Sshin
25856668Sshin			/* some more sanity check */
25956668Sshin			p = result[0];
26056668Sshin			while (*p) {
26156668Sshin				if (!isdigit(*p))
26256668Sshin					goto parsefail;
26356668Sshin				p++;
26456668Sshin			}
26556668Sshin			p = result[2];
26656668Sshin			while (*p) {
26756668Sshin				if (!isdigit(*p))
26856668Sshin					goto parsefail;
26956668Sshin				p++;
27056668Sshin			}
27156668Sshin
27256668Sshin			/* grab address */
27356668Sshin			memset(&hints, 0, sizeof(hints));
27456668Sshin			if (atoi(result[0]) == 1)
27556668Sshin				hints.ai_family = PF_INET;
27656668Sshin#ifdef INET6
27756668Sshin			else if (atoi(result[0]) == 2)
27856668Sshin				hints.ai_family = PF_INET6;
27956668Sshin#endif
28056668Sshin			else
28156668Sshin				hints.ai_family = PF_UNSPEC;	/*XXX*/
28256668Sshin			hints.ai_socktype = SOCK_STREAM;
28356668Sshin			i = getaddrinfo(result[1], result[2], &hints, &res);
28456668Sshin			if (i)
28556668Sshin				goto parsefail;
28656668Sshin			memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
28756668Sshin#ifdef INET6
28856668Sshin			if (his_addr.su_family == AF_INET6
28956668Sshin			    && data_dest.su_family == AF_INET6) {
29056668Sshin				/* XXX more sanity checks! */
29156668Sshin				data_dest.su_sin6.sin6_scope_id =
29256668Sshin					his_addr.su_sin6.sin6_scope_id;
29356668Sshin			}
29456668Sshin#endif
29556668Sshin			free(tmp);
29656668Sshin			tmp = NULL;
29756668Sshin
29856668Sshin			if (port_check("EPRT") == 1)
29956668Sshin				goto eprt_done;
30056668Sshin#ifdef INET6
30156668Sshin			if (his_addr.su_family != AF_INET6) {
30256668Sshin				usedefault = 1;
30356668Sshin				reply(500, "Invalid address rejected.");
30456668Sshin				goto eprt_done;
30556668Sshin			}
30656668Sshin			if (port_check_v6("EPRT") == 1)
30756668Sshin				goto eprt_done;
30856668Sshin#endif
30956668Sshin		eprt_done:;
3101592Srgrimes		}
31117433Spst	| PASV check_login CRLF
3121592Srgrimes		{
31356668Sshin			if (epsvall)
31456668Sshin				reply(501, "no PASV allowed after EPSV ALL");
31556668Sshin			else if ($2)
31617433Spst				passive();
3171592Srgrimes		}
31856668Sshin	| LPSV check_login CRLF
31956668Sshin		{
32056668Sshin			if (epsvall)
32156668Sshin				reply(501, "no LPSV allowed after EPSV ALL");
32256668Sshin			else if ($2)
32356668Sshin				long_passive("LPSV", PF_UNSPEC);
32456668Sshin		}
32570102Sphk	| EPSV check_login_epsv SP NUMBER CRLF
32656668Sshin		{
32756668Sshin			if ($2) {
32856668Sshin				int pf;
32956668Sshin				switch ($4) {
33056668Sshin				case 1:
33156668Sshin					pf = PF_INET;
33256668Sshin					break;
33356668Sshin#ifdef INET6
33456668Sshin				case 2:
33556668Sshin					pf = PF_INET6;
33656668Sshin					break;
33756668Sshin#endif
33856668Sshin				default:
33956668Sshin					pf = -1;	/*junk value*/
34056668Sshin					break;
34156668Sshin				}
34256668Sshin				long_passive("EPSV", pf);
34356668Sshin			}
34456668Sshin		}
34570102Sphk	| EPSV check_login_epsv SP ALL CRLF
34656668Sshin		{
34756668Sshin			if ($2) {
34856668Sshin				reply(200,
34956668Sshin				      "EPSV ALL command successful.");
35056668Sshin				epsvall++;
35156668Sshin			}
35256668Sshin		}
35370102Sphk	| EPSV check_login_epsv CRLF
35456668Sshin		{
35556668Sshin			if ($2)
35656668Sshin				long_passive("EPSV", PF_UNSPEC);
35756668Sshin		}
3581592Srgrimes	| TYPE SP type_code CRLF
3591592Srgrimes		{
3601592Srgrimes			switch (cmd_type) {
3611592Srgrimes
3621592Srgrimes			case TYPE_A:
3631592Srgrimes				if (cmd_form == FORM_N) {
3641592Srgrimes					reply(200, "Type set to A.");
3651592Srgrimes					type = cmd_type;
3661592Srgrimes					form = cmd_form;
3671592Srgrimes				} else
3681592Srgrimes					reply(504, "Form must be N.");
3691592Srgrimes				break;
3701592Srgrimes
3711592Srgrimes			case TYPE_E:
3721592Srgrimes				reply(504, "Type E not implemented.");
3731592Srgrimes				break;
3741592Srgrimes
3751592Srgrimes			case TYPE_I:
3761592Srgrimes				reply(200, "Type set to I.");
3771592Srgrimes				type = cmd_type;
3781592Srgrimes				break;
3791592Srgrimes
3801592Srgrimes			case TYPE_L:
3811592Srgrimes#if NBBY == 8
3821592Srgrimes				if (cmd_bytesz == 8) {
3831592Srgrimes					reply(200,
3841592Srgrimes					    "Type set to L (byte size 8).");
3851592Srgrimes					type = cmd_type;
3861592Srgrimes				} else
3871592Srgrimes					reply(504, "Byte size must be 8.");
3881592Srgrimes#else /* NBBY == 8 */
3891592Srgrimes				UNIMPLEMENTED for NBBY != 8
3901592Srgrimes#endif /* NBBY == 8 */
3911592Srgrimes			}
3921592Srgrimes		}
3931592Srgrimes	| STRU SP struct_code CRLF
3941592Srgrimes		{
3951592Srgrimes			switch ($3) {
3961592Srgrimes
3971592Srgrimes			case STRU_F:
3981592Srgrimes				reply(200, "STRU F ok.");
3991592Srgrimes				break;
4001592Srgrimes
4011592Srgrimes			default:
4021592Srgrimes				reply(504, "Unimplemented STRU type.");
4031592Srgrimes			}
4041592Srgrimes		}
4051592Srgrimes	| MODE SP mode_code CRLF
4061592Srgrimes		{
4071592Srgrimes			switch ($3) {
4081592Srgrimes
4091592Srgrimes			case MODE_S:
4101592Srgrimes				reply(200, "MODE S ok.");
4111592Srgrimes				break;
4121592Srgrimes
4131592Srgrimes			default:
4141592Srgrimes				reply(502, "Unimplemented MODE type.");
4151592Srgrimes			}
4161592Srgrimes		}
4171592Srgrimes	| ALLO SP NUMBER CRLF
4181592Srgrimes		{
4191592Srgrimes			reply(202, "ALLO command ignored.");
4201592Srgrimes		}
4211592Srgrimes	| ALLO SP NUMBER SP R SP NUMBER CRLF
4221592Srgrimes		{
4231592Srgrimes			reply(202, "ALLO command ignored.");
4241592Srgrimes		}
4251592Srgrimes	| RETR check_login SP pathname CRLF
4261592Srgrimes		{
4271592Srgrimes			if ($2 && $4 != NULL)
4281592Srgrimes				retrieve((char *) 0, $4);
4291592Srgrimes			if ($4 != NULL)
4301592Srgrimes				free($4);
4311592Srgrimes		}
43270102Sphk	| STOR check_login_ro SP pathname CRLF
4331592Srgrimes		{
4341592Srgrimes			if ($2 && $4 != NULL)
4351592Srgrimes				store($4, "w", 0);
4361592Srgrimes			if ($4 != NULL)
4371592Srgrimes				free($4);
4381592Srgrimes		}
43970102Sphk	| APPE check_login_ro SP pathname CRLF
4401592Srgrimes		{
4411592Srgrimes			if ($2 && $4 != NULL)
4421592Srgrimes				store($4, "a", 0);
4431592Srgrimes			if ($4 != NULL)
4441592Srgrimes				free($4);
4451592Srgrimes		}
4461592Srgrimes	| NLST check_login CRLF
4471592Srgrimes		{
4481592Srgrimes			if ($2)
4491592Srgrimes				send_file_list(".");
4501592Srgrimes		}
4511592Srgrimes	| NLST check_login SP STRING CRLF
4521592Srgrimes		{
4531592Srgrimes			if ($2 && $4 != NULL)
4541592Srgrimes				send_file_list($4);
4551592Srgrimes			if ($4 != NULL)
4561592Srgrimes				free($4);
4571592Srgrimes		}
4581592Srgrimes	| LIST check_login CRLF
4591592Srgrimes		{
4601592Srgrimes			if ($2)
4611592Srgrimes				retrieve("/bin/ls -lgA", "");
4621592Srgrimes		}
4631592Srgrimes	| LIST check_login SP pathname CRLF
4641592Srgrimes		{
4651592Srgrimes			if ($2 && $4 != NULL)
4661592Srgrimes				retrieve("/bin/ls -lgA %s", $4);
4671592Srgrimes			if ($4 != NULL)
4681592Srgrimes				free($4);
4691592Srgrimes		}
4701592Srgrimes	| STAT check_login SP pathname CRLF
4711592Srgrimes		{
4721592Srgrimes			if ($2 && $4 != NULL)
4731592Srgrimes				statfilecmd($4);
4741592Srgrimes			if ($4 != NULL)
4751592Srgrimes				free($4);
4761592Srgrimes		}
4771592Srgrimes	| STAT CRLF
4781592Srgrimes		{
4791592Srgrimes			statcmd();
4801592Srgrimes		}
48170102Sphk	| DELE check_login_ro SP pathname CRLF
4821592Srgrimes		{
4831592Srgrimes			if ($2 && $4 != NULL)
4841592Srgrimes				delete($4);
4851592Srgrimes			if ($4 != NULL)
4861592Srgrimes				free($4);
4871592Srgrimes		}
48870102Sphk	| RNTO check_login_ro SP pathname CRLF
4891592Srgrimes		{
49017433Spst			if ($2) {
49117433Spst				if (fromname) {
49217433Spst					renamecmd(fromname, $4);
49317433Spst					free(fromname);
49417433Spst					fromname = (char *) 0;
49517433Spst				} else {
49617433Spst					reply(503, "Bad sequence of commands.");
49717433Spst				}
4981592Srgrimes			}
49917433Spst			free($4);
5001592Srgrimes		}
5011592Srgrimes	| ABOR CRLF
5021592Srgrimes		{
5031592Srgrimes			reply(225, "ABOR command successful.");
5041592Srgrimes		}
5051592Srgrimes	| CWD check_login CRLF
5061592Srgrimes		{
50769234Sdanny			if ($2) {
50869234Sdanny				if (guest)
50969234Sdanny					cwd("/");
51069234Sdanny				else
51169234Sdanny					cwd(pw->pw_dir);
51269234Sdanny			}
5131592Srgrimes		}
5141592Srgrimes	| CWD check_login SP pathname CRLF
5151592Srgrimes		{
5161592Srgrimes			if ($2 && $4 != NULL)
5171592Srgrimes				cwd($4);
5181592Srgrimes			if ($4 != NULL)
5191592Srgrimes				free($4);
5201592Srgrimes		}
5211592Srgrimes	| HELP CRLF
5221592Srgrimes		{
5231592Srgrimes			help(cmdtab, (char *) 0);
5241592Srgrimes		}
5251592Srgrimes	| HELP SP STRING CRLF
5261592Srgrimes		{
5271592Srgrimes			char *cp = $3;
5281592Srgrimes
5291592Srgrimes			if (strncasecmp(cp, "SITE", 4) == 0) {
5301592Srgrimes				cp = $3 + 4;
5311592Srgrimes				if (*cp == ' ')
5321592Srgrimes					cp++;
5331592Srgrimes				if (*cp)
5341592Srgrimes					help(sitetab, cp);
5351592Srgrimes				else
5361592Srgrimes					help(sitetab, (char *) 0);
5371592Srgrimes			} else
5381592Srgrimes				help(cmdtab, $3);
5391592Srgrimes		}
5401592Srgrimes	| NOOP CRLF
5411592Srgrimes		{
5421592Srgrimes			reply(200, "NOOP command successful.");
5431592Srgrimes		}
54470102Sphk	| MKD check_login_ro SP pathname CRLF
5451592Srgrimes		{
5461592Srgrimes			if ($2 && $4 != NULL)
5471592Srgrimes				makedir($4);
5481592Srgrimes			if ($4 != NULL)
5491592Srgrimes				free($4);
5501592Srgrimes		}
55170102Sphk	| RMD check_login_ro SP pathname CRLF
5521592Srgrimes		{
5531592Srgrimes			if ($2 && $4 != NULL)
5541592Srgrimes				removedir($4);
5551592Srgrimes			if ($4 != NULL)
5561592Srgrimes				free($4);
5571592Srgrimes		}
5581592Srgrimes	| PWD check_login CRLF
5591592Srgrimes		{
5601592Srgrimes			if ($2)
5611592Srgrimes				pwd();
5621592Srgrimes		}
5631592Srgrimes	| CDUP check_login CRLF
5641592Srgrimes		{
5651592Srgrimes			if ($2)
5661592Srgrimes				cwd("..");
5671592Srgrimes		}
5681592Srgrimes	| SITE SP HELP CRLF
5691592Srgrimes		{
5701592Srgrimes			help(sitetab, (char *) 0);
5711592Srgrimes		}
5721592Srgrimes	| SITE SP HELP SP STRING CRLF
5731592Srgrimes		{
5741592Srgrimes			help(sitetab, $5);
5751592Srgrimes		}
5761592Srgrimes	| SITE SP UMASK check_login CRLF
5771592Srgrimes		{
5781592Srgrimes			int oldmask;
5791592Srgrimes
5801592Srgrimes			if ($4) {
5811592Srgrimes				oldmask = umask(0);
5821592Srgrimes				(void) umask(oldmask);
5831592Srgrimes				reply(200, "Current UMASK is %03o", oldmask);
5841592Srgrimes			}
5851592Srgrimes		}
5861592Srgrimes	| SITE SP UMASK check_login SP octal_number CRLF
5871592Srgrimes		{
5881592Srgrimes			int oldmask;
5891592Srgrimes
5901592Srgrimes			if ($4) {
5911592Srgrimes				if (($6 == -1) || ($6 > 0777)) {
5921592Srgrimes					reply(501, "Bad UMASK value");
5931592Srgrimes				} else {
5941592Srgrimes					oldmask = umask($6);
5951592Srgrimes					reply(200,
5961592Srgrimes					    "UMASK set to %03o (was %03o)",
5971592Srgrimes					    $6, oldmask);
5981592Srgrimes				}
5991592Srgrimes			}
6001592Srgrimes		}
60170102Sphk	| SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
6021592Srgrimes		{
6031592Srgrimes			if ($4 && ($8 != NULL)) {
6041592Srgrimes				if ($6 > 0777)
6051592Srgrimes					reply(501,
6061592Srgrimes				"CHMOD: Mode value must be between 0 and 0777");
6071592Srgrimes				else if (chmod($8, $6) < 0)
6081592Srgrimes					perror_reply(550, $8);
6091592Srgrimes				else
6101592Srgrimes					reply(200, "CHMOD command successful.");
6111592Srgrimes			}
6121592Srgrimes			if ($8 != NULL)
6131592Srgrimes				free($8);
6141592Srgrimes		}
6151592Srgrimes	| SITE SP IDLE CRLF
6161592Srgrimes		{
6171592Srgrimes			reply(200,
6181592Srgrimes			    "Current IDLE time limit is %d seconds; max %d",
6191592Srgrimes				timeout, maxtimeout);
6201592Srgrimes		}
6211592Srgrimes	| SITE SP IDLE SP NUMBER CRLF
6221592Srgrimes		{
6231592Srgrimes			if ($5 < 30 || $5 > maxtimeout) {
6241592Srgrimes				reply(501,
6251592Srgrimes			"Maximum IDLE time must be between 30 and %d seconds",
6261592Srgrimes				    maxtimeout);
6271592Srgrimes			} else {
6281592Srgrimes				timeout = $5;
6291592Srgrimes				(void) alarm((unsigned) timeout);
6301592Srgrimes				reply(200,
6311592Srgrimes				    "Maximum IDLE time set to %d seconds",
6321592Srgrimes				    timeout);
6331592Srgrimes			}
6341592Srgrimes		}
63570102Sphk	| STOU check_login_ro SP pathname CRLF
6361592Srgrimes		{
6371592Srgrimes			if ($2 && $4 != NULL)
6381592Srgrimes				store($4, "w", 1);
6391592Srgrimes			if ($4 != NULL)
6401592Srgrimes				free($4);
6411592Srgrimes		}
6421592Srgrimes	| SYST CRLF
6431592Srgrimes		{
6441592Srgrimes#ifdef unix
6451592Srgrimes#ifdef BSD
6461592Srgrimes			reply(215, "UNIX Type: L%d Version: BSD-%d",
6471592Srgrimes				NBBY, BSD);
6481592Srgrimes#else /* BSD */
6491592Srgrimes			reply(215, "UNIX Type: L%d", NBBY);
6501592Srgrimes#endif /* BSD */
6511592Srgrimes#else /* unix */
6521592Srgrimes			reply(215, "UNKNOWN Type: L%d", NBBY);
6531592Srgrimes#endif /* unix */
6541592Srgrimes		}
6551592Srgrimes
6561592Srgrimes		/*
6571592Srgrimes		 * SIZE is not in RFC959, but Postel has blessed it and
6581592Srgrimes		 * it will be in the updated RFC.
6591592Srgrimes		 *
6601592Srgrimes		 * Return size of file in a format suitable for
6611592Srgrimes		 * using with RESTART (we just count bytes).
6621592Srgrimes		 */
6631592Srgrimes	| SIZE check_login SP pathname CRLF
6641592Srgrimes		{
6651592Srgrimes			if ($2 && $4 != NULL)
6661592Srgrimes				sizecmd($4);
6671592Srgrimes			if ($4 != NULL)
6681592Srgrimes				free($4);
6691592Srgrimes		}
6701592Srgrimes
6711592Srgrimes		/*
6721592Srgrimes		 * MDTM is not in RFC959, but Postel has blessed it and
6731592Srgrimes		 * it will be in the updated RFC.
6741592Srgrimes		 *
6751592Srgrimes		 * Return modification time of file as an ISO 3307
6761592Srgrimes		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
6771592Srgrimes		 * where xxx is the fractional second (of any precision,
6781592Srgrimes		 * not necessarily 3 digits)
6791592Srgrimes		 */
6801592Srgrimes	| MDTM check_login SP pathname CRLF
6811592Srgrimes		{
6821592Srgrimes			if ($2 && $4 != NULL) {
6831592Srgrimes				struct stat stbuf;
6841592Srgrimes				if (stat($4, &stbuf) < 0)
6851592Srgrimes					reply(550, "%s: %s",
6861592Srgrimes					    $4, strerror(errno));
6871592Srgrimes				else if (!S_ISREG(stbuf.st_mode)) {
6881592Srgrimes					reply(550, "%s: not a plain file.", $4);
6891592Srgrimes				} else {
6901592Srgrimes					struct tm *t;
6911592Srgrimes					t = gmtime(&stbuf.st_mtime);
6921592Srgrimes					reply(213,
69317435Spst					    "%04d%02d%02d%02d%02d%02d",
69417435Spst					    1900 + t->tm_year,
69517435Spst					    t->tm_mon+1, t->tm_mday,
6961592Srgrimes					    t->tm_hour, t->tm_min, t->tm_sec);
6971592Srgrimes				}
6981592Srgrimes			}
6991592Srgrimes			if ($4 != NULL)
7001592Srgrimes				free($4);
7011592Srgrimes		}
7021592Srgrimes	| QUIT CRLF
7031592Srgrimes		{
7041592Srgrimes			reply(221, "Goodbye.");
7051592Srgrimes			dologout(0);
7061592Srgrimes		}
7071592Srgrimes	| error CRLF
7081592Srgrimes		{
7091592Srgrimes			yyerrok;
7101592Srgrimes		}
7111592Srgrimes	;
7121592Srgrimesrcmd
71370102Sphk	: RNFR check_login_ro SP pathname CRLF
7141592Srgrimes		{
7151592Srgrimes			char *renamefrom();
7161592Srgrimes
7171592Srgrimes			restart_point = (off_t) 0;
7181592Srgrimes			if ($2 && $4) {
7191592Srgrimes				fromname = renamefrom($4);
7201592Srgrimes				if (fromname == (char *) 0 && $4) {
7211592Srgrimes					free($4);
7221592Srgrimes				}
7231592Srgrimes			}
7241592Srgrimes		}
7251592Srgrimes	| REST SP byte_size CRLF
7261592Srgrimes		{
7271592Srgrimes			fromname = (char *) 0;
7281592Srgrimes			restart_point = $3;	/* XXX $3 is only "int" */
7291592Srgrimes			reply(350, "Restarting at %qd. %s", restart_point,
7301592Srgrimes			    "Send STORE or RETRIEVE to initiate transfer.");
7311592Srgrimes		}
7321592Srgrimes	;
7331592Srgrimes
7341592Srgrimesusername
7351592Srgrimes	: STRING
7361592Srgrimes	;
7371592Srgrimes
7381592Srgrimespassword
7391592Srgrimes	: /* empty */
7401592Srgrimes		{
7411592Srgrimes			$$ = (char *)calloc(1, sizeof(char));
7421592Srgrimes		}
7431592Srgrimes	| STRING
7441592Srgrimes	;
7451592Srgrimes
7461592Srgrimesbyte_size
7471592Srgrimes	: NUMBER
7481592Srgrimes	;
7491592Srgrimes
7501592Srgrimeshost_port
7511592Srgrimes	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
7521592Srgrimes		NUMBER COMMA NUMBER
7531592Srgrimes		{
7541592Srgrimes			char *a, *p;
7551592Srgrimes
75656668Sshin			data_dest.su_len = sizeof(struct sockaddr_in);
75756668Sshin			data_dest.su_family = AF_INET;
75856668Sshin			p = (char *)&data_dest.su_sin.sin_port;
75917435Spst			p[0] = $9; p[1] = $11;
76056668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
7611592Srgrimes			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
7621592Srgrimes		}
7631592Srgrimes	;
7641592Srgrimes
76556668Sshinhost_long_port
76656668Sshin	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
76756668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
76856668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
76956668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
77056668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
77156668Sshin		NUMBER
77256668Sshin		{
77356668Sshin			char *a, *p;
77456668Sshin
77556668Sshin			memset(&data_dest, 0, sizeof(data_dest));
77656668Sshin			data_dest.su_len = sizeof(struct sockaddr_in6);
77756668Sshin			data_dest.su_family = AF_INET6;
77856668Sshin			p = (char *)&data_dest.su_port;
77956668Sshin			p[0] = $39; p[1] = $41;
78056668Sshin			a = (char *)&data_dest.su_sin6.sin6_addr;
78156668Sshin			 a[0] =  $5;  a[1] =  $7;  a[2] =  $9;  a[3] = $11;
78256668Sshin			 a[4] = $13;  a[5] = $15;  a[6] = $17;  a[7] = $19;
78356668Sshin			 a[8] = $21;  a[9] = $23; a[10] = $25; a[11] = $27;
78456668Sshin			a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
78556668Sshin			if (his_addr.su_family == AF_INET6) {
78656668Sshin				/* XXX more sanity checks! */
78756668Sshin				data_dest.su_sin6.sin6_scope_id =
78856668Sshin					his_addr.su_sin6.sin6_scope_id;
78956668Sshin			}
79056668Sshin			if ($1 != 6 || $3 != 16 || $37 != 2)
79156668Sshin				memset(&data_dest, 0, sizeof(data_dest));
79256668Sshin		}
79356668Sshin	| NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
79456668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
79556668Sshin		NUMBER
79656668Sshin		{
79756668Sshin			char *a, *p;
79856668Sshin
79956668Sshin			memset(&data_dest, 0, sizeof(data_dest));
80056668Sshin			data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
80156668Sshin			data_dest.su_family = AF_INET;
80256668Sshin			p = (char *)&data_dest.su_port;
80356668Sshin			p[0] = $15; p[1] = $17;
80456668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
80556668Sshin			a[0] =  $5;  a[1] =  $7;  a[2] =  $9;  a[3] = $11;
80656668Sshin			if ($1 != 4 || $3 != 4 || $13 != 2)
80756668Sshin				memset(&data_dest, 0, sizeof(data_dest));
80856668Sshin		}
80956668Sshin	;
81056668Sshin
8111592Srgrimesform_code
8121592Srgrimes	: N
8131592Srgrimes		{
8141592Srgrimes			$$ = FORM_N;
8151592Srgrimes		}
8161592Srgrimes	| T
8171592Srgrimes		{
8181592Srgrimes			$$ = FORM_T;
8191592Srgrimes		}
8201592Srgrimes	| C
8211592Srgrimes		{
8221592Srgrimes			$$ = FORM_C;
8231592Srgrimes		}
8241592Srgrimes	;
8251592Srgrimes
8261592Srgrimestype_code
8271592Srgrimes	: A
8281592Srgrimes		{
8291592Srgrimes			cmd_type = TYPE_A;
8301592Srgrimes			cmd_form = FORM_N;
8311592Srgrimes		}
8321592Srgrimes	| A SP form_code
8331592Srgrimes		{
8341592Srgrimes			cmd_type = TYPE_A;
8351592Srgrimes			cmd_form = $3;
8361592Srgrimes		}
8371592Srgrimes	| E
8381592Srgrimes		{
8391592Srgrimes			cmd_type = TYPE_E;
8401592Srgrimes			cmd_form = FORM_N;
8411592Srgrimes		}
8421592Srgrimes	| E SP form_code
8431592Srgrimes		{
8441592Srgrimes			cmd_type = TYPE_E;
8451592Srgrimes			cmd_form = $3;
8461592Srgrimes		}
8471592Srgrimes	| I
8481592Srgrimes		{
8491592Srgrimes			cmd_type = TYPE_I;
8501592Srgrimes		}
8511592Srgrimes	| L
8521592Srgrimes		{
8531592Srgrimes			cmd_type = TYPE_L;
8541592Srgrimes			cmd_bytesz = NBBY;
8551592Srgrimes		}
8561592Srgrimes	| L SP byte_size
8571592Srgrimes		{
8581592Srgrimes			cmd_type = TYPE_L;
8591592Srgrimes			cmd_bytesz = $3;
8601592Srgrimes		}
8611592Srgrimes		/* this is for a bug in the BBN ftp */
8621592Srgrimes	| L byte_size
8631592Srgrimes		{
8641592Srgrimes			cmd_type = TYPE_L;
8651592Srgrimes			cmd_bytesz = $2;
8661592Srgrimes		}
8671592Srgrimes	;
8681592Srgrimes
8691592Srgrimesstruct_code
8701592Srgrimes	: F
8711592Srgrimes		{
8721592Srgrimes			$$ = STRU_F;
8731592Srgrimes		}
8741592Srgrimes	| R
8751592Srgrimes		{
8761592Srgrimes			$$ = STRU_R;
8771592Srgrimes		}
8781592Srgrimes	| P
8791592Srgrimes		{
8801592Srgrimes			$$ = STRU_P;
8811592Srgrimes		}
8821592Srgrimes	;
8831592Srgrimes
8841592Srgrimesmode_code
8851592Srgrimes	: S
8861592Srgrimes		{
8871592Srgrimes			$$ = MODE_S;
8881592Srgrimes		}
8891592Srgrimes	| B
8901592Srgrimes		{
8911592Srgrimes			$$ = MODE_B;
8921592Srgrimes		}
8931592Srgrimes	| C
8941592Srgrimes		{
8951592Srgrimes			$$ = MODE_C;
8961592Srgrimes		}
8971592Srgrimes	;
8981592Srgrimes
8991592Srgrimespathname
9001592Srgrimes	: pathstring
9011592Srgrimes		{
9021592Srgrimes			/*
9031592Srgrimes			 * Problem: this production is used for all pathname
9041592Srgrimes			 * processing, but only gives a 550 error reply.
9051592Srgrimes			 * This is a valid reply in some cases but not in others.
9061592Srgrimes			 */
9071592Srgrimes			if (logged_in && $1 && *$1 == '~') {
9081592Srgrimes				glob_t gl;
9091592Srgrimes				int flags =
9101592Srgrimes				 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
9111592Srgrimes
9121592Srgrimes				memset(&gl, 0, sizeof(gl));
9131592Srgrimes				if (glob($1, flags, NULL, &gl) ||
9141592Srgrimes				    gl.gl_pathc == 0) {
9151592Srgrimes					reply(550, "not found");
9161592Srgrimes					$$ = NULL;
9171592Srgrimes				} else {
9181592Srgrimes					$$ = strdup(gl.gl_pathv[0]);
9191592Srgrimes				}
9201592Srgrimes				globfree(&gl);
9211592Srgrimes				free($1);
9221592Srgrimes			} else
9231592Srgrimes				$$ = $1;
9241592Srgrimes		}
9251592Srgrimes	;
9261592Srgrimes
9271592Srgrimespathstring
9281592Srgrimes	: STRING
9291592Srgrimes	;
9301592Srgrimes
9311592Srgrimesoctal_number
9321592Srgrimes	: NUMBER
9331592Srgrimes		{
9341592Srgrimes			int ret, dec, multby, digit;
9351592Srgrimes
9361592Srgrimes			/*
9371592Srgrimes			 * Convert a number that was read as decimal number
9381592Srgrimes			 * to what it would be if it had been read as octal.
9391592Srgrimes			 */
9401592Srgrimes			dec = $1;
9411592Srgrimes			multby = 1;
9421592Srgrimes			ret = 0;
9431592Srgrimes			while (dec) {
9441592Srgrimes				digit = dec%10;
9451592Srgrimes				if (digit > 7) {
9461592Srgrimes					ret = -1;
9471592Srgrimes					break;
9481592Srgrimes				}
9491592Srgrimes				ret += digit * multby;
9501592Srgrimes				multby *= 8;
9511592Srgrimes				dec /= 10;
9521592Srgrimes			}
9531592Srgrimes			$$ = ret;
9541592Srgrimes		}
9551592Srgrimes	;
9561592Srgrimes
9571592Srgrimes
9581592Srgrimescheck_login
9591592Srgrimes	: /* empty */
9601592Srgrimes		{
96170102Sphk		$$ = check_login1();
9621592Srgrimes		}
9631592Srgrimes	;
9641592Srgrimes
96570102Sphkcheck_login_epsv
96670102Sphk	: /* empty */
96770102Sphk		{
96870102Sphk		if (noepsv) {
96970102Sphk			reply(500, "EPSV command disabled");
97070102Sphk			$$ = 0;
97170102Sphk		}
97270102Sphk		else
97370102Sphk			$$ = check_login1();
97470102Sphk		}
97570102Sphk	;
97670102Sphk
97770102Sphkcheck_login_ro
97870102Sphk	: /* empty */
97970102Sphk		{
98070102Sphk		if (readonly) {
98170102Sphk			reply(202, "Command ignored. Server is in readonly mode.");
98270102Sphk			$$ = 0;
98370102Sphk		}
98470102Sphk		else
98570102Sphk			$$ = check_login1();
98670102Sphk		}
98770102Sphk	;
98870102Sphk
9891592Srgrimes%%
9901592Srgrimes
9911592Srgrimesextern jmp_buf errcatch;
9921592Srgrimes
9931592Srgrimes#define	CMD	0	/* beginning of command */
9941592Srgrimes#define	ARGS	1	/* expect miscellaneous arguments */
9951592Srgrimes#define	STR1	2	/* expect SP followed by STRING */
9961592Srgrimes#define	STR2	3	/* expect STRING */
9971592Srgrimes#define	OSTR	4	/* optional SP then STRING */
9981592Srgrimes#define	ZSTR1	5	/* SP then optional STRING */
9991592Srgrimes#define	ZSTR2	6	/* optional STRING after SP */
10001592Srgrimes#define	SITECMD	7	/* SITE command */
10011592Srgrimes#define	NSTR	8	/* Number followed by a string */
10021592Srgrimes
10031592Srgrimesstruct tab {
10041592Srgrimes	char	*name;
10051592Srgrimes	short	token;
10061592Srgrimes	short	state;
10071592Srgrimes	short	implemented;	/* 1 if command is implemented */
10081592Srgrimes	char	*help;
10091592Srgrimes};
10101592Srgrimes
10111592Srgrimesstruct tab cmdtab[] = {		/* In order defined in RFC 765 */
10121592Srgrimes	{ "USER", USER, STR1, 1,	"<sp> username" },
10131592Srgrimes	{ "PASS", PASS, ZSTR1, 1,	"<sp> password" },
10141592Srgrimes	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
10151592Srgrimes	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
10161592Srgrimes	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
10171592Srgrimes	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
10181592Srgrimes	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
101956668Sshin	{ "LPRT", LPRT, ARGS, 1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
102056668Sshin	{ "EPRT", EPRT, STR1, 1,	"<sp> |af|addr|port|" },
10211592Srgrimes	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
102256668Sshin	{ "LPSV", LPSV, ARGS, 1,	"(set server in passive mode)" },
102356668Sshin	{ "EPSV", EPSV, ARGS, 1,	"[<sp> af|ALL]" },
10241592Srgrimes	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
10251592Srgrimes	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
10261592Srgrimes	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
10271592Srgrimes	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
10281592Srgrimes	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
10291592Srgrimes	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
10301592Srgrimes	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
10311592Srgrimes	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
10321592Srgrimes	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
10331592Srgrimes	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
10341592Srgrimes	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
10351592Srgrimes	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
10361592Srgrimes	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
10371592Srgrimes	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
10381592Srgrimes	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
10391592Srgrimes	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
10401592Srgrimes	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
10411592Srgrimes	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
10421592Srgrimes	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
10431592Srgrimes	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
10441592Srgrimes	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
10451592Srgrimes	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
10461592Srgrimes	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
10471592Srgrimes	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
10481592Srgrimes	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
10491592Srgrimes	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
10501592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
10511592Srgrimes	{ "NOOP", NOOP, ARGS, 1,	"" },
10521592Srgrimes	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
10531592Srgrimes	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
10541592Srgrimes	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
10551592Srgrimes	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
10561592Srgrimes	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
10571592Srgrimes	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
10581592Srgrimes	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
10591592Srgrimes	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
10601592Srgrimes	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
10611592Srgrimes	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
10621592Srgrimes	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
10631592Srgrimes	{ NULL,   0,    0,    0,	0 }
10641592Srgrimes};
10651592Srgrimes
10661592Srgrimesstruct tab sitetab[] = {
10671592Srgrimes	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
10681592Srgrimes	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
10691592Srgrimes	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
10701592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
10711592Srgrimes	{ NULL,   0,    0,    0,	0 }
10721592Srgrimes};
10731592Srgrimes
10741592Srgrimesstatic char	*copy __P((char *));
10751592Srgrimesstatic void	 help __P((struct tab *, char *));
10761592Srgrimesstatic struct tab *
10771592Srgrimes		 lookup __P((struct tab *, char *));
107856668Sshinstatic int	 port_check __P((const char *));
107956668Sshinstatic int	 port_check_v6 __P((const char *));
10801592Srgrimesstatic void	 sizecmd __P((char *));
10811592Srgrimesstatic void	 toolong __P((int));
108256668Sshinstatic void	 v4map_data_dest __P((void));
10831592Srgrimesstatic int	 yylex __P((void));
10841592Srgrimes
10851592Srgrimesstatic struct tab *
10861592Srgrimeslookup(p, cmd)
10871592Srgrimes	struct tab *p;
10881592Srgrimes	char *cmd;
10891592Srgrimes{
10901592Srgrimes
10911592Srgrimes	for (; p->name != NULL; p++)
10921592Srgrimes		if (strcmp(cmd, p->name) == 0)
10931592Srgrimes			return (p);
10941592Srgrimes	return (0);
10951592Srgrimes}
10961592Srgrimes
10971592Srgrimes#include <arpa/telnet.h>
10981592Srgrimes
10991592Srgrimes/*
11001592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes.
11011592Srgrimes */
11021592Srgrimeschar *
11031592Srgrimesgetline(s, n, iop)
11041592Srgrimes	char *s;
11051592Srgrimes	int n;
11061592Srgrimes	FILE *iop;
11071592Srgrimes{
11081592Srgrimes	int c;
11091592Srgrimes	register char *cs;
11101592Srgrimes
11111592Srgrimes	cs = s;
11121592Srgrimes/* tmpline may contain saved command from urgent mode interruption */
11131592Srgrimes	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
11141592Srgrimes		*cs++ = tmpline[c];
11151592Srgrimes		if (tmpline[c] == '\n') {
11161592Srgrimes			*cs++ = '\0';
11171592Srgrimes			if (debug)
11181592Srgrimes				syslog(LOG_DEBUG, "command: %s", s);
11191592Srgrimes			tmpline[0] = '\0';
11201592Srgrimes			return(s);
11211592Srgrimes		}
11221592Srgrimes		if (c == 0)
11231592Srgrimes			tmpline[0] = '\0';
11241592Srgrimes	}
11251592Srgrimes	while ((c = getc(iop)) != EOF) {
11261592Srgrimes		c &= 0377;
11271592Srgrimes		if (c == IAC) {
11281592Srgrimes		    if ((c = getc(iop)) != EOF) {
11291592Srgrimes			c &= 0377;
11301592Srgrimes			switch (c) {
11311592Srgrimes			case WILL:
11321592Srgrimes			case WONT:
11331592Srgrimes				c = getc(iop);
11341592Srgrimes				printf("%c%c%c", IAC, DONT, 0377&c);
11351592Srgrimes				(void) fflush(stdout);
11361592Srgrimes				continue;
11371592Srgrimes			case DO:
11381592Srgrimes			case DONT:
11391592Srgrimes				c = getc(iop);
11401592Srgrimes				printf("%c%c%c", IAC, WONT, 0377&c);
11411592Srgrimes				(void) fflush(stdout);
11421592Srgrimes				continue;
11431592Srgrimes			case IAC:
11441592Srgrimes				break;
11451592Srgrimes			default:
11461592Srgrimes				continue;	/* ignore command */
11471592Srgrimes			}
11481592Srgrimes		    }
11491592Srgrimes		}
11501592Srgrimes		*cs++ = c;
11511592Srgrimes		if (--n <= 0 || c == '\n')
11521592Srgrimes			break;
11531592Srgrimes	}
11541592Srgrimes	if (c == EOF && cs == s)
11551592Srgrimes		return (NULL);
11561592Srgrimes	*cs++ = '\0';
11571592Srgrimes	if (debug) {
11581592Srgrimes		if (!guest && strncasecmp("pass ", s, 5) == 0) {
11591592Srgrimes			/* Don't syslog passwords */
11601592Srgrimes			syslog(LOG_DEBUG, "command: %.5s ???", s);
11611592Srgrimes		} else {
11621592Srgrimes			register char *cp;
11631592Srgrimes			register int len;
11641592Srgrimes
11651592Srgrimes			/* Don't syslog trailing CR-LF */
11661592Srgrimes			len = strlen(s);
11671592Srgrimes			cp = s + len - 1;
11681592Srgrimes			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
11691592Srgrimes				--cp;
11701592Srgrimes				--len;
11711592Srgrimes			}
11721592Srgrimes			syslog(LOG_DEBUG, "command: %.*s", len, s);
11731592Srgrimes		}
11741592Srgrimes	}
11751592Srgrimes	return (s);
11761592Srgrimes}
11771592Srgrimes
11781592Srgrimesstatic void
11791592Srgrimestoolong(signo)
11801592Srgrimes	int signo;
11811592Srgrimes{
11821592Srgrimes
11831592Srgrimes	reply(421,
11841592Srgrimes	    "Timeout (%d seconds): closing control connection.", timeout);
11851592Srgrimes	if (logging)
11861592Srgrimes		syslog(LOG_INFO, "User %s timed out after %d seconds",
11871592Srgrimes		    (pw ? pw -> pw_name : "unknown"), timeout);
11881592Srgrimes	dologout(1);
11891592Srgrimes}
11901592Srgrimes
11911592Srgrimesstatic int
11921592Srgrimesyylex()
11931592Srgrimes{
11941592Srgrimes	static int cpos, state;
11951592Srgrimes	char *cp, *cp2;
11961592Srgrimes	struct tab *p;
11971592Srgrimes	int n;
11981592Srgrimes	char c;
11991592Srgrimes
12001592Srgrimes	for (;;) {
12011592Srgrimes		switch (state) {
12021592Srgrimes
12031592Srgrimes		case CMD:
12041592Srgrimes			(void) signal(SIGALRM, toolong);
12051592Srgrimes			(void) alarm((unsigned) timeout);
12061592Srgrimes			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
12071592Srgrimes				reply(221, "You could at least say goodbye.");
12081592Srgrimes				dologout(0);
12091592Srgrimes			}
12101592Srgrimes			(void) alarm(0);
12111592Srgrimes#ifdef SETPROCTITLE
121229574Sphk			if (strncasecmp(cbuf, "PASS", 4) != 0)
12131592Srgrimes				setproctitle("%s: %s", proctitle, cbuf);
12141592Srgrimes#endif /* SETPROCTITLE */
12151592Srgrimes			if ((cp = strchr(cbuf, '\r'))) {
12161592Srgrimes				*cp++ = '\n';
12171592Srgrimes				*cp = '\0';
12181592Srgrimes			}
12191592Srgrimes			if ((cp = strpbrk(cbuf, " \n")))
12201592Srgrimes				cpos = cp - cbuf;
12211592Srgrimes			if (cpos == 0)
12221592Srgrimes				cpos = 4;
12231592Srgrimes			c = cbuf[cpos];
12241592Srgrimes			cbuf[cpos] = '\0';
12251592Srgrimes			upper(cbuf);
12261592Srgrimes			p = lookup(cmdtab, cbuf);
12271592Srgrimes			cbuf[cpos] = c;
12283776Spst			if (p != 0) {
12291592Srgrimes				if (p->implemented == 0) {
12301592Srgrimes					nack(p->name);
12311592Srgrimes					longjmp(errcatch,0);
12321592Srgrimes					/* NOTREACHED */
12331592Srgrimes				}
12341592Srgrimes				state = p->state;
12351592Srgrimes				yylval.s = p->name;
12361592Srgrimes				return (p->token);
12371592Srgrimes			}
12381592Srgrimes			break;
12391592Srgrimes
12401592Srgrimes		case SITECMD:
12411592Srgrimes			if (cbuf[cpos] == ' ') {
12421592Srgrimes				cpos++;
12431592Srgrimes				return (SP);
12441592Srgrimes			}
12451592Srgrimes			cp = &cbuf[cpos];
12461592Srgrimes			if ((cp2 = strpbrk(cp, " \n")))
12471592Srgrimes				cpos = cp2 - cbuf;
12481592Srgrimes			c = cbuf[cpos];
12491592Srgrimes			cbuf[cpos] = '\0';
12501592Srgrimes			upper(cp);
12511592Srgrimes			p = lookup(sitetab, cp);
12521592Srgrimes			cbuf[cpos] = c;
12533777Spst			if (guest == 0 && p != 0) {
12541592Srgrimes				if (p->implemented == 0) {
12551592Srgrimes					state = CMD;
12561592Srgrimes					nack(p->name);
12571592Srgrimes					longjmp(errcatch,0);
12581592Srgrimes					/* NOTREACHED */
12591592Srgrimes				}
12601592Srgrimes				state = p->state;
12611592Srgrimes				yylval.s = p->name;
12621592Srgrimes				return (p->token);
12631592Srgrimes			}
12641592Srgrimes			state = CMD;
12651592Srgrimes			break;
12661592Srgrimes
12671592Srgrimes		case OSTR:
12681592Srgrimes			if (cbuf[cpos] == '\n') {
12691592Srgrimes				state = CMD;
12701592Srgrimes				return (CRLF);
12711592Srgrimes			}
12721592Srgrimes			/* FALLTHROUGH */
12731592Srgrimes
12741592Srgrimes		case STR1:
12751592Srgrimes		case ZSTR1:
12761592Srgrimes		dostr1:
12771592Srgrimes			if (cbuf[cpos] == ' ') {
12781592Srgrimes				cpos++;
127951979Salfred				state = state == OSTR ? STR2 : state+1;
12801592Srgrimes				return (SP);
12811592Srgrimes			}
12821592Srgrimes			break;
12831592Srgrimes
12841592Srgrimes		case ZSTR2:
12851592Srgrimes			if (cbuf[cpos] == '\n') {
12861592Srgrimes				state = CMD;
12871592Srgrimes				return (CRLF);
12881592Srgrimes			}
12891592Srgrimes			/* FALLTHROUGH */
12901592Srgrimes
12911592Srgrimes		case STR2:
12921592Srgrimes			cp = &cbuf[cpos];
12931592Srgrimes			n = strlen(cp);
12941592Srgrimes			cpos += n - 1;
12951592Srgrimes			/*
12961592Srgrimes			 * Make sure the string is nonempty and \n terminated.
12971592Srgrimes			 */
12981592Srgrimes			if (n > 1 && cbuf[cpos] == '\n') {
12991592Srgrimes				cbuf[cpos] = '\0';
13001592Srgrimes				yylval.s = copy(cp);
13011592Srgrimes				cbuf[cpos] = '\n';
13021592Srgrimes				state = ARGS;
13031592Srgrimes				return (STRING);
13041592Srgrimes			}
13051592Srgrimes			break;
13061592Srgrimes
13071592Srgrimes		case NSTR:
13081592Srgrimes			if (cbuf[cpos] == ' ') {
13091592Srgrimes				cpos++;
13101592Srgrimes				return (SP);
13111592Srgrimes			}
13121592Srgrimes			if (isdigit(cbuf[cpos])) {
13131592Srgrimes				cp = &cbuf[cpos];
13141592Srgrimes				while (isdigit(cbuf[++cpos]))
13151592Srgrimes					;
13161592Srgrimes				c = cbuf[cpos];
13171592Srgrimes				cbuf[cpos] = '\0';
13181592Srgrimes				yylval.i = atoi(cp);
13191592Srgrimes				cbuf[cpos] = c;
13201592Srgrimes				state = STR1;
13211592Srgrimes				return (NUMBER);
13221592Srgrimes			}
13231592Srgrimes			state = STR1;
13241592Srgrimes			goto dostr1;
13251592Srgrimes
13261592Srgrimes		case ARGS:
13271592Srgrimes			if (isdigit(cbuf[cpos])) {
13281592Srgrimes				cp = &cbuf[cpos];
13291592Srgrimes				while (isdigit(cbuf[++cpos]))
13301592Srgrimes					;
13311592Srgrimes				c = cbuf[cpos];
13321592Srgrimes				cbuf[cpos] = '\0';
13331592Srgrimes				yylval.i = atoi(cp);
13341592Srgrimes				cbuf[cpos] = c;
13351592Srgrimes				return (NUMBER);
13361592Srgrimes			}
133756668Sshin			if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
133856668Sshin			 && !isalnum(cbuf[cpos + 3])) {
133956668Sshin				cpos += 3;
134056668Sshin				return ALL;
134156668Sshin			}
13421592Srgrimes			switch (cbuf[cpos++]) {
13431592Srgrimes
13441592Srgrimes			case '\n':
13451592Srgrimes				state = CMD;
13461592Srgrimes				return (CRLF);
13471592Srgrimes
13481592Srgrimes			case ' ':
13491592Srgrimes				return (SP);
13501592Srgrimes
13511592Srgrimes			case ',':
13521592Srgrimes				return (COMMA);
13531592Srgrimes
13541592Srgrimes			case 'A':
13551592Srgrimes			case 'a':
13561592Srgrimes				return (A);
13571592Srgrimes
13581592Srgrimes			case 'B':
13591592Srgrimes			case 'b':
13601592Srgrimes				return (B);
13611592Srgrimes
13621592Srgrimes			case 'C':
13631592Srgrimes			case 'c':
13641592Srgrimes				return (C);
13651592Srgrimes
13661592Srgrimes			case 'E':
13671592Srgrimes			case 'e':
13681592Srgrimes				return (E);
13691592Srgrimes
13701592Srgrimes			case 'F':
13711592Srgrimes			case 'f':
13721592Srgrimes				return (F);
13731592Srgrimes
13741592Srgrimes			case 'I':
13751592Srgrimes			case 'i':
13761592Srgrimes				return (I);
13771592Srgrimes
13781592Srgrimes			case 'L':
13791592Srgrimes			case 'l':
13801592Srgrimes				return (L);
13811592Srgrimes
13821592Srgrimes			case 'N':
13831592Srgrimes			case 'n':
13841592Srgrimes				return (N);
13851592Srgrimes
13861592Srgrimes			case 'P':
13871592Srgrimes			case 'p':
13881592Srgrimes				return (P);
13891592Srgrimes
13901592Srgrimes			case 'R':
13911592Srgrimes			case 'r':
13921592Srgrimes				return (R);
13931592Srgrimes
13941592Srgrimes			case 'S':
13951592Srgrimes			case 's':
13961592Srgrimes				return (S);
13971592Srgrimes
13981592Srgrimes			case 'T':
13991592Srgrimes			case 't':
14001592Srgrimes				return (T);
14011592Srgrimes
14021592Srgrimes			}
14031592Srgrimes			break;
14041592Srgrimes
14051592Srgrimes		default:
14061592Srgrimes			fatal("Unknown state in scanner.");
14071592Srgrimes		}
14081592Srgrimes		yyerror((char *) 0);
14091592Srgrimes		state = CMD;
14101592Srgrimes		longjmp(errcatch,0);
14111592Srgrimes	}
14121592Srgrimes}
14131592Srgrimes
14141592Srgrimesvoid
14151592Srgrimesupper(s)
14161592Srgrimes	char *s;
14171592Srgrimes{
14181592Srgrimes	while (*s != '\0') {
14191592Srgrimes		if (islower(*s))
14201592Srgrimes			*s = toupper(*s);
14211592Srgrimes		s++;
14221592Srgrimes	}
14231592Srgrimes}
14241592Srgrimes
14251592Srgrimesstatic char *
14261592Srgrimescopy(s)
14271592Srgrimes	char *s;
14281592Srgrimes{
14291592Srgrimes	char *p;
14301592Srgrimes
14311592Srgrimes	p = malloc((unsigned) strlen(s) + 1);
14321592Srgrimes	if (p == NULL)
14331592Srgrimes		fatal("Ran out of memory.");
14341592Srgrimes	(void) strcpy(p, s);
14351592Srgrimes	return (p);
14361592Srgrimes}
14371592Srgrimes
14381592Srgrimesstatic void
14391592Srgrimeshelp(ctab, s)
14401592Srgrimes	struct tab *ctab;
14411592Srgrimes	char *s;
14421592Srgrimes{
14431592Srgrimes	struct tab *c;
14441592Srgrimes	int width, NCMDS;
14451592Srgrimes	char *type;
14461592Srgrimes
14471592Srgrimes	if (ctab == sitetab)
14481592Srgrimes		type = "SITE ";
14491592Srgrimes	else
14501592Srgrimes		type = "";
14511592Srgrimes	width = 0, NCMDS = 0;
14521592Srgrimes	for (c = ctab; c->name != NULL; c++) {
14531592Srgrimes		int len = strlen(c->name);
14541592Srgrimes
14551592Srgrimes		if (len > width)
14561592Srgrimes			width = len;
14571592Srgrimes		NCMDS++;
14581592Srgrimes	}
14591592Srgrimes	width = (width + 8) &~ 7;
14601592Srgrimes	if (s == 0) {
14611592Srgrimes		int i, j, w;
14621592Srgrimes		int columns, lines;
14631592Srgrimes
14641592Srgrimes		lreply(214, "The following %scommands are recognized %s.",
14651592Srgrimes		    type, "(* =>'s unimplemented)");
14661592Srgrimes		columns = 76 / width;
14671592Srgrimes		if (columns == 0)
14681592Srgrimes			columns = 1;
14691592Srgrimes		lines = (NCMDS + columns - 1) / columns;
14701592Srgrimes		for (i = 0; i < lines; i++) {
14711592Srgrimes			printf("   ");
14721592Srgrimes			for (j = 0; j < columns; j++) {
14731592Srgrimes				c = ctab + j * lines + i;
14741592Srgrimes				printf("%s%c", c->name,
14751592Srgrimes					c->implemented ? ' ' : '*');
14761592Srgrimes				if (c + lines >= &ctab[NCMDS])
14771592Srgrimes					break;
14781592Srgrimes				w = strlen(c->name) + 1;
14791592Srgrimes				while (w < width) {
14801592Srgrimes					putchar(' ');
14811592Srgrimes					w++;
14821592Srgrimes				}
14831592Srgrimes			}
14841592Srgrimes			printf("\r\n");
14851592Srgrimes		}
14861592Srgrimes		(void) fflush(stdout);
14871592Srgrimes		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
14881592Srgrimes		return;
14891592Srgrimes	}
14901592Srgrimes	upper(s);
14911592Srgrimes	c = lookup(ctab, s);
14921592Srgrimes	if (c == (struct tab *)0) {
14931592Srgrimes		reply(502, "Unknown command %s.", s);
14941592Srgrimes		return;
14951592Srgrimes	}
14961592Srgrimes	if (c->implemented)
14971592Srgrimes		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
14981592Srgrimes	else
14991592Srgrimes		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
15001592Srgrimes		    c->name, c->help);
15011592Srgrimes}
15021592Srgrimes
15031592Srgrimesstatic void
15041592Srgrimessizecmd(filename)
15051592Srgrimes	char *filename;
15061592Srgrimes{
15071592Srgrimes	switch (type) {
15081592Srgrimes	case TYPE_L:
15091592Srgrimes	case TYPE_I: {
15101592Srgrimes		struct stat stbuf;
151163350Sdes		if (stat(filename, &stbuf) < 0)
151263350Sdes			perror_reply(550, filename);
151363350Sdes		else if (!S_ISREG(stbuf.st_mode))
15141592Srgrimes			reply(550, "%s: not a plain file.", filename);
15151592Srgrimes		else
15161592Srgrimes			reply(213, "%qu", stbuf.st_size);
15171592Srgrimes		break; }
15181592Srgrimes	case TYPE_A: {
15191592Srgrimes		FILE *fin;
15201592Srgrimes		int c;
15211592Srgrimes		off_t count;
15221592Srgrimes		struct stat stbuf;
15231592Srgrimes		fin = fopen(filename, "r");
15241592Srgrimes		if (fin == NULL) {
15251592Srgrimes			perror_reply(550, filename);
15261592Srgrimes			return;
15271592Srgrimes		}
152863350Sdes		if (fstat(fileno(fin), &stbuf) < 0) {
152963350Sdes			perror_reply(550, filename);
153063350Sdes			(void) fclose(fin);
153163350Sdes			return;
153263350Sdes		} else if (!S_ISREG(stbuf.st_mode)) {
15331592Srgrimes			reply(550, "%s: not a plain file.", filename);
15341592Srgrimes			(void) fclose(fin);
15351592Srgrimes			return;
15361592Srgrimes		}
15371592Srgrimes
15381592Srgrimes		count = 0;
15391592Srgrimes		while((c=getc(fin)) != EOF) {
15401592Srgrimes			if (c == '\n')	/* will get expanded to \r\n */
15411592Srgrimes				count++;
15421592Srgrimes			count++;
15431592Srgrimes		}
15441592Srgrimes		(void) fclose(fin);
15451592Srgrimes
15461592Srgrimes		reply(213, "%qd", count);
15471592Srgrimes		break; }
15481592Srgrimes	default:
15491592Srgrimes		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
15501592Srgrimes	}
15511592Srgrimes}
155256668Sshin
155356668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
155456668Sshinstatic int
155556668Sshinport_check(pcmd)
155656668Sshin	const char *pcmd;
155756668Sshin{
155856668Sshin	if (his_addr.su_family == AF_INET) {
155956668Sshin		if (data_dest.su_family != AF_INET) {
156056668Sshin			usedefault = 1;
156156668Sshin			reply(500, "Invalid address rejected.");
156256668Sshin			return 1;
156356668Sshin		}
156456668Sshin		if (paranoid &&
156556668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
156656668Sshin		     memcmp(&data_dest.su_sin.sin_addr,
156756668Sshin			    &his_addr.su_sin.sin_addr,
156856668Sshin			    sizeof(data_dest.su_sin.sin_addr)))) {
156956668Sshin			usedefault = 1;
157056668Sshin			reply(500, "Illegal PORT range rejected.");
157156668Sshin		} else {
157256668Sshin			usedefault = 0;
157356668Sshin			if (pdata >= 0) {
157456668Sshin				(void) close(pdata);
157556668Sshin				pdata = -1;
157656668Sshin			}
157756668Sshin			reply(200, "%s command successful.", pcmd);
157856668Sshin		}
157956668Sshin		return 1;
158056668Sshin	}
158156668Sshin	return 0;
158256668Sshin}
158356668Sshin
158470102Sphkstatic int
158570102Sphkcheck_login1()
158670102Sphk{
158770102Sphk	if (logged_in)
158870102Sphk		return 1;
158970102Sphk	else {
159070102Sphk		reply(530, "Please login with USER and PASS.");
159170102Sphk		return 0;
159270102Sphk	}
159370102Sphk}
159470102Sphk
159556668Sshin#ifdef INET6
159656668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
159756668Sshinstatic int
159856668Sshinport_check_v6(pcmd)
159956668Sshin	const char *pcmd;
160056668Sshin{
160156668Sshin	if (his_addr.su_family == AF_INET6) {
160256668Sshin		if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
160356668Sshin			/* Convert data_dest into v4 mapped sockaddr.*/
160456668Sshin			v4map_data_dest();
160556668Sshin		if (data_dest.su_family != AF_INET6) {
160656668Sshin			usedefault = 1;
160756668Sshin			reply(500, "Invalid address rejected.");
160856668Sshin			return 1;
160956668Sshin		}
161056668Sshin		if (paranoid &&
161156668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
161256668Sshin		     memcmp(&data_dest.su_sin6.sin6_addr,
161356668Sshin			    &his_addr.su_sin6.sin6_addr,
161456668Sshin			    sizeof(data_dest.su_sin6.sin6_addr)))) {
161556668Sshin			usedefault = 1;
161656668Sshin			reply(500, "Illegal PORT range rejected.");
161756668Sshin		} else {
161856668Sshin			usedefault = 0;
161956668Sshin			if (pdata >= 0) {
162056668Sshin				(void) close(pdata);
162156668Sshin				pdata = -1;
162256668Sshin			}
162356668Sshin			reply(200, "%s command successful.", pcmd);
162456668Sshin		}
162556668Sshin		return 1;
162656668Sshin	}
162756668Sshin	return 0;
162856668Sshin}
162956668Sshin
163056668Sshinstatic void
163156668Sshinv4map_data_dest()
163256668Sshin{
163356668Sshin	struct in_addr savedaddr;
163456668Sshin	int savedport;
163556668Sshin
163656668Sshin	if (data_dest.su_family != AF_INET) {
163756668Sshin		usedefault = 1;
163856668Sshin		reply(500, "Invalid address rejected.");
163956668Sshin		return;
164056668Sshin	}
164156668Sshin
164256668Sshin	savedaddr = data_dest.su_sin.sin_addr;
164356668Sshin	savedport = data_dest.su_port;
164456668Sshin
164556668Sshin	memset(&data_dest, 0, sizeof(data_dest));
164656668Sshin	data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
164756668Sshin	data_dest.su_sin6.sin6_family = AF_INET6;
164856668Sshin	data_dest.su_sin6.sin6_port = savedport;
164956668Sshin	memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
165056668Sshin	memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
165156668Sshin	       (caddr_t)&savedaddr, sizeof(savedaddr));
165256668Sshin}
165356668Sshin#endif
1654