ftpcmd.y revision 56668
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 56668 2000-01-27 09:28:38Z shin $";
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		{
5031592Srgrimes			if ($2)
5041592Srgrimes				cwd(pw->pw_dir);
5051592Srgrimes		}
5061592Srgrimes	| CWD check_login SP pathname CRLF
5071592Srgrimes		{
5081592Srgrimes			if ($2 && $4 != NULL)
5091592Srgrimes				cwd($4);
5101592Srgrimes			if ($4 != NULL)
5111592Srgrimes				free($4);
5121592Srgrimes		}
5131592Srgrimes	| HELP CRLF
5141592Srgrimes		{
5151592Srgrimes			help(cmdtab, (char *) 0);
5161592Srgrimes		}
5171592Srgrimes	| HELP SP STRING CRLF
5181592Srgrimes		{
5191592Srgrimes			char *cp = $3;
5201592Srgrimes
5211592Srgrimes			if (strncasecmp(cp, "SITE", 4) == 0) {
5221592Srgrimes				cp = $3 + 4;
5231592Srgrimes				if (*cp == ' ')
5241592Srgrimes					cp++;
5251592Srgrimes				if (*cp)
5261592Srgrimes					help(sitetab, cp);
5271592Srgrimes				else
5281592Srgrimes					help(sitetab, (char *) 0);
5291592Srgrimes			} else
5301592Srgrimes				help(cmdtab, $3);
5311592Srgrimes		}
5321592Srgrimes	| NOOP CRLF
5331592Srgrimes		{
5341592Srgrimes			reply(200, "NOOP command successful.");
5351592Srgrimes		}
5361592Srgrimes	| MKD check_login SP pathname CRLF
5371592Srgrimes		{
5381592Srgrimes			if ($2 && $4 != NULL)
5391592Srgrimes				makedir($4);
5401592Srgrimes			if ($4 != NULL)
5411592Srgrimes				free($4);
5421592Srgrimes		}
5431592Srgrimes	| RMD check_login SP pathname CRLF
5441592Srgrimes		{
5451592Srgrimes			if ($2 && $4 != NULL)
5461592Srgrimes				removedir($4);
5471592Srgrimes			if ($4 != NULL)
5481592Srgrimes				free($4);
5491592Srgrimes		}
5501592Srgrimes	| PWD check_login CRLF
5511592Srgrimes		{
5521592Srgrimes			if ($2)
5531592Srgrimes				pwd();
5541592Srgrimes		}
5551592Srgrimes	| CDUP check_login CRLF
5561592Srgrimes		{
5571592Srgrimes			if ($2)
5581592Srgrimes				cwd("..");
5591592Srgrimes		}
5601592Srgrimes	| SITE SP HELP CRLF
5611592Srgrimes		{
5621592Srgrimes			help(sitetab, (char *) 0);
5631592Srgrimes		}
5641592Srgrimes	| SITE SP HELP SP STRING CRLF
5651592Srgrimes		{
5661592Srgrimes			help(sitetab, $5);
5671592Srgrimes		}
5681592Srgrimes	| SITE SP UMASK check_login CRLF
5691592Srgrimes		{
5701592Srgrimes			int oldmask;
5711592Srgrimes
5721592Srgrimes			if ($4) {
5731592Srgrimes				oldmask = umask(0);
5741592Srgrimes				(void) umask(oldmask);
5751592Srgrimes				reply(200, "Current UMASK is %03o", oldmask);
5761592Srgrimes			}
5771592Srgrimes		}
5781592Srgrimes	| SITE SP UMASK check_login SP octal_number CRLF
5791592Srgrimes		{
5801592Srgrimes			int oldmask;
5811592Srgrimes
5821592Srgrimes			if ($4) {
5831592Srgrimes				if (($6 == -1) || ($6 > 0777)) {
5841592Srgrimes					reply(501, "Bad UMASK value");
5851592Srgrimes				} else {
5861592Srgrimes					oldmask = umask($6);
5871592Srgrimes					reply(200,
5881592Srgrimes					    "UMASK set to %03o (was %03o)",
5891592Srgrimes					    $6, oldmask);
5901592Srgrimes				}
5911592Srgrimes			}
5921592Srgrimes		}
5931592Srgrimes	| SITE SP CHMOD check_login SP octal_number SP pathname CRLF
5941592Srgrimes		{
5951592Srgrimes			if ($4 && ($8 != NULL)) {
5961592Srgrimes				if ($6 > 0777)
5971592Srgrimes					reply(501,
5981592Srgrimes				"CHMOD: Mode value must be between 0 and 0777");
5991592Srgrimes				else if (chmod($8, $6) < 0)
6001592Srgrimes					perror_reply(550, $8);
6011592Srgrimes				else
6021592Srgrimes					reply(200, "CHMOD command successful.");
6031592Srgrimes			}
6041592Srgrimes			if ($8 != NULL)
6051592Srgrimes				free($8);
6061592Srgrimes		}
6071592Srgrimes	| SITE SP IDLE CRLF
6081592Srgrimes		{
6091592Srgrimes			reply(200,
6101592Srgrimes			    "Current IDLE time limit is %d seconds; max %d",
6111592Srgrimes				timeout, maxtimeout);
6121592Srgrimes		}
6131592Srgrimes	| SITE SP IDLE SP NUMBER CRLF
6141592Srgrimes		{
6151592Srgrimes			if ($5 < 30 || $5 > maxtimeout) {
6161592Srgrimes				reply(501,
6171592Srgrimes			"Maximum IDLE time must be between 30 and %d seconds",
6181592Srgrimes				    maxtimeout);
6191592Srgrimes			} else {
6201592Srgrimes				timeout = $5;
6211592Srgrimes				(void) alarm((unsigned) timeout);
6221592Srgrimes				reply(200,
6231592Srgrimes				    "Maximum IDLE time set to %d seconds",
6241592Srgrimes				    timeout);
6251592Srgrimes			}
6261592Srgrimes		}
6271592Srgrimes	| STOU check_login SP pathname CRLF
6281592Srgrimes		{
6291592Srgrimes			if ($2 && $4 != NULL)
6301592Srgrimes				store($4, "w", 1);
6311592Srgrimes			if ($4 != NULL)
6321592Srgrimes				free($4);
6331592Srgrimes		}
6341592Srgrimes	| SYST CRLF
6351592Srgrimes		{
6361592Srgrimes#ifdef unix
6371592Srgrimes#ifdef BSD
6381592Srgrimes			reply(215, "UNIX Type: L%d Version: BSD-%d",
6391592Srgrimes				NBBY, BSD);
6401592Srgrimes#else /* BSD */
6411592Srgrimes			reply(215, "UNIX Type: L%d", NBBY);
6421592Srgrimes#endif /* BSD */
6431592Srgrimes#else /* unix */
6441592Srgrimes			reply(215, "UNKNOWN Type: L%d", NBBY);
6451592Srgrimes#endif /* unix */
6461592Srgrimes		}
6471592Srgrimes
6481592Srgrimes		/*
6491592Srgrimes		 * SIZE is not in RFC959, but Postel has blessed it and
6501592Srgrimes		 * it will be in the updated RFC.
6511592Srgrimes		 *
6521592Srgrimes		 * Return size of file in a format suitable for
6531592Srgrimes		 * using with RESTART (we just count bytes).
6541592Srgrimes		 */
6551592Srgrimes	| SIZE check_login SP pathname CRLF
6561592Srgrimes		{
6571592Srgrimes			if ($2 && $4 != NULL)
6581592Srgrimes				sizecmd($4);
6591592Srgrimes			if ($4 != NULL)
6601592Srgrimes				free($4);
6611592Srgrimes		}
6621592Srgrimes
6631592Srgrimes		/*
6641592Srgrimes		 * MDTM is not in RFC959, but Postel has blessed it and
6651592Srgrimes		 * it will be in the updated RFC.
6661592Srgrimes		 *
6671592Srgrimes		 * Return modification time of file as an ISO 3307
6681592Srgrimes		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
6691592Srgrimes		 * where xxx is the fractional second (of any precision,
6701592Srgrimes		 * not necessarily 3 digits)
6711592Srgrimes		 */
6721592Srgrimes	| MDTM check_login SP pathname CRLF
6731592Srgrimes		{
6741592Srgrimes			if ($2 && $4 != NULL) {
6751592Srgrimes				struct stat stbuf;
6761592Srgrimes				if (stat($4, &stbuf) < 0)
6771592Srgrimes					reply(550, "%s: %s",
6781592Srgrimes					    $4, strerror(errno));
6791592Srgrimes				else if (!S_ISREG(stbuf.st_mode)) {
6801592Srgrimes					reply(550, "%s: not a plain file.", $4);
6811592Srgrimes				} else {
6821592Srgrimes					struct tm *t;
6831592Srgrimes					t = gmtime(&stbuf.st_mtime);
6841592Srgrimes					reply(213,
68517435Spst					    "%04d%02d%02d%02d%02d%02d",
68617435Spst					    1900 + t->tm_year,
68717435Spst					    t->tm_mon+1, t->tm_mday,
6881592Srgrimes					    t->tm_hour, t->tm_min, t->tm_sec);
6891592Srgrimes				}
6901592Srgrimes			}
6911592Srgrimes			if ($4 != NULL)
6921592Srgrimes				free($4);
6931592Srgrimes		}
6941592Srgrimes	| QUIT CRLF
6951592Srgrimes		{
6961592Srgrimes			reply(221, "Goodbye.");
6971592Srgrimes			dologout(0);
6981592Srgrimes		}
6991592Srgrimes	| error CRLF
7001592Srgrimes		{
7011592Srgrimes			yyerrok;
7021592Srgrimes		}
7031592Srgrimes	;
7041592Srgrimesrcmd
7051592Srgrimes	: RNFR check_login SP pathname CRLF
7061592Srgrimes		{
7071592Srgrimes			char *renamefrom();
7081592Srgrimes
7091592Srgrimes			restart_point = (off_t) 0;
7101592Srgrimes			if ($2 && $4) {
7111592Srgrimes				fromname = renamefrom($4);
7121592Srgrimes				if (fromname == (char *) 0 && $4) {
7131592Srgrimes					free($4);
7141592Srgrimes				}
7151592Srgrimes			}
7161592Srgrimes		}
7171592Srgrimes	| REST SP byte_size CRLF
7181592Srgrimes		{
7191592Srgrimes			fromname = (char *) 0;
7201592Srgrimes			restart_point = $3;	/* XXX $3 is only "int" */
7211592Srgrimes			reply(350, "Restarting at %qd. %s", restart_point,
7221592Srgrimes			    "Send STORE or RETRIEVE to initiate transfer.");
7231592Srgrimes		}
7241592Srgrimes	;
7251592Srgrimes
7261592Srgrimesusername
7271592Srgrimes	: STRING
7281592Srgrimes	;
7291592Srgrimes
7301592Srgrimespassword
7311592Srgrimes	: /* empty */
7321592Srgrimes		{
7331592Srgrimes			$$ = (char *)calloc(1, sizeof(char));
7341592Srgrimes		}
7351592Srgrimes	| STRING
7361592Srgrimes	;
7371592Srgrimes
7381592Srgrimesbyte_size
7391592Srgrimes	: NUMBER
7401592Srgrimes	;
7411592Srgrimes
7421592Srgrimeshost_port
7431592Srgrimes	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
7441592Srgrimes		NUMBER COMMA NUMBER
7451592Srgrimes		{
7461592Srgrimes			char *a, *p;
7471592Srgrimes
74856668Sshin			data_dest.su_len = sizeof(struct sockaddr_in);
74956668Sshin			data_dest.su_family = AF_INET;
75056668Sshin			p = (char *)&data_dest.su_sin.sin_port;
75117435Spst			p[0] = $9; p[1] = $11;
75256668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
7531592Srgrimes			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
7541592Srgrimes		}
7551592Srgrimes	;
7561592Srgrimes
75756668Sshinhost_long_port
75856668Sshin	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
75956668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
76056668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
76156668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
76256668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
76356668Sshin		NUMBER
76456668Sshin		{
76556668Sshin			char *a, *p;
76656668Sshin
76756668Sshin			memset(&data_dest, 0, sizeof(data_dest));
76856668Sshin			data_dest.su_len = sizeof(struct sockaddr_in6);
76956668Sshin			data_dest.su_family = AF_INET6;
77056668Sshin			p = (char *)&data_dest.su_port;
77156668Sshin			p[0] = $39; p[1] = $41;
77256668Sshin			a = (char *)&data_dest.su_sin6.sin6_addr;
77356668Sshin			 a[0] =  $5;  a[1] =  $7;  a[2] =  $9;  a[3] = $11;
77456668Sshin			 a[4] = $13;  a[5] = $15;  a[6] = $17;  a[7] = $19;
77556668Sshin			 a[8] = $21;  a[9] = $23; a[10] = $25; a[11] = $27;
77656668Sshin			a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
77756668Sshin			if (his_addr.su_family == AF_INET6) {
77856668Sshin				/* XXX more sanity checks! */
77956668Sshin				data_dest.su_sin6.sin6_scope_id =
78056668Sshin					his_addr.su_sin6.sin6_scope_id;
78156668Sshin			}
78256668Sshin			if ($1 != 6 || $3 != 16 || $37 != 2)
78356668Sshin				memset(&data_dest, 0, sizeof(data_dest));
78456668Sshin		}
78556668Sshin	| NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
78656668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
78756668Sshin		NUMBER
78856668Sshin		{
78956668Sshin			char *a, *p;
79056668Sshin
79156668Sshin			memset(&data_dest, 0, sizeof(data_dest));
79256668Sshin			data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
79356668Sshin			data_dest.su_family = AF_INET;
79456668Sshin			p = (char *)&data_dest.su_port;
79556668Sshin			p[0] = $15; p[1] = $17;
79656668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
79756668Sshin			a[0] =  $5;  a[1] =  $7;  a[2] =  $9;  a[3] = $11;
79856668Sshin			if ($1 != 4 || $3 != 4 || $13 != 2)
79956668Sshin				memset(&data_dest, 0, sizeof(data_dest));
80056668Sshin		}
80156668Sshin	;
80256668Sshin
8031592Srgrimesform_code
8041592Srgrimes	: N
8051592Srgrimes		{
8061592Srgrimes			$$ = FORM_N;
8071592Srgrimes		}
8081592Srgrimes	| T
8091592Srgrimes		{
8101592Srgrimes			$$ = FORM_T;
8111592Srgrimes		}
8121592Srgrimes	| C
8131592Srgrimes		{
8141592Srgrimes			$$ = FORM_C;
8151592Srgrimes		}
8161592Srgrimes	;
8171592Srgrimes
8181592Srgrimestype_code
8191592Srgrimes	: A
8201592Srgrimes		{
8211592Srgrimes			cmd_type = TYPE_A;
8221592Srgrimes			cmd_form = FORM_N;
8231592Srgrimes		}
8241592Srgrimes	| A SP form_code
8251592Srgrimes		{
8261592Srgrimes			cmd_type = TYPE_A;
8271592Srgrimes			cmd_form = $3;
8281592Srgrimes		}
8291592Srgrimes	| E
8301592Srgrimes		{
8311592Srgrimes			cmd_type = TYPE_E;
8321592Srgrimes			cmd_form = FORM_N;
8331592Srgrimes		}
8341592Srgrimes	| E SP form_code
8351592Srgrimes		{
8361592Srgrimes			cmd_type = TYPE_E;
8371592Srgrimes			cmd_form = $3;
8381592Srgrimes		}
8391592Srgrimes	| I
8401592Srgrimes		{
8411592Srgrimes			cmd_type = TYPE_I;
8421592Srgrimes		}
8431592Srgrimes	| L
8441592Srgrimes		{
8451592Srgrimes			cmd_type = TYPE_L;
8461592Srgrimes			cmd_bytesz = NBBY;
8471592Srgrimes		}
8481592Srgrimes	| L SP byte_size
8491592Srgrimes		{
8501592Srgrimes			cmd_type = TYPE_L;
8511592Srgrimes			cmd_bytesz = $3;
8521592Srgrimes		}
8531592Srgrimes		/* this is for a bug in the BBN ftp */
8541592Srgrimes	| L byte_size
8551592Srgrimes		{
8561592Srgrimes			cmd_type = TYPE_L;
8571592Srgrimes			cmd_bytesz = $2;
8581592Srgrimes		}
8591592Srgrimes	;
8601592Srgrimes
8611592Srgrimesstruct_code
8621592Srgrimes	: F
8631592Srgrimes		{
8641592Srgrimes			$$ = STRU_F;
8651592Srgrimes		}
8661592Srgrimes	| R
8671592Srgrimes		{
8681592Srgrimes			$$ = STRU_R;
8691592Srgrimes		}
8701592Srgrimes	| P
8711592Srgrimes		{
8721592Srgrimes			$$ = STRU_P;
8731592Srgrimes		}
8741592Srgrimes	;
8751592Srgrimes
8761592Srgrimesmode_code
8771592Srgrimes	: S
8781592Srgrimes		{
8791592Srgrimes			$$ = MODE_S;
8801592Srgrimes		}
8811592Srgrimes	| B
8821592Srgrimes		{
8831592Srgrimes			$$ = MODE_B;
8841592Srgrimes		}
8851592Srgrimes	| C
8861592Srgrimes		{
8871592Srgrimes			$$ = MODE_C;
8881592Srgrimes		}
8891592Srgrimes	;
8901592Srgrimes
8911592Srgrimespathname
8921592Srgrimes	: pathstring
8931592Srgrimes		{
8941592Srgrimes			/*
8951592Srgrimes			 * Problem: this production is used for all pathname
8961592Srgrimes			 * processing, but only gives a 550 error reply.
8971592Srgrimes			 * This is a valid reply in some cases but not in others.
8981592Srgrimes			 */
8991592Srgrimes			if (logged_in && $1 && *$1 == '~') {
9001592Srgrimes				glob_t gl;
9011592Srgrimes				int flags =
9021592Srgrimes				 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
9031592Srgrimes
9041592Srgrimes				memset(&gl, 0, sizeof(gl));
9051592Srgrimes				if (glob($1, flags, NULL, &gl) ||
9061592Srgrimes				    gl.gl_pathc == 0) {
9071592Srgrimes					reply(550, "not found");
9081592Srgrimes					$$ = NULL;
9091592Srgrimes				} else {
9101592Srgrimes					$$ = strdup(gl.gl_pathv[0]);
9111592Srgrimes				}
9121592Srgrimes				globfree(&gl);
9131592Srgrimes				free($1);
9141592Srgrimes			} else
9151592Srgrimes				$$ = $1;
9161592Srgrimes		}
9171592Srgrimes	;
9181592Srgrimes
9191592Srgrimespathstring
9201592Srgrimes	: STRING
9211592Srgrimes	;
9221592Srgrimes
9231592Srgrimesoctal_number
9241592Srgrimes	: NUMBER
9251592Srgrimes		{
9261592Srgrimes			int ret, dec, multby, digit;
9271592Srgrimes
9281592Srgrimes			/*
9291592Srgrimes			 * Convert a number that was read as decimal number
9301592Srgrimes			 * to what it would be if it had been read as octal.
9311592Srgrimes			 */
9321592Srgrimes			dec = $1;
9331592Srgrimes			multby = 1;
9341592Srgrimes			ret = 0;
9351592Srgrimes			while (dec) {
9361592Srgrimes				digit = dec%10;
9371592Srgrimes				if (digit > 7) {
9381592Srgrimes					ret = -1;
9391592Srgrimes					break;
9401592Srgrimes				}
9411592Srgrimes				ret += digit * multby;
9421592Srgrimes				multby *= 8;
9431592Srgrimes				dec /= 10;
9441592Srgrimes			}
9451592Srgrimes			$$ = ret;
9461592Srgrimes		}
9471592Srgrimes	;
9481592Srgrimes
9491592Srgrimes
9501592Srgrimescheck_login
9511592Srgrimes	: /* empty */
9521592Srgrimes		{
9531592Srgrimes			if (logged_in)
9541592Srgrimes				$$ = 1;
9551592Srgrimes			else {
9561592Srgrimes				reply(530, "Please login with USER and PASS.");
9571592Srgrimes				$$ = 0;
9581592Srgrimes			}
9591592Srgrimes		}
9601592Srgrimes	;
9611592Srgrimes
9621592Srgrimes%%
9631592Srgrimes
9641592Srgrimesextern jmp_buf errcatch;
9651592Srgrimes
9661592Srgrimes#define	CMD	0	/* beginning of command */
9671592Srgrimes#define	ARGS	1	/* expect miscellaneous arguments */
9681592Srgrimes#define	STR1	2	/* expect SP followed by STRING */
9691592Srgrimes#define	STR2	3	/* expect STRING */
9701592Srgrimes#define	OSTR	4	/* optional SP then STRING */
9711592Srgrimes#define	ZSTR1	5	/* SP then optional STRING */
9721592Srgrimes#define	ZSTR2	6	/* optional STRING after SP */
9731592Srgrimes#define	SITECMD	7	/* SITE command */
9741592Srgrimes#define	NSTR	8	/* Number followed by a string */
9751592Srgrimes
9761592Srgrimesstruct tab {
9771592Srgrimes	char	*name;
9781592Srgrimes	short	token;
9791592Srgrimes	short	state;
9801592Srgrimes	short	implemented;	/* 1 if command is implemented */
9811592Srgrimes	char	*help;
9821592Srgrimes};
9831592Srgrimes
9841592Srgrimesstruct tab cmdtab[] = {		/* In order defined in RFC 765 */
9851592Srgrimes	{ "USER", USER, STR1, 1,	"<sp> username" },
9861592Srgrimes	{ "PASS", PASS, ZSTR1, 1,	"<sp> password" },
9871592Srgrimes	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
9881592Srgrimes	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
9891592Srgrimes	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
9901592Srgrimes	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
9911592Srgrimes	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
99256668Sshin	{ "LPRT", LPRT, ARGS, 1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
99356668Sshin	{ "EPRT", EPRT, STR1, 1,	"<sp> |af|addr|port|" },
9941592Srgrimes	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
99556668Sshin	{ "LPSV", LPSV, ARGS, 1,	"(set server in passive mode)" },
99656668Sshin	{ "EPSV", EPSV, ARGS, 1,	"[<sp> af|ALL]" },
9971592Srgrimes	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
9981592Srgrimes	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
9991592Srgrimes	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
10001592Srgrimes	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
10011592Srgrimes	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
10021592Srgrimes	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
10031592Srgrimes	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
10041592Srgrimes	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
10051592Srgrimes	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
10061592Srgrimes	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
10071592Srgrimes	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
10081592Srgrimes	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
10091592Srgrimes	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
10101592Srgrimes	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
10111592Srgrimes	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
10121592Srgrimes	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
10131592Srgrimes	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
10141592Srgrimes	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
10151592Srgrimes	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
10161592Srgrimes	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
10171592Srgrimes	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
10181592Srgrimes	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
10191592Srgrimes	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
10201592Srgrimes	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
10211592Srgrimes	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
10221592Srgrimes	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
10231592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
10241592Srgrimes	{ "NOOP", NOOP, ARGS, 1,	"" },
10251592Srgrimes	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
10261592Srgrimes	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
10271592Srgrimes	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
10281592Srgrimes	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
10291592Srgrimes	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
10301592Srgrimes	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
10311592Srgrimes	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
10321592Srgrimes	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
10331592Srgrimes	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
10341592Srgrimes	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
10351592Srgrimes	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
10361592Srgrimes	{ NULL,   0,    0,    0,	0 }
10371592Srgrimes};
10381592Srgrimes
10391592Srgrimesstruct tab sitetab[] = {
10401592Srgrimes	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
10411592Srgrimes	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
10421592Srgrimes	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
10431592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
10441592Srgrimes	{ NULL,   0,    0,    0,	0 }
10451592Srgrimes};
10461592Srgrimes
10471592Srgrimesstatic char	*copy __P((char *));
10481592Srgrimesstatic void	 help __P((struct tab *, char *));
10491592Srgrimesstatic struct tab *
10501592Srgrimes		 lookup __P((struct tab *, char *));
105156668Sshinstatic int	 port_check __P((const char *));
105256668Sshinstatic int	 port_check_v6 __P((const char *));
10531592Srgrimesstatic void	 sizecmd __P((char *));
10541592Srgrimesstatic void	 toolong __P((int));
105556668Sshinstatic void	 v4map_data_dest __P((void));
10561592Srgrimesstatic int	 yylex __P((void));
10571592Srgrimes
10581592Srgrimesstatic struct tab *
10591592Srgrimeslookup(p, cmd)
10601592Srgrimes	struct tab *p;
10611592Srgrimes	char *cmd;
10621592Srgrimes{
10631592Srgrimes
10641592Srgrimes	for (; p->name != NULL; p++)
10651592Srgrimes		if (strcmp(cmd, p->name) == 0)
10661592Srgrimes			return (p);
10671592Srgrimes	return (0);
10681592Srgrimes}
10691592Srgrimes
10701592Srgrimes#include <arpa/telnet.h>
10711592Srgrimes
10721592Srgrimes/*
10731592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes.
10741592Srgrimes */
10751592Srgrimeschar *
10761592Srgrimesgetline(s, n, iop)
10771592Srgrimes	char *s;
10781592Srgrimes	int n;
10791592Srgrimes	FILE *iop;
10801592Srgrimes{
10811592Srgrimes	int c;
10821592Srgrimes	register char *cs;
10831592Srgrimes
10841592Srgrimes	cs = s;
10851592Srgrimes/* tmpline may contain saved command from urgent mode interruption */
10861592Srgrimes	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
10871592Srgrimes		*cs++ = tmpline[c];
10881592Srgrimes		if (tmpline[c] == '\n') {
10891592Srgrimes			*cs++ = '\0';
10901592Srgrimes			if (debug)
10911592Srgrimes				syslog(LOG_DEBUG, "command: %s", s);
10921592Srgrimes			tmpline[0] = '\0';
10931592Srgrimes			return(s);
10941592Srgrimes		}
10951592Srgrimes		if (c == 0)
10961592Srgrimes			tmpline[0] = '\0';
10971592Srgrimes	}
10981592Srgrimes	while ((c = getc(iop)) != EOF) {
10991592Srgrimes		c &= 0377;
11001592Srgrimes		if (c == IAC) {
11011592Srgrimes		    if ((c = getc(iop)) != EOF) {
11021592Srgrimes			c &= 0377;
11031592Srgrimes			switch (c) {
11041592Srgrimes			case WILL:
11051592Srgrimes			case WONT:
11061592Srgrimes				c = getc(iop);
11071592Srgrimes				printf("%c%c%c", IAC, DONT, 0377&c);
11081592Srgrimes				(void) fflush(stdout);
11091592Srgrimes				continue;
11101592Srgrimes			case DO:
11111592Srgrimes			case DONT:
11121592Srgrimes				c = getc(iop);
11131592Srgrimes				printf("%c%c%c", IAC, WONT, 0377&c);
11141592Srgrimes				(void) fflush(stdout);
11151592Srgrimes				continue;
11161592Srgrimes			case IAC:
11171592Srgrimes				break;
11181592Srgrimes			default:
11191592Srgrimes				continue;	/* ignore command */
11201592Srgrimes			}
11211592Srgrimes		    }
11221592Srgrimes		}
11231592Srgrimes		*cs++ = c;
11241592Srgrimes		if (--n <= 0 || c == '\n')
11251592Srgrimes			break;
11261592Srgrimes	}
11271592Srgrimes	if (c == EOF && cs == s)
11281592Srgrimes		return (NULL);
11291592Srgrimes	*cs++ = '\0';
11301592Srgrimes	if (debug) {
11311592Srgrimes		if (!guest && strncasecmp("pass ", s, 5) == 0) {
11321592Srgrimes			/* Don't syslog passwords */
11331592Srgrimes			syslog(LOG_DEBUG, "command: %.5s ???", s);
11341592Srgrimes		} else {
11351592Srgrimes			register char *cp;
11361592Srgrimes			register int len;
11371592Srgrimes
11381592Srgrimes			/* Don't syslog trailing CR-LF */
11391592Srgrimes			len = strlen(s);
11401592Srgrimes			cp = s + len - 1;
11411592Srgrimes			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
11421592Srgrimes				--cp;
11431592Srgrimes				--len;
11441592Srgrimes			}
11451592Srgrimes			syslog(LOG_DEBUG, "command: %.*s", len, s);
11461592Srgrimes		}
11471592Srgrimes	}
11481592Srgrimes	return (s);
11491592Srgrimes}
11501592Srgrimes
11511592Srgrimesstatic void
11521592Srgrimestoolong(signo)
11531592Srgrimes	int signo;
11541592Srgrimes{
11551592Srgrimes
11561592Srgrimes	reply(421,
11571592Srgrimes	    "Timeout (%d seconds): closing control connection.", timeout);
11581592Srgrimes	if (logging)
11591592Srgrimes		syslog(LOG_INFO, "User %s timed out after %d seconds",
11601592Srgrimes		    (pw ? pw -> pw_name : "unknown"), timeout);
11611592Srgrimes	dologout(1);
11621592Srgrimes}
11631592Srgrimes
11641592Srgrimesstatic int
11651592Srgrimesyylex()
11661592Srgrimes{
11671592Srgrimes	static int cpos, state;
11681592Srgrimes	char *cp, *cp2;
11691592Srgrimes	struct tab *p;
11701592Srgrimes	int n;
11711592Srgrimes	char c;
11721592Srgrimes
11731592Srgrimes	for (;;) {
11741592Srgrimes		switch (state) {
11751592Srgrimes
11761592Srgrimes		case CMD:
11771592Srgrimes			(void) signal(SIGALRM, toolong);
11781592Srgrimes			(void) alarm((unsigned) timeout);
11791592Srgrimes			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
11801592Srgrimes				reply(221, "You could at least say goodbye.");
11811592Srgrimes				dologout(0);
11821592Srgrimes			}
11831592Srgrimes			(void) alarm(0);
11841592Srgrimes#ifdef SETPROCTITLE
118529574Sphk			if (strncasecmp(cbuf, "PASS", 4) != 0)
11861592Srgrimes				setproctitle("%s: %s", proctitle, cbuf);
11871592Srgrimes#endif /* SETPROCTITLE */
11881592Srgrimes			if ((cp = strchr(cbuf, '\r'))) {
11891592Srgrimes				*cp++ = '\n';
11901592Srgrimes				*cp = '\0';
11911592Srgrimes			}
11921592Srgrimes			if ((cp = strpbrk(cbuf, " \n")))
11931592Srgrimes				cpos = cp - cbuf;
11941592Srgrimes			if (cpos == 0)
11951592Srgrimes				cpos = 4;
11961592Srgrimes			c = cbuf[cpos];
11971592Srgrimes			cbuf[cpos] = '\0';
11981592Srgrimes			upper(cbuf);
11991592Srgrimes			p = lookup(cmdtab, cbuf);
12001592Srgrimes			cbuf[cpos] = c;
12013776Spst			if (p != 0) {
12021592Srgrimes				if (p->implemented == 0) {
12031592Srgrimes					nack(p->name);
12041592Srgrimes					longjmp(errcatch,0);
12051592Srgrimes					/* NOTREACHED */
12061592Srgrimes				}
12071592Srgrimes				state = p->state;
12081592Srgrimes				yylval.s = p->name;
12091592Srgrimes				return (p->token);
12101592Srgrimes			}
12111592Srgrimes			break;
12121592Srgrimes
12131592Srgrimes		case SITECMD:
12141592Srgrimes			if (cbuf[cpos] == ' ') {
12151592Srgrimes				cpos++;
12161592Srgrimes				return (SP);
12171592Srgrimes			}
12181592Srgrimes			cp = &cbuf[cpos];
12191592Srgrimes			if ((cp2 = strpbrk(cp, " \n")))
12201592Srgrimes				cpos = cp2 - cbuf;
12211592Srgrimes			c = cbuf[cpos];
12221592Srgrimes			cbuf[cpos] = '\0';
12231592Srgrimes			upper(cp);
12241592Srgrimes			p = lookup(sitetab, cp);
12251592Srgrimes			cbuf[cpos] = c;
12263777Spst			if (guest == 0 && p != 0) {
12271592Srgrimes				if (p->implemented == 0) {
12281592Srgrimes					state = CMD;
12291592Srgrimes					nack(p->name);
12301592Srgrimes					longjmp(errcatch,0);
12311592Srgrimes					/* NOTREACHED */
12321592Srgrimes				}
12331592Srgrimes				state = p->state;
12341592Srgrimes				yylval.s = p->name;
12351592Srgrimes				return (p->token);
12361592Srgrimes			}
12371592Srgrimes			state = CMD;
12381592Srgrimes			break;
12391592Srgrimes
12401592Srgrimes		case OSTR:
12411592Srgrimes			if (cbuf[cpos] == '\n') {
12421592Srgrimes				state = CMD;
12431592Srgrimes				return (CRLF);
12441592Srgrimes			}
12451592Srgrimes			/* FALLTHROUGH */
12461592Srgrimes
12471592Srgrimes		case STR1:
12481592Srgrimes		case ZSTR1:
12491592Srgrimes		dostr1:
12501592Srgrimes			if (cbuf[cpos] == ' ') {
12511592Srgrimes				cpos++;
125251979Salfred				state = state == OSTR ? STR2 : state+1;
12531592Srgrimes				return (SP);
12541592Srgrimes			}
12551592Srgrimes			break;
12561592Srgrimes
12571592Srgrimes		case ZSTR2:
12581592Srgrimes			if (cbuf[cpos] == '\n') {
12591592Srgrimes				state = CMD;
12601592Srgrimes				return (CRLF);
12611592Srgrimes			}
12621592Srgrimes			/* FALLTHROUGH */
12631592Srgrimes
12641592Srgrimes		case STR2:
12651592Srgrimes			cp = &cbuf[cpos];
12661592Srgrimes			n = strlen(cp);
12671592Srgrimes			cpos += n - 1;
12681592Srgrimes			/*
12691592Srgrimes			 * Make sure the string is nonempty and \n terminated.
12701592Srgrimes			 */
12711592Srgrimes			if (n > 1 && cbuf[cpos] == '\n') {
12721592Srgrimes				cbuf[cpos] = '\0';
12731592Srgrimes				yylval.s = copy(cp);
12741592Srgrimes				cbuf[cpos] = '\n';
12751592Srgrimes				state = ARGS;
12761592Srgrimes				return (STRING);
12771592Srgrimes			}
12781592Srgrimes			break;
12791592Srgrimes
12801592Srgrimes		case NSTR:
12811592Srgrimes			if (cbuf[cpos] == ' ') {
12821592Srgrimes				cpos++;
12831592Srgrimes				return (SP);
12841592Srgrimes			}
12851592Srgrimes			if (isdigit(cbuf[cpos])) {
12861592Srgrimes				cp = &cbuf[cpos];
12871592Srgrimes				while (isdigit(cbuf[++cpos]))
12881592Srgrimes					;
12891592Srgrimes				c = cbuf[cpos];
12901592Srgrimes				cbuf[cpos] = '\0';
12911592Srgrimes				yylval.i = atoi(cp);
12921592Srgrimes				cbuf[cpos] = c;
12931592Srgrimes				state = STR1;
12941592Srgrimes				return (NUMBER);
12951592Srgrimes			}
12961592Srgrimes			state = STR1;
12971592Srgrimes			goto dostr1;
12981592Srgrimes
12991592Srgrimes		case ARGS:
13001592Srgrimes			if (isdigit(cbuf[cpos])) {
13011592Srgrimes				cp = &cbuf[cpos];
13021592Srgrimes				while (isdigit(cbuf[++cpos]))
13031592Srgrimes					;
13041592Srgrimes				c = cbuf[cpos];
13051592Srgrimes				cbuf[cpos] = '\0';
13061592Srgrimes				yylval.i = atoi(cp);
13071592Srgrimes				cbuf[cpos] = c;
13081592Srgrimes				return (NUMBER);
13091592Srgrimes			}
131056668Sshin			if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
131156668Sshin			 && !isalnum(cbuf[cpos + 3])) {
131256668Sshin				cpos += 3;
131356668Sshin				return ALL;
131456668Sshin			}
13151592Srgrimes			switch (cbuf[cpos++]) {
13161592Srgrimes
13171592Srgrimes			case '\n':
13181592Srgrimes				state = CMD;
13191592Srgrimes				return (CRLF);
13201592Srgrimes
13211592Srgrimes			case ' ':
13221592Srgrimes				return (SP);
13231592Srgrimes
13241592Srgrimes			case ',':
13251592Srgrimes				return (COMMA);
13261592Srgrimes
13271592Srgrimes			case 'A':
13281592Srgrimes			case 'a':
13291592Srgrimes				return (A);
13301592Srgrimes
13311592Srgrimes			case 'B':
13321592Srgrimes			case 'b':
13331592Srgrimes				return (B);
13341592Srgrimes
13351592Srgrimes			case 'C':
13361592Srgrimes			case 'c':
13371592Srgrimes				return (C);
13381592Srgrimes
13391592Srgrimes			case 'E':
13401592Srgrimes			case 'e':
13411592Srgrimes				return (E);
13421592Srgrimes
13431592Srgrimes			case 'F':
13441592Srgrimes			case 'f':
13451592Srgrimes				return (F);
13461592Srgrimes
13471592Srgrimes			case 'I':
13481592Srgrimes			case 'i':
13491592Srgrimes				return (I);
13501592Srgrimes
13511592Srgrimes			case 'L':
13521592Srgrimes			case 'l':
13531592Srgrimes				return (L);
13541592Srgrimes
13551592Srgrimes			case 'N':
13561592Srgrimes			case 'n':
13571592Srgrimes				return (N);
13581592Srgrimes
13591592Srgrimes			case 'P':
13601592Srgrimes			case 'p':
13611592Srgrimes				return (P);
13621592Srgrimes
13631592Srgrimes			case 'R':
13641592Srgrimes			case 'r':
13651592Srgrimes				return (R);
13661592Srgrimes
13671592Srgrimes			case 'S':
13681592Srgrimes			case 's':
13691592Srgrimes				return (S);
13701592Srgrimes
13711592Srgrimes			case 'T':
13721592Srgrimes			case 't':
13731592Srgrimes				return (T);
13741592Srgrimes
13751592Srgrimes			}
13761592Srgrimes			break;
13771592Srgrimes
13781592Srgrimes		default:
13791592Srgrimes			fatal("Unknown state in scanner.");
13801592Srgrimes		}
13811592Srgrimes		yyerror((char *) 0);
13821592Srgrimes		state = CMD;
13831592Srgrimes		longjmp(errcatch,0);
13841592Srgrimes	}
13851592Srgrimes}
13861592Srgrimes
13871592Srgrimesvoid
13881592Srgrimesupper(s)
13891592Srgrimes	char *s;
13901592Srgrimes{
13911592Srgrimes	while (*s != '\0') {
13921592Srgrimes		if (islower(*s))
13931592Srgrimes			*s = toupper(*s);
13941592Srgrimes		s++;
13951592Srgrimes	}
13961592Srgrimes}
13971592Srgrimes
13981592Srgrimesstatic char *
13991592Srgrimescopy(s)
14001592Srgrimes	char *s;
14011592Srgrimes{
14021592Srgrimes	char *p;
14031592Srgrimes
14041592Srgrimes	p = malloc((unsigned) strlen(s) + 1);
14051592Srgrimes	if (p == NULL)
14061592Srgrimes		fatal("Ran out of memory.");
14071592Srgrimes	(void) strcpy(p, s);
14081592Srgrimes	return (p);
14091592Srgrimes}
14101592Srgrimes
14111592Srgrimesstatic void
14121592Srgrimeshelp(ctab, s)
14131592Srgrimes	struct tab *ctab;
14141592Srgrimes	char *s;
14151592Srgrimes{
14161592Srgrimes	struct tab *c;
14171592Srgrimes	int width, NCMDS;
14181592Srgrimes	char *type;
14191592Srgrimes
14201592Srgrimes	if (ctab == sitetab)
14211592Srgrimes		type = "SITE ";
14221592Srgrimes	else
14231592Srgrimes		type = "";
14241592Srgrimes	width = 0, NCMDS = 0;
14251592Srgrimes	for (c = ctab; c->name != NULL; c++) {
14261592Srgrimes		int len = strlen(c->name);
14271592Srgrimes
14281592Srgrimes		if (len > width)
14291592Srgrimes			width = len;
14301592Srgrimes		NCMDS++;
14311592Srgrimes	}
14321592Srgrimes	width = (width + 8) &~ 7;
14331592Srgrimes	if (s == 0) {
14341592Srgrimes		int i, j, w;
14351592Srgrimes		int columns, lines;
14361592Srgrimes
14371592Srgrimes		lreply(214, "The following %scommands are recognized %s.",
14381592Srgrimes		    type, "(* =>'s unimplemented)");
14391592Srgrimes		columns = 76 / width;
14401592Srgrimes		if (columns == 0)
14411592Srgrimes			columns = 1;
14421592Srgrimes		lines = (NCMDS + columns - 1) / columns;
14431592Srgrimes		for (i = 0; i < lines; i++) {
14441592Srgrimes			printf("   ");
14451592Srgrimes			for (j = 0; j < columns; j++) {
14461592Srgrimes				c = ctab + j * lines + i;
14471592Srgrimes				printf("%s%c", c->name,
14481592Srgrimes					c->implemented ? ' ' : '*');
14491592Srgrimes				if (c + lines >= &ctab[NCMDS])
14501592Srgrimes					break;
14511592Srgrimes				w = strlen(c->name) + 1;
14521592Srgrimes				while (w < width) {
14531592Srgrimes					putchar(' ');
14541592Srgrimes					w++;
14551592Srgrimes				}
14561592Srgrimes			}
14571592Srgrimes			printf("\r\n");
14581592Srgrimes		}
14591592Srgrimes		(void) fflush(stdout);
14601592Srgrimes		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
14611592Srgrimes		return;
14621592Srgrimes	}
14631592Srgrimes	upper(s);
14641592Srgrimes	c = lookup(ctab, s);
14651592Srgrimes	if (c == (struct tab *)0) {
14661592Srgrimes		reply(502, "Unknown command %s.", s);
14671592Srgrimes		return;
14681592Srgrimes	}
14691592Srgrimes	if (c->implemented)
14701592Srgrimes		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
14711592Srgrimes	else
14721592Srgrimes		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
14731592Srgrimes		    c->name, c->help);
14741592Srgrimes}
14751592Srgrimes
14761592Srgrimesstatic void
14771592Srgrimessizecmd(filename)
14781592Srgrimes	char *filename;
14791592Srgrimes{
14801592Srgrimes	switch (type) {
14811592Srgrimes	case TYPE_L:
14821592Srgrimes	case TYPE_I: {
14831592Srgrimes		struct stat stbuf;
14841592Srgrimes		if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
14851592Srgrimes			reply(550, "%s: not a plain file.", filename);
14861592Srgrimes		else
14871592Srgrimes			reply(213, "%qu", stbuf.st_size);
14881592Srgrimes		break; }
14891592Srgrimes	case TYPE_A: {
14901592Srgrimes		FILE *fin;
14911592Srgrimes		int c;
14921592Srgrimes		off_t count;
14931592Srgrimes		struct stat stbuf;
14941592Srgrimes		fin = fopen(filename, "r");
14951592Srgrimes		if (fin == NULL) {
14961592Srgrimes			perror_reply(550, filename);
14971592Srgrimes			return;
14981592Srgrimes		}
14991592Srgrimes		if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) {
15001592Srgrimes			reply(550, "%s: not a plain file.", filename);
15011592Srgrimes			(void) fclose(fin);
15021592Srgrimes			return;
15031592Srgrimes		}
15041592Srgrimes
15051592Srgrimes		count = 0;
15061592Srgrimes		while((c=getc(fin)) != EOF) {
15071592Srgrimes			if (c == '\n')	/* will get expanded to \r\n */
15081592Srgrimes				count++;
15091592Srgrimes			count++;
15101592Srgrimes		}
15111592Srgrimes		(void) fclose(fin);
15121592Srgrimes
15131592Srgrimes		reply(213, "%qd", count);
15141592Srgrimes		break; }
15151592Srgrimes	default:
15161592Srgrimes		reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
15171592Srgrimes	}
15181592Srgrimes}
151956668Sshin
152056668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
152156668Sshinstatic int
152256668Sshinport_check(pcmd)
152356668Sshin	const char *pcmd;
152456668Sshin{
152556668Sshin	if (his_addr.su_family == AF_INET) {
152656668Sshin		if (data_dest.su_family != AF_INET) {
152756668Sshin			usedefault = 1;
152856668Sshin			reply(500, "Invalid address rejected.");
152956668Sshin			return 1;
153056668Sshin		}
153156668Sshin		if (paranoid &&
153256668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
153356668Sshin		     memcmp(&data_dest.su_sin.sin_addr,
153456668Sshin			    &his_addr.su_sin.sin_addr,
153556668Sshin			    sizeof(data_dest.su_sin.sin_addr)))) {
153656668Sshin			usedefault = 1;
153756668Sshin			reply(500, "Illegal PORT range rejected.");
153856668Sshin		} else {
153956668Sshin			usedefault = 0;
154056668Sshin			if (pdata >= 0) {
154156668Sshin				(void) close(pdata);
154256668Sshin				pdata = -1;
154356668Sshin			}
154456668Sshin			reply(200, "%s command successful.", pcmd);
154556668Sshin		}
154656668Sshin		return 1;
154756668Sshin	}
154856668Sshin	return 0;
154956668Sshin}
155056668Sshin
155156668Sshin#ifdef INET6
155256668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
155356668Sshinstatic int
155456668Sshinport_check_v6(pcmd)
155556668Sshin	const char *pcmd;
155656668Sshin{
155756668Sshin	if (his_addr.su_family == AF_INET6) {
155856668Sshin		if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
155956668Sshin			/* Convert data_dest into v4 mapped sockaddr.*/
156056668Sshin			v4map_data_dest();
156156668Sshin		if (data_dest.su_family != AF_INET6) {
156256668Sshin			usedefault = 1;
156356668Sshin			reply(500, "Invalid address rejected.");
156456668Sshin			return 1;
156556668Sshin		}
156656668Sshin		if (paranoid &&
156756668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
156856668Sshin		     memcmp(&data_dest.su_sin6.sin6_addr,
156956668Sshin			    &his_addr.su_sin6.sin6_addr,
157056668Sshin			    sizeof(data_dest.su_sin6.sin6_addr)))) {
157156668Sshin			usedefault = 1;
157256668Sshin			reply(500, "Illegal PORT range rejected.");
157356668Sshin		} else {
157456668Sshin			usedefault = 0;
157556668Sshin			if (pdata >= 0) {
157656668Sshin				(void) close(pdata);
157756668Sshin				pdata = -1;
157856668Sshin			}
157956668Sshin			reply(200, "%s command successful.", pcmd);
158056668Sshin		}
158156668Sshin		return 1;
158256668Sshin	}
158356668Sshin	return 0;
158456668Sshin}
158556668Sshin
158656668Sshinstatic void
158756668Sshinv4map_data_dest()
158856668Sshin{
158956668Sshin	struct in_addr savedaddr;
159056668Sshin	int savedport;
159156668Sshin
159256668Sshin	if (data_dest.su_family != AF_INET) {
159356668Sshin		usedefault = 1;
159456668Sshin		reply(500, "Invalid address rejected.");
159556668Sshin		return;
159656668Sshin	}
159756668Sshin
159856668Sshin	savedaddr = data_dest.su_sin.sin_addr;
159956668Sshin	savedport = data_dest.su_port;
160056668Sshin
160156668Sshin	memset(&data_dest, 0, sizeof(data_dest));
160256668Sshin	data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
160356668Sshin	data_dest.su_sin6.sin6_family = AF_INET6;
160456668Sshin	data_dest.su_sin6.sin6_port = savedport;
160556668Sshin	memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
160656668Sshin	memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
160756668Sshin	       (caddr_t)&savedaddr, sizeof(savedaddr));
160856668Sshin}
160956668Sshin#endif
1610