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