ftpcmd.y revision 92282
192282Sobrien/*	$NetBSD: ftpcmd.y,v 1.66 2001/12/01 10:25:30 lukem Exp $	*/
279968Sobrien
379968Sobrien/*-
479968Sobrien * Copyright (c) 1997-2001 The NetBSD Foundation, Inc.
579968Sobrien * All rights reserved.
679968Sobrien *
779968Sobrien * This code is derived from software contributed to The NetBSD Foundation
879968Sobrien * by Luke Mewburn.
979968Sobrien *
1079968Sobrien * Redistribution and use in source and binary forms, with or without
1179968Sobrien * modification, are permitted provided that the following conditions
1279968Sobrien * are met:
1379968Sobrien * 1. Redistributions of source code must retain the above copyright
1479968Sobrien *    notice, this list of conditions and the following disclaimer.
1579968Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1679968Sobrien *    notice, this list of conditions and the following disclaimer in the
1779968Sobrien *    documentation and/or other materials provided with the distribution.
1879968Sobrien * 3. All advertising materials mentioning features or use of this software
1979968Sobrien *    must display the following acknowledgement:
2079968Sobrien *        This product includes software developed by the NetBSD
2179968Sobrien *        Foundation, Inc. and its contributors.
2279968Sobrien * 4. Neither the name of The NetBSD Foundation nor the names of its
2379968Sobrien *    contributors may be used to endorse or promote products derived
2479968Sobrien *    from this software without specific prior written permission.
2579968Sobrien *
2679968Sobrien * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2779968Sobrien * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2879968Sobrien * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2979968Sobrien * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
3079968Sobrien * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3179968Sobrien * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3279968Sobrien * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3379968Sobrien * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3479968Sobrien * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3579968Sobrien * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3679968Sobrien * POSSIBILITY OF SUCH DAMAGE.
3779968Sobrien */
3879968Sobrien
3979968Sobrien/*
4079968Sobrien * Copyright (c) 1985, 1988, 1993, 1994
4179968Sobrien *	The Regents of the University of California.  All rights reserved.
4279968Sobrien *
4379968Sobrien * Redistribution and use in source and binary forms, with or without
4479968Sobrien * modification, are permitted provided that the following conditions
4579968Sobrien * are met:
4679968Sobrien * 1. Redistributions of source code must retain the above copyright
4779968Sobrien *    notice, this list of conditions and the following disclaimer.
4879968Sobrien * 2. Redistributions in binary form must reproduce the above copyright
4979968Sobrien *    notice, this list of conditions and the following disclaimer in the
5079968Sobrien *    documentation and/or other materials provided with the distribution.
5179968Sobrien * 3. All advertising materials mentioning features or use of this software
5279968Sobrien *    must display the following acknowledgement:
5379968Sobrien *	This product includes software developed by the University of
5479968Sobrien *	California, Berkeley and its contributors.
5579968Sobrien * 4. Neither the name of the University nor the names of its contributors
5679968Sobrien *    may be used to endorse or promote products derived from this software
5779968Sobrien *    without specific prior written permission.
5879968Sobrien *
5979968Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
6079968Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
6179968Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
6279968Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
6379968Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
6479968Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
6579968Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
6679968Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
6779968Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6879968Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6979968Sobrien * SUCH DAMAGE.
7079968Sobrien *
7179968Sobrien *	@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94
7279968Sobrien */
7379968Sobrien
7479968Sobrien/*
7579968Sobrien * Grammar for FTP commands.
7679968Sobrien * See RFC 959.
7779968Sobrien */
7879968Sobrien
7979968Sobrien%{
8079968Sobrien#include "lukemftpd.h"
8179968Sobrien
8279968Sobrien#include "extern.h"
8379968Sobrien#include "version.h"
8479968Sobrien
8579968Sobrienstatic	int cmd_type;
8679968Sobrienstatic	int cmd_form;
8779968Sobrienstatic	int cmd_bytesz;
8879968Sobrien
8979968Sobrienchar	cbuf[FTP_BUFLEN];
9079968Sobrienchar	*cmdp;
9179968Sobrienchar	*fromname;
9279968Sobrien
9379968Sobrien%}
9479968Sobrien
9579968Sobrien%union {
9679968Sobrien	int	i;
9779968Sobrien	char   *s;
9879968Sobrien}
9979968Sobrien
10079968Sobrien%token
10179968Sobrien	A	B	C	E	F	I
10279968Sobrien	L	N	P	R	S	T
10379968Sobrien
10479968Sobrien	SP	CRLF	COMMA
10579968Sobrien
10679968Sobrien	USER	PASS	ACCT	CWD	CDUP	SMNT
10779968Sobrien	QUIT	REIN	PORT	PASV	TYPE	STRU
10879968Sobrien	MODE	RETR	STOR	STOU	APPE	ALLO
10979968Sobrien	REST	RNFR	RNTO	ABOR	DELE	RMD
11079968Sobrien	MKD	PWD	LIST	NLST	SITE	SYST
11179968Sobrien	STAT	HELP	NOOP
11279968Sobrien
11379968Sobrien	AUTH	ADAT	PROT	PBSZ	CCC	MIC
11479968Sobrien	CONF	ENC
11579968Sobrien
11679968Sobrien	FEAT	OPTS
11779968Sobrien
11879968Sobrien	SIZE	MDTM	MLST	MLSD
11979968Sobrien
12079968Sobrien	LPRT	LPSV	EPRT	EPSV
12179968Sobrien
12279968Sobrien	MAIL	MLFL	MRCP	MRSQ	MSAM	MSND
12379968Sobrien	MSOM
12479968Sobrien
12579968Sobrien	CHMOD	IDLE	RATEGET	RATEPUT	UMASK
12679968Sobrien
12779968Sobrien	LEXERR
12879968Sobrien
12979968Sobrien%token	<s> STRING
13079968Sobrien%token	<s> ALL
13179968Sobrien%token	<i> NUMBER
13279968Sobrien
13379968Sobrien%type	<i> check_login octal_number byte_size
13479968Sobrien%type	<i> struct_code mode_code type_code form_code decimal_integer
13579968Sobrien%type	<s> pathstring pathname password username
13679968Sobrien%type	<s> mechanism_name base64data prot_code
13779968Sobrien
13879968Sobrien%start	cmd_sel
13979968Sobrien
14079968Sobrien%%
14179968Sobrien
14279968Sobriencmd_sel
14379968Sobrien	: cmd
14479968Sobrien		{
14579968Sobrien			fromname = NULL;
14679968Sobrien			restart_point = (off_t) 0;
14779968Sobrien		}
14879968Sobrien
14979968Sobrien	| rcmd
15079968Sobrien
15179968Sobrien	;
15279968Sobrien
15379968Sobriencmd
15479968Sobrien						/* RFC 959 */
15579968Sobrien	: USER SP username CRLF
15679968Sobrien		{
15779968Sobrien			user($3);
15879968Sobrien			free($3);
15979968Sobrien		}
16079968Sobrien
16179968Sobrien	| PASS SP password CRLF
16279968Sobrien		{
16379968Sobrien			pass($3);
16479968Sobrien			memset($3, 0, strlen($3));
16579968Sobrien			free($3);
16679968Sobrien		}
16779968Sobrien
16879968Sobrien	| CWD check_login CRLF
16979968Sobrien		{
17079968Sobrien			if ($2)
17179968Sobrien				cwd(homedir);
17279968Sobrien		}
17379968Sobrien
17479968Sobrien	| CWD check_login SP pathname CRLF
17579968Sobrien		{
17679968Sobrien			if ($2 && $4 != NULL)
17779968Sobrien				cwd($4);
17879968Sobrien			if ($4 != NULL)
17979968Sobrien				free($4);
18079968Sobrien		}
18179968Sobrien
18279968Sobrien	| CDUP check_login CRLF
18379968Sobrien		{
18479968Sobrien			if ($2)
18579968Sobrien				cwd("..");
18679968Sobrien		}
18779968Sobrien
18879968Sobrien	| QUIT CRLF
18979968Sobrien		{
19079968Sobrien			if (logged_in) {
19179968Sobrien				reply(-221, "%s", "");
19279968Sobrien				reply(0,
19379968Sobrien "Data traffic for this session was " LLF " byte%s in " LLF " file%s.",
19479968Sobrien				    (LLT)total_data, PLURAL(total_data),
19579968Sobrien				    (LLT)total_files, PLURAL(total_files));
19679968Sobrien				reply(0,
19779968Sobrien "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.",
19879968Sobrien				    (LLT)total_bytes, PLURAL(total_bytes),
19979968Sobrien				    (LLT)total_xfers, PLURAL(total_xfers));
20079968Sobrien			}
20179968Sobrien			reply(221,
20279968Sobrien			    "Thank you for using the FTP service on %s.",
20379968Sobrien			    hostname);
20479968Sobrien			if (logged_in && logging) {
20579968Sobrien				syslog(LOG_INFO,
20679968Sobrien		"Data traffic: " LLF " byte%s in " LLF " file%s",
20779968Sobrien				    (LLT)total_data, PLURAL(total_data),
20879968Sobrien				    (LLT)total_files, PLURAL(total_files));
20979968Sobrien				syslog(LOG_INFO,
21079968Sobrien		"Total traffic: " LLF " byte%s in " LLF " transfer%s",
21179968Sobrien				    (LLT)total_bytes, PLURAL(total_bytes),
21279968Sobrien				    (LLT)total_xfers, PLURAL(total_xfers));
21379968Sobrien			}
21479968Sobrien
21579968Sobrien			dologout(0);
21679968Sobrien		}
21779968Sobrien
21879968Sobrien	| PORT check_login SP host_port CRLF
21979968Sobrien		{
22079968Sobrien			if ($2)
22179968Sobrien				port_check("PORT", AF_INET);
22279968Sobrien		}
22379968Sobrien
22479968Sobrien	| LPRT check_login SP host_long_port4 CRLF
22579968Sobrien		{
22679968Sobrien			if ($2)
22779968Sobrien				port_check("LPRT", AF_INET);
22879968Sobrien		}
22979968Sobrien
23079968Sobrien	| LPRT check_login SP host_long_port6 CRLF
23179968Sobrien		{
23279968Sobrien#ifdef INET6
23379968Sobrien			if ($2)
23479968Sobrien				port_check("LPRT", AF_INET6);
23579968Sobrien#else
23679968Sobrien			reply(500, "IPv6 support not available.");
23779968Sobrien#endif
23879968Sobrien		}
23979968Sobrien
24079968Sobrien	| EPRT check_login SP STRING CRLF
24179968Sobrien		{
24279968Sobrien			if ($2) {
24379968Sobrien				if (extended_port($4) == 0)
24479968Sobrien					port_check("EPRT", -1);
24579968Sobrien			}
24679968Sobrien			free($4);
24779968Sobrien		}
24879968Sobrien
24979968Sobrien	| PASV check_login CRLF
25079968Sobrien		{
25179968Sobrien			if ($2) {
25279968Sobrien				if (CURCLASS_FLAGS_ISSET(passive))
25379968Sobrien					passive();
25479968Sobrien				else
25579968Sobrien					reply(500, "PASV mode not available.");
25679968Sobrien			}
25779968Sobrien		}
25879968Sobrien
25979968Sobrien	| LPSV check_login CRLF
26079968Sobrien		{
26179968Sobrien			if ($2) {
26279968Sobrien				if (epsvall)
26379968Sobrien					reply(501,
26479968Sobrien					    "LPSV disallowed after EPSV ALL");
26579968Sobrien				else
26679968Sobrien					long_passive("LPSV", PF_UNSPEC);
26779968Sobrien			}
26879968Sobrien		}
26979968Sobrien
27079968Sobrien	| EPSV check_login SP NUMBER CRLF
27179968Sobrien		{
27279968Sobrien			if ($2)
27379968Sobrien				long_passive("EPSV", epsvproto2af($4));
27479968Sobrien		}
27579968Sobrien
27679968Sobrien	| EPSV check_login SP ALL CRLF
27779968Sobrien		{
27879968Sobrien			if ($2) {
27979968Sobrien				reply(200, "EPSV ALL command successful.");
28079968Sobrien				epsvall++;
28179968Sobrien			}
28279968Sobrien		}
28379968Sobrien
28479968Sobrien	| EPSV check_login CRLF
28579968Sobrien		{
28679968Sobrien			if ($2)
28779968Sobrien				long_passive("EPSV", PF_UNSPEC);
28879968Sobrien		}
28979968Sobrien
29079968Sobrien	| TYPE check_login SP type_code CRLF
29179968Sobrien		{
29279968Sobrien			if ($2) {
29379968Sobrien
29479968Sobrien			switch (cmd_type) {
29579968Sobrien
29679968Sobrien			case TYPE_A:
29779968Sobrien				if (cmd_form == FORM_N) {
29879968Sobrien					reply(200, "Type set to A.");
29979968Sobrien					type = cmd_type;
30079968Sobrien					form = cmd_form;
30179968Sobrien				} else
30279968Sobrien					reply(504, "Form must be N.");
30379968Sobrien				break;
30479968Sobrien
30579968Sobrien			case TYPE_E:
30679968Sobrien				reply(504, "Type E not implemented.");
30779968Sobrien				break;
30879968Sobrien
30979968Sobrien			case TYPE_I:
31079968Sobrien				reply(200, "Type set to I.");
31179968Sobrien				type = cmd_type;
31279968Sobrien				break;
31379968Sobrien
31479968Sobrien			case TYPE_L:
31579968Sobrien#if NBBY == 8
31679968Sobrien				if (cmd_bytesz == 8) {
31779968Sobrien					reply(200,
31879968Sobrien					    "Type set to L (byte size 8).");
31979968Sobrien					type = cmd_type;
32079968Sobrien				} else
32179968Sobrien					reply(504, "Byte size must be 8.");
32279968Sobrien#else /* NBBY == 8 */
32379968Sobrien				UNIMPLEMENTED for NBBY != 8
32479968Sobrien#endif /* NBBY == 8 */
32579968Sobrien			}
32679968Sobrien
32779968Sobrien			}
32879968Sobrien		}
32979968Sobrien
33079968Sobrien	| STRU check_login SP struct_code CRLF
33179968Sobrien		{
33279968Sobrien			if ($2) {
33379968Sobrien				switch ($4) {
33479968Sobrien
33579968Sobrien				case STRU_F:
33679968Sobrien					reply(200, "STRU F ok.");
33779968Sobrien					break;
33879968Sobrien
33979968Sobrien				default:
34079968Sobrien					reply(504, "Unimplemented STRU type.");
34179968Sobrien				}
34279968Sobrien			}
34379968Sobrien		}
34479968Sobrien
34579968Sobrien	| MODE check_login SP mode_code CRLF
34679968Sobrien		{
34779968Sobrien			if ($2) {
34879968Sobrien				switch ($4) {
34979968Sobrien
35079968Sobrien				case MODE_S:
35179968Sobrien					reply(200, "MODE S ok.");
35279968Sobrien					break;
35379968Sobrien
35479968Sobrien				default:
35579968Sobrien					reply(502, "Unimplemented MODE type.");
35679968Sobrien				}
35779968Sobrien			}
35879968Sobrien		}
35979968Sobrien
36079968Sobrien	| RETR check_login SP pathname CRLF
36179968Sobrien		{
36279968Sobrien			if ($2 && $4 != NULL)
36379968Sobrien				retrieve(NULL, $4);
36479968Sobrien			if ($4 != NULL)
36579968Sobrien				free($4);
36679968Sobrien		}
36779968Sobrien
36879968Sobrien	| STOR SP pathname CRLF
36979968Sobrien		{
37079968Sobrien			if (check_write($3, 1))
37179968Sobrien				store($3, "w", 0);
37279968Sobrien			if ($3 != NULL)
37379968Sobrien				free($3);
37479968Sobrien		}
37579968Sobrien
37679968Sobrien	| STOU SP pathname CRLF
37779968Sobrien		{
37879968Sobrien			if (check_write($3, 1))
37979968Sobrien				store($3, "w", 1);
38079968Sobrien			if ($3 != NULL)
38179968Sobrien				free($3);
38279968Sobrien		}
38379968Sobrien
38479968Sobrien	| APPE SP pathname CRLF
38579968Sobrien		{
38679968Sobrien			if (check_write($3, 1))
38779968Sobrien				store($3, "a", 0);
38879968Sobrien			if ($3 != NULL)
38979968Sobrien				free($3);
39079968Sobrien		}
39179968Sobrien
39279968Sobrien	| ALLO check_login SP NUMBER CRLF
39379968Sobrien		{
39479968Sobrien			if ($2)
39579968Sobrien				reply(202, "ALLO command ignored.");
39679968Sobrien		}
39779968Sobrien
39879968Sobrien	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
39979968Sobrien		{
40079968Sobrien			if ($2)
40179968Sobrien				reply(202, "ALLO command ignored.");
40279968Sobrien		}
40379968Sobrien
40479968Sobrien	| RNTO SP pathname CRLF
40579968Sobrien		{
40679968Sobrien			if (check_write($3, 0)) {
40779968Sobrien				if (fromname) {
40879968Sobrien					renamecmd(fromname, $3);
40979968Sobrien					free(fromname);
41079968Sobrien					fromname = NULL;
41179968Sobrien				} else {
41279968Sobrien					reply(503, "Bad sequence of commands.");
41379968Sobrien				}
41479968Sobrien			}
41579968Sobrien			if ($3 != NULL)
41679968Sobrien				free($3);
41779968Sobrien		}
41879968Sobrien
41979968Sobrien	| ABOR check_login CRLF
42079968Sobrien		{
42179968Sobrien			if (is_oob)
42279968Sobrien				abor();
42379968Sobrien			else if ($2)
42479968Sobrien				reply(225, "ABOR command successful.");
42579968Sobrien		}
42679968Sobrien
42779968Sobrien	| DELE SP pathname CRLF
42879968Sobrien		{
42979968Sobrien			if (check_write($3, 0))
43079968Sobrien				delete($3);
43179968Sobrien			if ($3 != NULL)
43279968Sobrien				free($3);
43379968Sobrien		}
43479968Sobrien
43579968Sobrien	| RMD SP pathname CRLF
43679968Sobrien		{
43779968Sobrien			if (check_write($3, 0))
43879968Sobrien				removedir($3);
43979968Sobrien			if ($3 != NULL)
44079968Sobrien				free($3);
44179968Sobrien		}
44279968Sobrien
44379968Sobrien	| MKD SP pathname CRLF
44479968Sobrien		{
44579968Sobrien			if (check_write($3, 0))
44679968Sobrien				makedir($3);
44779968Sobrien			if ($3 != NULL)
44879968Sobrien				free($3);
44979968Sobrien		}
45079968Sobrien
45179968Sobrien	| PWD check_login CRLF
45279968Sobrien		{
45379968Sobrien			if ($2)
45479968Sobrien				pwd();
45579968Sobrien		}
45679968Sobrien
45779968Sobrien	| LIST check_login CRLF
45879968Sobrien		{
45979968Sobrien			char *argv[] = { INTERNAL_LS, "-lgA", NULL };
46079968Sobrien
46179968Sobrien			if ($2)
46279968Sobrien				retrieve(argv, "");
46379968Sobrien		}
46479968Sobrien
46579968Sobrien	| LIST check_login SP pathname CRLF
46679968Sobrien		{
46779968Sobrien			char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL };
46879968Sobrien
46979968Sobrien			if ($2 && $4 != NULL) {
47079968Sobrien				argv[2] = $4;
47179968Sobrien				retrieve(argv, $4);
47279968Sobrien			}
47379968Sobrien			if ($4 != NULL)
47479968Sobrien				free($4);
47579968Sobrien		}
47679968Sobrien
47779968Sobrien	| NLST check_login CRLF
47879968Sobrien		{
47979968Sobrien			if ($2)
48079968Sobrien				send_file_list(".");
48179968Sobrien		}
48279968Sobrien
48379968Sobrien	| NLST check_login SP pathname CRLF
48479968Sobrien		{
48579968Sobrien			if ($2)
48679968Sobrien				send_file_list($4);
48779968Sobrien			free($4);
48879968Sobrien		}
48979968Sobrien
49079968Sobrien	| SITE SP HELP CRLF
49179968Sobrien		{
49279968Sobrien			help(sitetab, NULL);
49379968Sobrien		}
49479968Sobrien
49579968Sobrien	| SITE SP CHMOD SP octal_number SP pathname CRLF
49679968Sobrien		{
49779968Sobrien			if (check_write($7, 0)) {
49879968Sobrien				if ($5 > 0777)
49979968Sobrien					reply(501,
50079968Sobrien				"CHMOD: Mode value must be between 0 and 0777");
50179968Sobrien				else if (chmod($7, $5) < 0)
50279968Sobrien					perror_reply(550, $7);
50379968Sobrien				else
50479968Sobrien					reply(200, "CHMOD command successful.");
50579968Sobrien			}
50679968Sobrien			if ($7 != NULL)
50779968Sobrien				free($7);
50879968Sobrien		}
50979968Sobrien
51079968Sobrien	| SITE SP HELP SP STRING CRLF
51179968Sobrien		{
51279968Sobrien			help(sitetab, $5);
51379968Sobrien			free($5);
51479968Sobrien		}
51579968Sobrien
51679968Sobrien	| SITE SP IDLE check_login CRLF
51779968Sobrien		{
51879968Sobrien			if ($4) {
51979968Sobrien				reply(200,
52079968Sobrien			    "Current IDLE time limit is %d seconds; max %d",
52179968Sobrien				    curclass.timeout, curclass.maxtimeout);
52279968Sobrien			}
52379968Sobrien		}
52479968Sobrien
52579968Sobrien	| SITE SP IDLE check_login SP NUMBER CRLF
52679968Sobrien		{
52779968Sobrien			if ($4) {
52879968Sobrien				if ($6 < 30 || $6 > curclass.maxtimeout) {
52979968Sobrien					reply(501,
53079968Sobrien			    "IDLE time limit must be between 30 and %d seconds",
53179968Sobrien					    curclass.maxtimeout);
53279968Sobrien				} else {
53379968Sobrien					curclass.timeout = $6;
53479968Sobrien					(void) alarm(curclass.timeout);
53579968Sobrien					reply(200,
53679968Sobrien					    "IDLE time limit set to %d seconds",
53779968Sobrien					    curclass.timeout);
53879968Sobrien				}
53979968Sobrien			}
54079968Sobrien		}
54179968Sobrien
54279968Sobrien	| SITE SP RATEGET check_login CRLF
54379968Sobrien		{
54479968Sobrien			if ($4) {
54579968Sobrien				reply(200,
54679968Sobrien				    "Current RATEGET is " LLF " bytes/sec",
54779968Sobrien				    (LLT)curclass.rateget);
54879968Sobrien			}
54979968Sobrien		}
55079968Sobrien
55179968Sobrien	| SITE SP RATEGET check_login SP STRING CRLF
55279968Sobrien		{
55379968Sobrien			char *p = $6;
55479968Sobrien			LLT rate;
55579968Sobrien
55679968Sobrien			if ($4) {
55779968Sobrien				rate = strsuftoll(p);
55879968Sobrien				if (rate == -1)
55979968Sobrien					reply(501, "Invalid RATEGET %s", p);
56079968Sobrien				else if (curclass.maxrateget &&
56179968Sobrien				    rate > curclass.maxrateget)
56279968Sobrien					reply(501,
56379968Sobrien			"RATEGET " LLF " is larger than maximum RATEGET " LLF,
56479968Sobrien					    (LLT)rate,
56579968Sobrien					    (LLT)curclass.maxrateget);
56679968Sobrien				else {
56779968Sobrien					curclass.rateget = rate;
56879968Sobrien					reply(200,
56979968Sobrien					    "RATEGET set to " LLF " bytes/sec",
57079968Sobrien					    (LLT)curclass.rateget);
57179968Sobrien				}
57279968Sobrien			}
57379968Sobrien			free($6);
57479968Sobrien		}
57579968Sobrien
57679968Sobrien	| SITE SP RATEPUT check_login CRLF
57779968Sobrien		{
57879968Sobrien			if ($4) {
57979968Sobrien				reply(200,
58079968Sobrien				    "Current RATEPUT is " LLF " bytes/sec",
58179968Sobrien				    (LLT)curclass.rateput);
58279968Sobrien			}
58379968Sobrien		}
58479968Sobrien
58579968Sobrien	| SITE SP RATEPUT check_login SP STRING CRLF
58679968Sobrien		{
58779968Sobrien			char *p = $6;
58879968Sobrien			LLT rate;
58979968Sobrien
59079968Sobrien			if ($4) {
59179968Sobrien				rate = strsuftoll(p);
59279968Sobrien				if (rate == -1)
59379968Sobrien					reply(501, "Invalid RATEPUT %s", p);
59479968Sobrien				else if (curclass.maxrateput &&
59579968Sobrien				    rate > curclass.maxrateput)
59679968Sobrien					reply(501,
59779968Sobrien			"RATEPUT " LLF " is larger than maximum RATEPUT " LLF,
59879968Sobrien					    (LLT)rate,
59979968Sobrien					    (LLT)curclass.maxrateput);
60079968Sobrien				else {
60179968Sobrien					curclass.rateput = rate;
60279968Sobrien					reply(200,
60379968Sobrien					    "RATEPUT set to " LLF " bytes/sec",
60479968Sobrien					    (LLT)curclass.rateput);
60579968Sobrien				}
60679968Sobrien			}
60779968Sobrien			free($6);
60879968Sobrien		}
60979968Sobrien
61079968Sobrien	| SITE SP UMASK check_login CRLF
61179968Sobrien		{
61279968Sobrien			int oldmask;
61379968Sobrien
61479968Sobrien			if ($4) {
61579968Sobrien				oldmask = umask(0);
61679968Sobrien				(void) umask(oldmask);
61779968Sobrien				reply(200, "Current UMASK is %03o", oldmask);
61879968Sobrien			}
61979968Sobrien		}
62079968Sobrien
62179968Sobrien	| SITE SP UMASK check_login SP octal_number CRLF
62279968Sobrien		{
62379968Sobrien			int oldmask;
62479968Sobrien
62579968Sobrien			if ($4 && CURCLASS_FLAGS_ISSET(modify)) {
62679968Sobrien				if (($6 == -1) || ($6 > 0777)) {
62779968Sobrien					reply(501, "Bad UMASK value");
62879968Sobrien				} else {
62979968Sobrien					oldmask = umask($6);
63079968Sobrien					reply(200,
63179968Sobrien					    "UMASK set to %03o (was %03o)",
63279968Sobrien					    $6, oldmask);
63379968Sobrien				}
63479968Sobrien			}
63579968Sobrien		}
63679968Sobrien
63779968Sobrien	| SYST CRLF
63879968Sobrien		{
63979968Sobrien			if (EMPTYSTR(version))
64079968Sobrien				reply(215, "UNIX Type: L%d", NBBY);
64179968Sobrien			else
64279968Sobrien				reply(215, "UNIX Type: L%d Version: %s", NBBY,
64379968Sobrien				    version);
64479968Sobrien		}
64579968Sobrien
64679968Sobrien	| STAT check_login SP pathname CRLF
64779968Sobrien		{
64879968Sobrien			if ($2 && $4 != NULL)
64979968Sobrien				statfilecmd($4);
65079968Sobrien			if ($4 != NULL)
65179968Sobrien				free($4);
65279968Sobrien		}
65379968Sobrien
65479968Sobrien	| STAT CRLF
65579968Sobrien		{
65679968Sobrien			if (is_oob)
65779968Sobrien				statxfer();
65879968Sobrien			else
65979968Sobrien				statcmd();
66079968Sobrien		}
66179968Sobrien
66279968Sobrien	| HELP CRLF
66379968Sobrien		{
66479968Sobrien			help(cmdtab, NULL);
66579968Sobrien		}
66679968Sobrien
66779968Sobrien	| HELP SP STRING CRLF
66879968Sobrien		{
66979968Sobrien			char *cp = $3;
67079968Sobrien
67179968Sobrien			if (strncasecmp(cp, "SITE", 4) == 0) {
67279968Sobrien				cp = $3 + 4;
67379968Sobrien				if (*cp == ' ')
67479968Sobrien					cp++;
67579968Sobrien				if (*cp)
67679968Sobrien					help(sitetab, cp);
67779968Sobrien				else
67879968Sobrien					help(sitetab, NULL);
67979968Sobrien			} else
68079968Sobrien				help(cmdtab, $3);
68179968Sobrien			free($3);
68279968Sobrien		}
68379968Sobrien
68479968Sobrien	| NOOP CRLF
68579968Sobrien		{
68679968Sobrien			reply(200, "NOOP command successful.");
68779968Sobrien		}
68879968Sobrien
68979968Sobrien						/* RFC 2228 */
69079968Sobrien	| AUTH SP mechanism_name CRLF
69179968Sobrien		{
69279968Sobrien			reply(502, "RFC 2228 authentication not implemented.");
69379968Sobrien			free($3);
69479968Sobrien		}
69579968Sobrien
69679968Sobrien	| ADAT SP base64data CRLF
69779968Sobrien		{
69879968Sobrien			reply(503,
69979968Sobrien			    "Please set authentication state with AUTH.");
70079968Sobrien			free($3);
70179968Sobrien		}
70279968Sobrien
70379968Sobrien	| PROT SP prot_code CRLF
70479968Sobrien		{
70579968Sobrien			reply(503,
70679968Sobrien			    "Please set protection buffer size with PBSZ.");
70779968Sobrien			free($3);
70879968Sobrien		}
70979968Sobrien
71079968Sobrien	| PBSZ SP decimal_integer CRLF
71179968Sobrien		{
71279968Sobrien			reply(503,
71379968Sobrien			    "Please set authentication state with AUTH.");
71479968Sobrien		}
71579968Sobrien
71679968Sobrien	| CCC CRLF
71779968Sobrien		{
71879968Sobrien			reply(533, "No protection enabled.");
71979968Sobrien		}
72079968Sobrien
72179968Sobrien	| MIC SP base64data CRLF
72279968Sobrien		{
72379968Sobrien			reply(502, "RFC 2228 authentication not implemented.");
72479968Sobrien			free($3);
72579968Sobrien		}
72679968Sobrien
72779968Sobrien	| CONF SP base64data CRLF
72879968Sobrien		{
72979968Sobrien			reply(502, "RFC 2228 authentication not implemented.");
73079968Sobrien			free($3);
73179968Sobrien		}
73279968Sobrien
73379968Sobrien	| ENC SP base64data CRLF
73479968Sobrien		{
73579968Sobrien			reply(502, "RFC 2228 authentication not implemented.");
73679968Sobrien			free($3);
73779968Sobrien		}
73879968Sobrien
73979968Sobrien						/* RFC 2389 */
74079968Sobrien	| FEAT CRLF
74179968Sobrien		{
74279968Sobrien
74379968Sobrien			feat();
74479968Sobrien		}
74579968Sobrien
74679968Sobrien	| OPTS SP STRING CRLF
74779968Sobrien		{
74879968Sobrien
74979968Sobrien			opts($3);
75079968Sobrien			free($3);
75179968Sobrien		}
75279968Sobrien
75379968Sobrien
75479968Sobrien				/* extensions from draft-ietf-ftpext-mlst-11 */
75579968Sobrien
75679968Sobrien		/*
75779968Sobrien		 * Return size of file in a format suitable for
75879968Sobrien		 * using with RESTART (we just count bytes).
75979968Sobrien		 */
76079968Sobrien	| SIZE check_login SP pathname CRLF
76179968Sobrien		{
76279968Sobrien			if ($2 && $4 != NULL)
76379968Sobrien				sizecmd($4);
76479968Sobrien			if ($4 != NULL)
76579968Sobrien				free($4);
76679968Sobrien		}
76779968Sobrien
76879968Sobrien		/*
76979968Sobrien		 * Return modification time of file as an ISO 3307
77079968Sobrien		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
77179968Sobrien		 * where xxx is the fractional second (of any precision,
77279968Sobrien		 * not necessarily 3 digits)
77379968Sobrien		 */
77479968Sobrien	| MDTM check_login SP pathname CRLF
77579968Sobrien		{
77679968Sobrien			if ($2 && $4 != NULL) {
77779968Sobrien				struct stat stbuf;
77879968Sobrien				if (stat($4, &stbuf) < 0)
77979968Sobrien					perror_reply(550, $4);
78079968Sobrien				else if (!S_ISREG(stbuf.st_mode)) {
78179968Sobrien					reply(550, "%s: not a plain file.", $4);
78279968Sobrien				} else {
78379968Sobrien					struct tm *t;
78479968Sobrien
78579968Sobrien					t = gmtime(&stbuf.st_mtime);
78679968Sobrien					reply(213,
78779968Sobrien					    "%04d%02d%02d%02d%02d%02d",
78879968Sobrien					    TM_YEAR_BASE + t->tm_year,
78979968Sobrien					    t->tm_mon+1, t->tm_mday,
79079968Sobrien					    t->tm_hour, t->tm_min, t->tm_sec);
79179968Sobrien				}
79279968Sobrien			}
79379968Sobrien			if ($4 != NULL)
79479968Sobrien				free($4);
79579968Sobrien		}
79679968Sobrien
79779968Sobrien	| MLST check_login SP pathname CRLF
79879968Sobrien		{
79979968Sobrien			if ($2 && $4 != NULL)
80079968Sobrien				mlst($4);
80179968Sobrien			if ($4 != NULL)
80279968Sobrien				free($4);
80379968Sobrien		}
80479968Sobrien
80579968Sobrien	| MLST check_login CRLF
80679968Sobrien		{
80779968Sobrien			mlst(NULL);
80879968Sobrien		}
80979968Sobrien
81079968Sobrien	| MLSD check_login SP pathname CRLF
81179968Sobrien		{
81279968Sobrien			if ($2 && $4 != NULL)
81379968Sobrien				mlsd($4);
81479968Sobrien			if ($4 != NULL)
81579968Sobrien				free($4);
81679968Sobrien		}
81779968Sobrien
81879968Sobrien	| MLSD check_login CRLF
81979968Sobrien		{
82079968Sobrien			mlsd(NULL);
82179968Sobrien		}
82279968Sobrien
82379968Sobrien	| error CRLF
82479968Sobrien		{
82579968Sobrien			yyerrok;
82679968Sobrien		}
82779968Sobrien	;
82879968Sobrien
82979968Sobrienrcmd
83079968Sobrien	: REST check_login SP byte_size CRLF
83179968Sobrien		{
83279968Sobrien			if ($2) {
83379968Sobrien				fromname = NULL;
83479968Sobrien				restart_point = $4; /* XXX: $4 is only "int" */
83579968Sobrien				reply(350,
83679968Sobrien    "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.",
83779968Sobrien				    (LLT)restart_point);
83879968Sobrien			}
83979968Sobrien		}
84079968Sobrien
84179968Sobrien	| RNFR SP pathname CRLF
84279968Sobrien		{
84379968Sobrien			restart_point = (off_t) 0;
84479968Sobrien			if (check_write($3, 0))
84579968Sobrien				fromname = renamefrom($3);
84679968Sobrien			if ($3 != NULL)
84779968Sobrien				free($3);
84879968Sobrien		}
84979968Sobrien	;
85079968Sobrien
85179968Sobrienusername
85279968Sobrien	: STRING
85379968Sobrien	;
85479968Sobrien
85579968Sobrienpassword
85679968Sobrien	: /* empty */
85779968Sobrien		{
85879968Sobrien			$$ = (char *)calloc(1, sizeof(char));
85979968Sobrien		}
86079968Sobrien
86179968Sobrien	| STRING
86279968Sobrien	;
86379968Sobrien
86479968Sobrienbyte_size
86579968Sobrien	: NUMBER
86679968Sobrien	;
86779968Sobrien
86879968Sobrienhost_port
86979968Sobrien	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
87079968Sobrien		NUMBER COMMA NUMBER
87179968Sobrien		{
87279968Sobrien			char *a, *p;
87379968Sobrien
87479968Sobrien			memset(&data_dest, 0, sizeof(data_dest));
87579968Sobrien			data_dest.su_len = sizeof(struct sockaddr_in);
87679968Sobrien			data_dest.su_family = AF_INET;
87779968Sobrien			p = (char *)&data_dest.su_port;
87879968Sobrien			p[0] = $9; p[1] = $11;
87979968Sobrien			a = (char *)&data_dest.su_addr;
88079968Sobrien			a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
88179968Sobrien		}
88279968Sobrien	;
88379968Sobrien
88479968Sobrienhost_long_port4
88579968Sobrien	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
88679968Sobrien		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
88779968Sobrien		NUMBER
88879968Sobrien		{
88979968Sobrien			char *a, *p;
89079968Sobrien
89179968Sobrien			memset(&data_dest, 0, sizeof(data_dest));
89279968Sobrien			data_dest.su_len = sizeof(struct sockaddr_in);
89379968Sobrien			data_dest.su_family = AF_INET;
89479968Sobrien			p = (char *)&data_dest.su_port;
89579968Sobrien			p[0] = $15; p[1] = $17;
89679968Sobrien			a = (char *)&data_dest.su_addr;
89779968Sobrien			a[0] =  $5;  a[1] =  $7;  a[2] =  $9;  a[3] = $11;
89879968Sobrien
89979968Sobrien			/* reject invalid LPRT command */
90079968Sobrien			if ($1 != 4 || $3 != 4 || $13 != 2)
90179968Sobrien				memset(&data_dest, 0, sizeof(data_dest));
90279968Sobrien		}
90379968Sobrien	;
90479968Sobrien
90579968Sobrienhost_long_port6
90679968Sobrien	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
90779968Sobrien		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
90879968Sobrien		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
90979968Sobrien		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
91079968Sobrien		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
91179968Sobrien		NUMBER
91279968Sobrien		{
91379968Sobrien#ifdef INET6
91479968Sobrien			char *a, *p;
91579968Sobrien
91679968Sobrien			memset(&data_dest, 0, sizeof(data_dest));
91779968Sobrien			data_dest.su_len = sizeof(struct sockaddr_in6);
91879968Sobrien			data_dest.su_family = AF_INET6;
91979968Sobrien			p = (char *)&data_dest.su_port;
92079968Sobrien			p[0] = $39; p[1] = $41;
92179968Sobrien			a = (char *)&data_dest.si_su.su_sin6.sin6_addr;
92279968Sobrien			 a[0] =  $5;  a[1] =  $7;  a[2] =  $9;  a[3] = $11;
92379968Sobrien			 a[4] = $13;  a[5] = $15;  a[6] = $17;  a[7] = $19;
92479968Sobrien			 a[8] = $21;  a[9] = $23; a[10] = $25; a[11] = $27;
92579968Sobrien			a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
92679968Sobrien			if (his_addr.su_family == AF_INET6) {
92779968Sobrien				/* XXX: more sanity checks! */
92879968Sobrien				data_dest.su_scope_id = his_addr.su_scope_id;
92979968Sobrien			}
93079968Sobrien#else
93179968Sobrien			memset(&data_dest, 0, sizeof(data_dest));
93279968Sobrien#endif /* INET6 */
93379968Sobrien			/* reject invalid LPRT command */
93479968Sobrien			if ($1 != 6 || $3 != 16 || $37 != 2)
93579968Sobrien				memset(&data_dest, 0, sizeof(data_dest));
93679968Sobrien		}
93779968Sobrien	;
93879968Sobrien
93979968Sobrienform_code
94079968Sobrien	: N
94179968Sobrien		{
94279968Sobrien			$$ = FORM_N;
94379968Sobrien		}
94479968Sobrien
94579968Sobrien	| T
94679968Sobrien		{
94779968Sobrien			$$ = FORM_T;
94879968Sobrien		}
94979968Sobrien
95079968Sobrien	| C
95179968Sobrien		{
95279968Sobrien			$$ = FORM_C;
95379968Sobrien		}
95479968Sobrien	;
95579968Sobrien
95679968Sobrientype_code
95779968Sobrien	: A
95879968Sobrien		{
95979968Sobrien			cmd_type = TYPE_A;
96079968Sobrien			cmd_form = FORM_N;
96179968Sobrien		}
96279968Sobrien
96379968Sobrien	| A SP form_code
96479968Sobrien		{
96579968Sobrien			cmd_type = TYPE_A;
96679968Sobrien			cmd_form = $3;
96779968Sobrien		}
96879968Sobrien
96979968Sobrien	| E
97079968Sobrien		{
97179968Sobrien			cmd_type = TYPE_E;
97279968Sobrien			cmd_form = FORM_N;
97379968Sobrien		}
97479968Sobrien
97579968Sobrien	| E SP form_code
97679968Sobrien		{
97779968Sobrien			cmd_type = TYPE_E;
97879968Sobrien			cmd_form = $3;
97979968Sobrien		}
98079968Sobrien
98179968Sobrien	| I
98279968Sobrien		{
98379968Sobrien			cmd_type = TYPE_I;
98479968Sobrien		}
98579968Sobrien
98679968Sobrien	| L
98779968Sobrien		{
98879968Sobrien			cmd_type = TYPE_L;
98979968Sobrien			cmd_bytesz = NBBY;
99079968Sobrien		}
99179968Sobrien
99279968Sobrien	| L SP byte_size
99379968Sobrien		{
99479968Sobrien			cmd_type = TYPE_L;
99579968Sobrien			cmd_bytesz = $3;
99679968Sobrien		}
99779968Sobrien
99879968Sobrien		/* this is for a bug in the BBN ftp */
99979968Sobrien	| L byte_size
100079968Sobrien		{
100179968Sobrien			cmd_type = TYPE_L;
100279968Sobrien			cmd_bytesz = $2;
100379968Sobrien		}
100479968Sobrien	;
100579968Sobrien
100679968Sobrienstruct_code
100779968Sobrien	: F
100879968Sobrien		{
100979968Sobrien			$$ = STRU_F;
101079968Sobrien		}
101179968Sobrien
101279968Sobrien	| R
101379968Sobrien		{
101479968Sobrien			$$ = STRU_R;
101579968Sobrien		}
101679968Sobrien
101779968Sobrien	| P
101879968Sobrien		{
101979968Sobrien			$$ = STRU_P;
102079968Sobrien		}
102179968Sobrien	;
102279968Sobrien
102379968Sobrienmode_code
102479968Sobrien	: S
102579968Sobrien		{
102679968Sobrien			$$ = MODE_S;
102779968Sobrien		}
102879968Sobrien
102979968Sobrien	| B
103079968Sobrien		{
103179968Sobrien			$$ = MODE_B;
103279968Sobrien		}
103379968Sobrien
103479968Sobrien	| C
103579968Sobrien		{
103679968Sobrien			$$ = MODE_C;
103779968Sobrien		}
103879968Sobrien	;
103979968Sobrien
104079968Sobrienpathname
104179968Sobrien	: pathstring
104279968Sobrien		{
104379968Sobrien			/*
104479968Sobrien			 * Problem: this production is used for all pathname
104579968Sobrien			 * processing, but only gives a 550 error reply.
104679968Sobrien			 * This is a valid reply in some cases but not in
104779968Sobrien			 * others.
104879968Sobrien			 */
104979968Sobrien			if (logged_in && $1 && *$1 == '~') {
105079968Sobrien				char	*path, *home, *result;
105179968Sobrien				size_t	len;
105279968Sobrien
105379968Sobrien				path = strchr($1 + 1, '/');
105479968Sobrien				if (path != NULL)
105579968Sobrien					*path++ = '\0';
105679968Sobrien				if ($1[1] == '\0')
105779968Sobrien					home = homedir;
105879968Sobrien				else {
105992282Sobrien					struct passwd	*hpw;
106079968Sobrien
106192282Sobrien					if ((hpw = getpwnam($1 + 1)) != NULL)
106292282Sobrien						home = hpw->pw_dir;
106379968Sobrien					else
106479968Sobrien						home = $1;
106579968Sobrien				}
106679968Sobrien				len = strlen(home) + 1;
106779968Sobrien				if (path != NULL)
106879968Sobrien					len += strlen(path) + 1;
106979968Sobrien				if ((result = malloc(len)) == NULL)
107079968Sobrien					fatal("Local resource failure: malloc");
107179968Sobrien				strlcpy(result, home, len);
107279968Sobrien				if (path != NULL) {
107379968Sobrien					strlcat(result, "/", len);
107479968Sobrien					strlcat(result, path, len);
107579968Sobrien				}
107679968Sobrien				$$ = result;
107779968Sobrien				free($1);
107879968Sobrien			} else
107979968Sobrien				$$ = $1;
108079968Sobrien		}
108179968Sobrien	;
108279968Sobrien
108379968Sobrienpathstring
108479968Sobrien	: STRING
108579968Sobrien	;
108679968Sobrien
108779968Sobrienoctal_number
108879968Sobrien	: NUMBER
108979968Sobrien		{
109079968Sobrien			int ret, dec, multby, digit;
109179968Sobrien
109279968Sobrien			/*
109379968Sobrien			 * Convert a number that was read as decimal number
109479968Sobrien			 * to what it would be if it had been read as octal.
109579968Sobrien			 */
109679968Sobrien			dec = $1;
109779968Sobrien			multby = 1;
109879968Sobrien			ret = 0;
109979968Sobrien			while (dec) {
110079968Sobrien				digit = dec%10;
110179968Sobrien				if (digit > 7) {
110279968Sobrien					ret = -1;
110379968Sobrien					break;
110479968Sobrien				}
110579968Sobrien				ret += digit * multby;
110679968Sobrien				multby *= 8;
110779968Sobrien				dec /= 10;
110879968Sobrien			}
110979968Sobrien			$$ = ret;
111079968Sobrien		}
111179968Sobrien	;
111279968Sobrien
111379968Sobrienmechanism_name
111479968Sobrien	: STRING
111579968Sobrien	;
111679968Sobrien
111779968Sobrienbase64data
111879968Sobrien	: STRING
111979968Sobrien	;
112079968Sobrien
112179968Sobrienprot_code
112279968Sobrien	: STRING
112379968Sobrien	;
112479968Sobrien
112579968Sobriendecimal_integer
112679968Sobrien	: NUMBER
112779968Sobrien	;
112879968Sobrien
112979968Sobriencheck_login
113079968Sobrien	: /* empty */
113179968Sobrien		{
113279968Sobrien			if (logged_in)
113379968Sobrien				$$ = 1;
113479968Sobrien			else {
113579968Sobrien				reply(530, "Please login with USER and PASS.");
113679968Sobrien				$$ = 0;
113779968Sobrien				hasyyerrored = 1;
113879968Sobrien			}
113979968Sobrien		}
114079968Sobrien	;
114179968Sobrien
114279968Sobrien%%
114379968Sobrien
114479968Sobrien#define	CMD	0	/* beginning of command */
114579968Sobrien#define	ARGS	1	/* expect miscellaneous arguments */
114679968Sobrien#define	STR1	2	/* expect SP followed by STRING */
114779968Sobrien#define	STR2	3	/* expect STRING */
114879968Sobrien#define	OSTR	4	/* optional SP then STRING */
114979968Sobrien#define	ZSTR1	5	/* SP then optional STRING */
115079968Sobrien#define	ZSTR2	6	/* optional STRING after SP */
115179968Sobrien#define	SITECMD	7	/* SITE command */
115279968Sobrien#define	NSTR	8	/* Number followed by a string */
115379968Sobrien#define NOARGS	9	/* No arguments allowed */
115479968Sobrien#define EOLN	10	/* End of line */
115579968Sobrien
115679968Sobrienstruct tab cmdtab[] = {
115779968Sobrien				/* From RFC 959, in order defined (5.3.1) */
115879968Sobrien	{ "USER", USER, STR1,	1,	"<sp> username" },
115979968Sobrien	{ "PASS", PASS, ZSTR1,	1,	"<sp> password" },
116079968Sobrien	{ "ACCT", ACCT, STR1,	0,	"(specify account)" },
116179968Sobrien	{ "CWD",  CWD,  OSTR,	1,	"[ <sp> directory-name ]" },
116279968Sobrien	{ "CDUP", CDUP, NOARGS,	1,	"(change to parent directory)" },
116379968Sobrien	{ "SMNT", SMNT, ARGS,	0,	"(structure mount)" },
116479968Sobrien	{ "QUIT", QUIT, NOARGS,	1,	"(terminate service)" },
116579968Sobrien	{ "REIN", REIN, NOARGS,	0,	"(reinitialize server state)" },
116679968Sobrien	{ "PORT", PORT, ARGS,	1,	"<sp> b0, b1, b2, b3, b4" },
116779968Sobrien	{ "LPRT", LPRT, ARGS,	1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
116879968Sobrien	{ "EPRT", EPRT, STR1,	1,	"<sp> |af|addr|port|" },
116979968Sobrien	{ "PASV", PASV, NOARGS,	1,	"(set server in passive mode)" },
117079968Sobrien	{ "LPSV", LPSV, ARGS,	1,	"(set server in passive mode)" },
117179968Sobrien	{ "EPSV", EPSV, ARGS,	1,	"[<sp> af|ALL]" },
117279968Sobrien	{ "TYPE", TYPE, ARGS,	1,	"<sp> [ A | E | I | L ]" },
117379968Sobrien	{ "STRU", STRU, ARGS,	1,	"(specify file structure)" },
117479968Sobrien	{ "MODE", MODE, ARGS,	1,	"(specify transfer mode)" },
117579968Sobrien	{ "RETR", RETR, STR1,	1,	"<sp> file-name" },
117679968Sobrien	{ "STOR", STOR, STR1,	1,	"<sp> file-name" },
117779968Sobrien	{ "STOU", STOU, STR1,	1,	"<sp> file-name" },
117879968Sobrien	{ "APPE", APPE, STR1,	1,	"<sp> file-name" },
117979968Sobrien	{ "ALLO", ALLO, ARGS,	1,	"allocate storage (vacuously)" },
118079968Sobrien	{ "REST", REST, ARGS,	1,	"<sp> offset (restart command)" },
118179968Sobrien	{ "RNFR", RNFR, STR1,	1,	"<sp> file-name" },
118279968Sobrien	{ "RNTO", RNTO, STR1,	1,	"<sp> file-name" },
118379968Sobrien	{ "ABOR", ABOR, NOARGS,	4,	"(abort operation)" },
118479968Sobrien	{ "DELE", DELE, STR1,	1,	"<sp> file-name" },
118579968Sobrien	{ "RMD",  RMD,  STR1,	1,	"<sp> path-name" },
118679968Sobrien	{ "MKD",  MKD,  STR1,	1,	"<sp> path-name" },
118779968Sobrien	{ "PWD",  PWD,  NOARGS,	1,	"(return current directory)" },
118879968Sobrien	{ "LIST", LIST, OSTR,	1,	"[ <sp> path-name ]" },
118979968Sobrien	{ "NLST", NLST, OSTR,	1,	"[ <sp> path-name ]" },
119079968Sobrien	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
119179968Sobrien	{ "SYST", SYST, NOARGS,	1,	"(get type of operating system)" },
119279968Sobrien	{ "STAT", STAT, OSTR,	4,	"[ <sp> path-name ]" },
119379968Sobrien	{ "HELP", HELP, OSTR,	1,	"[ <sp> <string> ]" },
119479968Sobrien	{ "NOOP", NOOP, NOARGS,	2,	"" },
119579968Sobrien
119679968Sobrien				/* From RFC 2228, in order defined */
119779968Sobrien	{ "AUTH", AUTH, STR1,	1,	"<sp> mechanism-name" },
119879968Sobrien	{ "ADAT", ADAT, STR1,	1,	"<sp> base-64-data" },
119979968Sobrien	{ "PROT", PROT, STR1,	1,	"<sp> prot-code" },
120079968Sobrien	{ "PBSZ", PBSZ, ARGS,	1,	"<sp> decimal-integer" },
120179968Sobrien	{ "CCC",  CCC,  NOARGS,	1,	"(Disable data protection)" },
120279968Sobrien	{ "MIC",  MIC,  STR1,	4,	"<sp> base64data" },
120379968Sobrien	{ "CONF", CONF, STR1,	4,	"<sp> base64data" },
120479968Sobrien	{ "ENC",  ENC,  STR1,	4,	"<sp> base64data" },
120579968Sobrien
120679968Sobrien				/* From RFC 2389, in order defined */
120779968Sobrien	{ "FEAT", FEAT, NOARGS,	1,	"(display extended features)" },
120879968Sobrien	{ "OPTS", OPTS, STR1,	1,	"<sp> command [ <sp> options ]" },
120979968Sobrien
121079968Sobrien				/* from draft-ietf-ftpext-mlst-11 */
121179968Sobrien	{ "MDTM", MDTM, OSTR,	1,	"<sp> path-name" },
121279968Sobrien	{ "SIZE", SIZE, OSTR,	1,	"<sp> path-name" },
121379968Sobrien	{ "MLST", MLST, OSTR,	2,	"[ <sp> path-name ]" },
121479968Sobrien	{ "MLSD", MLSD, OSTR,	1,	"[ <sp> directory-name ]" },
121579968Sobrien
121679968Sobrien				/* obsolete commands */
121779968Sobrien	{ "MAIL", MAIL, OSTR,	0,	"(mail to user)" },
121879968Sobrien	{ "MLFL", MLFL, OSTR,	0,	"(mail file)" },
121979968Sobrien	{ "MRCP", MRCP, STR1,	0,	"(mail recipient)" },
122079968Sobrien	{ "MRSQ", MRSQ, OSTR,	0,	"(mail recipient scheme question)" },
122179968Sobrien	{ "MSAM", MSAM, OSTR,	0,	"(mail send to terminal and mailbox)" },
122279968Sobrien	{ "MSND", MSND, OSTR,	0,	"(mail send to terminal)" },
122379968Sobrien	{ "MSOM", MSOM, OSTR,	0,	"(mail send to terminal or mailbox)" },
122479968Sobrien	{ "XCUP", CDUP, NOARGS,	1,	"(change to parent directory)" },
122579968Sobrien	{ "XCWD", CWD,  OSTR,	1,	"[ <sp> directory-name ]" },
122679968Sobrien	{ "XMKD", MKD,  STR1,	1,	"<sp> path-name" },
122779968Sobrien	{ "XPWD", PWD,  NOARGS,	1,	"(return current directory)" },
122879968Sobrien	{ "XRMD", RMD,  STR1,	1,	"<sp> path-name" },
122979968Sobrien
123079968Sobrien	{  NULL,  0,	0,	0,	0 }
123179968Sobrien};
123279968Sobrien
123379968Sobrienstruct tab sitetab[] = {
123479968Sobrien	{ "CHMOD",   	CHMOD,	NSTR, 1,	"<sp> mode <sp> file-name" },
123579968Sobrien	{ "HELP",    	HELP,	OSTR, 1,	"[ <sp> <string> ]" },
123679968Sobrien	{ "IDLE",    	IDLE,	ARGS, 1,	"[ <sp> maximum-idle-time ]" },
123779968Sobrien	{ "RATEGET", 	RATEGET,OSTR, 1,	"[ <sp> get-throttle-rate ]" },
123879968Sobrien	{ "RATEPUT", 	RATEPUT,OSTR, 1,	"[ <sp> put-throttle-rate ]" },
123979968Sobrien	{ "UMASK",   	UMASK,	ARGS, 1,	"[ <sp> umask ]" },
124079968Sobrien	{ NULL,		0,     0,     0,	NULL }
124179968Sobrien};
124279968Sobrien
124379968Sobrienstatic	int	check_write(const char *, int);
124479968Sobrienstatic	void	help(struct tab *, const char *);
124579968Sobrienstatic	void	port_check(const char *, int);
124679968Sobrienstatic	void	toolong(int);
124779968Sobrienstatic	int	yylex(void);
124879968Sobrien
124979968Sobrienextern int epsvall;
125079968Sobrien
125179968Sobrien/*
125279968Sobrien * Check if a filename is allowed to be modified (isupload == 0) or
125379968Sobrien * uploaded (isupload == 1), and if necessary, check the filename is `sane'.
125479968Sobrien */
125579968Sobrienstatic int
125679968Sobriencheck_write(const char *file, int isupload)
125779968Sobrien{
125879968Sobrien	if (file == NULL)
125979968Sobrien		return (0);
126079968Sobrien	if (! logged_in) {
126179968Sobrien		reply(530, "Please login with USER and PASS.");
126279968Sobrien		return (0);
126379968Sobrien	}
126479968Sobrien		/* checking modify */
126579968Sobrien	if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) {
126679968Sobrien		reply(502, "No permission to use this command.");
126779968Sobrien		return (0);
126879968Sobrien	}
126979968Sobrien		/* checking upload */
127079968Sobrien	if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) {
127179968Sobrien		reply(502, "No permission to use this command.");
127279968Sobrien		return (0);
127379968Sobrien	}
127479968Sobrien		/* checking sanenames */
127579968Sobrien	if (CURCLASS_FLAGS_ISSET(sanenames)) {
127679968Sobrien		const char *p;
127779968Sobrien
127879968Sobrien		if (file[0] == '.')
127979968Sobrien			goto insane_name;
128079968Sobrien		for (p = file; *p; p++) {
128179968Sobrien			if (isalnum(*p) || *p == '-' || *p == '+' ||
128279968Sobrien			    *p == ',' || *p == '.' || *p == '_')
128379968Sobrien				continue;
128479968Sobrien insane_name:
128579968Sobrien			reply(553, "File name `%s' not allowed.", file);
128679968Sobrien			return (0);
128779968Sobrien		}
128879968Sobrien	}
128979968Sobrien	return (1);
129079968Sobrien}
129179968Sobrien
129279968Sobrienstruct tab *
129379968Sobrienlookup(struct tab *p, const char *cmd)
129479968Sobrien{
129579968Sobrien
129679968Sobrien	for (; p->name != NULL; p++)
129779968Sobrien		if (strcasecmp(cmd, p->name) == 0)
129879968Sobrien			return (p);
129979968Sobrien	return (0);
130079968Sobrien}
130179968Sobrien
130279968Sobrien#include <arpa/telnet.h>
130379968Sobrien
130479968Sobrien/*
130579968Sobrien * getline - a hacked up version of fgets to ignore TELNET escape codes.
130679968Sobrien */
130779968Sobrienchar *
130879968Sobriengetline(char *s, int n, FILE *iop)
130979968Sobrien{
131079968Sobrien	int c;
131179968Sobrien	char *cs;
131279968Sobrien
131379968Sobrien	cs = s;
131479968Sobrien/* tmpline may contain saved command from urgent mode interruption */
131579968Sobrien	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
131679968Sobrien		*cs++ = tmpline[c];
131779968Sobrien		if (tmpline[c] == '\n') {
131879968Sobrien			*cs++ = '\0';
131979968Sobrien			if (debug)
132079968Sobrien				syslog(LOG_DEBUG, "command: %s", s);
132179968Sobrien			tmpline[0] = '\0';
132279968Sobrien			return(s);
132379968Sobrien		}
132479968Sobrien		if (c == 0)
132579968Sobrien			tmpline[0] = '\0';
132679968Sobrien	}
132779968Sobrien	while ((c = getc(iop)) != EOF) {
132879968Sobrien		total_bytes++;
132979968Sobrien		total_bytes_in++;
133079968Sobrien		c &= 0377;
133179968Sobrien		if (c == IAC) {
133279968Sobrien		    if ((c = getc(iop)) != EOF) {
133379968Sobrien			total_bytes++;
133479968Sobrien			total_bytes_in++;
133579968Sobrien			c &= 0377;
133679968Sobrien			switch (c) {
133779968Sobrien			case WILL:
133879968Sobrien			case WONT:
133979968Sobrien				c = getc(iop);
134079968Sobrien				total_bytes++;
134179968Sobrien				total_bytes_in++;
134279968Sobrien				cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c);
134379968Sobrien				(void) fflush(stdout);
134479968Sobrien				continue;
134579968Sobrien			case DO:
134679968Sobrien			case DONT:
134779968Sobrien				c = getc(iop);
134879968Sobrien				total_bytes++;
134979968Sobrien				total_bytes_in++;
135079968Sobrien				cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c);
135179968Sobrien				(void) fflush(stdout);
135279968Sobrien				continue;
135379968Sobrien			case IAC:
135479968Sobrien				break;
135579968Sobrien			default:
135679968Sobrien				continue;	/* ignore command */
135779968Sobrien			}
135879968Sobrien		    }
135979968Sobrien		}
136079968Sobrien		*cs++ = c;
136179968Sobrien		if (--n <= 0 || c == '\n')
136279968Sobrien			break;
136379968Sobrien	}
136479968Sobrien	if (c == EOF && cs == s)
136579968Sobrien		return (NULL);
136679968Sobrien	*cs++ = '\0';
136779968Sobrien	if (debug) {
136879968Sobrien		if ((curclass.type != CLASS_GUEST &&
136979968Sobrien		    strncasecmp(s, "PASS ", 5) == 0) ||
137079968Sobrien		    strncasecmp(s, "ACCT ", 5) == 0) {
137179968Sobrien			/* Don't syslog passwords */
137279968Sobrien			syslog(LOG_DEBUG, "command: %.4s ???", s);
137379968Sobrien		} else {
137479968Sobrien			char *cp;
137579968Sobrien			int len;
137679968Sobrien
137779968Sobrien			/* Don't syslog trailing CR-LF */
137879968Sobrien			len = strlen(s);
137979968Sobrien			cp = s + len - 1;
138079968Sobrien			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
138179968Sobrien				--cp;
138279968Sobrien				--len;
138379968Sobrien			}
138479968Sobrien			syslog(LOG_DEBUG, "command: %.*s", len, s);
138579968Sobrien		}
138679968Sobrien	}
138779968Sobrien	return (s);
138879968Sobrien}
138979968Sobrien
139079968Sobrienstatic void
139179968Sobrientoolong(int signo)
139279968Sobrien{
139379968Sobrien
139479968Sobrien	reply(421,
139579968Sobrien	    "Timeout (%d seconds): closing control connection.",
139679968Sobrien	    curclass.timeout);
139779968Sobrien	if (logging)
139879968Sobrien		syslog(LOG_INFO, "User %s timed out after %d seconds",
139979968Sobrien		    (pw ? pw->pw_name : "unknown"), curclass.timeout);
140079968Sobrien	dologout(1);
140179968Sobrien}
140279968Sobrien
140379968Sobrienvoid
140479968Sobrienftp_handle_line(char *cp)
140579968Sobrien{
140679968Sobrien
140779968Sobrien	cmdp = cp;
140879968Sobrien	yyparse();
140979968Sobrien}
141079968Sobrien
141179968Sobrienvoid
141279968Sobrienftp_loop(void)
141379968Sobrien{
141479968Sobrien
141579968Sobrien	while (1) {
141679968Sobrien		(void) signal(SIGALRM, toolong);
141779968Sobrien		(void) alarm(curclass.timeout);
141879968Sobrien		if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
141979968Sobrien			reply(221, "You could at least say goodbye.");
142079968Sobrien			dologout(0);
142179968Sobrien		}
142279968Sobrien		(void) alarm(0);
142379968Sobrien		ftp_handle_line(cbuf);
142479968Sobrien	}
142579968Sobrien	/*NOTREACHED*/
142679968Sobrien}
142779968Sobrien
142879968Sobrienstatic int
142979968Sobrienyylex(void)
143079968Sobrien{
143179968Sobrien	static int cpos, state;
143279968Sobrien	char *cp, *cp2;
143379968Sobrien	struct tab *p;
143479968Sobrien	int n;
143579968Sobrien	char c;
143679968Sobrien
143779968Sobrien	switch (state) {
143879968Sobrien
143979968Sobrien	case CMD:
144079968Sobrien		hasyyerrored = 0;
144179968Sobrien		if ((cp = strchr(cmdp, '\r'))) {
144279968Sobrien			*cp = '\0';
144379968Sobrien#if HAVE_SETPROCTITLE
144479968Sobrien			if (strncasecmp(cmdp, "PASS", 4) != 0 &&
144579968Sobrien			    strncasecmp(cmdp, "ACCT", 4) != 0)
144679968Sobrien				setproctitle("%s: %s", proctitle, cmdp);
144779968Sobrien#endif /* HAVE_SETPROCTITLE */
144879968Sobrien			*cp++ = '\n';
144979968Sobrien			*cp = '\0';
145079968Sobrien		}
145179968Sobrien		if ((cp = strpbrk(cmdp, " \n")))
145279968Sobrien			cpos = cp - cmdp;
145379968Sobrien		if (cpos == 0)
145479968Sobrien			cpos = 4;
145579968Sobrien		c = cmdp[cpos];
145679968Sobrien		cmdp[cpos] = '\0';
145779968Sobrien		p = lookup(cmdtab, cmdp);
145879968Sobrien		cmdp[cpos] = c;
145979968Sobrien		if (p != NULL) {
146079968Sobrien			if (is_oob && ! CMD_OOB(p)) {
146179968Sobrien				/* command will be handled in-band */
146279968Sobrien				return (0);
146379968Sobrien			} else if (! CMD_IMPLEMENTED(p)) {
146479968Sobrien				reply(502, "%s command not implemented.",
146579968Sobrien				    p->name);
146679968Sobrien				hasyyerrored = 1;
146779968Sobrien				break;
146879968Sobrien			}
146979968Sobrien			state = p->state;
147079968Sobrien			yylval.s = p->name;
147179968Sobrien			return (p->token);
147279968Sobrien		}
147379968Sobrien		break;
147479968Sobrien
147579968Sobrien	case SITECMD:
147679968Sobrien		if (cmdp[cpos] == ' ') {
147779968Sobrien			cpos++;
147879968Sobrien			return (SP);
147979968Sobrien		}
148079968Sobrien		cp = &cmdp[cpos];
148179968Sobrien		if ((cp2 = strpbrk(cp, " \n")))
148279968Sobrien			cpos = cp2 - cmdp;
148379968Sobrien		c = cmdp[cpos];
148479968Sobrien		cmdp[cpos] = '\0';
148579968Sobrien		p = lookup(sitetab, cp);
148679968Sobrien		cmdp[cpos] = c;
148779968Sobrien		if (p != NULL) {
148879968Sobrien			if (!CMD_IMPLEMENTED(p)) {
148979968Sobrien				reply(502, "SITE %s command not implemented.",
149079968Sobrien				    p->name);
149179968Sobrien				hasyyerrored = 1;
149279968Sobrien				break;
149379968Sobrien			}
149479968Sobrien			state = p->state;
149579968Sobrien			yylval.s = p->name;
149679968Sobrien			return (p->token);
149779968Sobrien		}
149879968Sobrien		break;
149979968Sobrien
150079968Sobrien	case OSTR:
150179968Sobrien		if (cmdp[cpos] == '\n') {
150279968Sobrien			state = EOLN;
150379968Sobrien			return (CRLF);
150479968Sobrien		}
150579968Sobrien		/* FALLTHROUGH */
150679968Sobrien
150779968Sobrien	case STR1:
150879968Sobrien	case ZSTR1:
150979968Sobrien	dostr1:
151079968Sobrien		if (cmdp[cpos] == ' ') {
151179968Sobrien			cpos++;
151279968Sobrien			state = state == OSTR ? STR2 : state+1;
151379968Sobrien			return (SP);
151479968Sobrien		}
151579968Sobrien		break;
151679968Sobrien
151779968Sobrien	case ZSTR2:
151879968Sobrien		if (cmdp[cpos] == '\n') {
151979968Sobrien			state = EOLN;
152079968Sobrien			return (CRLF);
152179968Sobrien		}
152279968Sobrien		/* FALLTHROUGH */
152379968Sobrien
152479968Sobrien	case STR2:
152579968Sobrien		cp = &cmdp[cpos];
152679968Sobrien		n = strlen(cp);
152779968Sobrien		cpos += n - 1;
152879968Sobrien		/*
152979968Sobrien		 * Make sure the string is nonempty and \n terminated.
153079968Sobrien		 */
153179968Sobrien		if (n > 1 && cmdp[cpos] == '\n') {
153279968Sobrien			cmdp[cpos] = '\0';
153379968Sobrien			yylval.s = xstrdup(cp);
153479968Sobrien			cmdp[cpos] = '\n';
153579968Sobrien			state = ARGS;
153679968Sobrien			return (STRING);
153779968Sobrien		}
153879968Sobrien		break;
153979968Sobrien
154079968Sobrien	case NSTR:
154179968Sobrien		if (cmdp[cpos] == ' ') {
154279968Sobrien			cpos++;
154379968Sobrien			return (SP);
154479968Sobrien		}
154579968Sobrien		if (isdigit(cmdp[cpos])) {
154679968Sobrien			cp = &cmdp[cpos];
154779968Sobrien			while (isdigit(cmdp[++cpos]))
154879968Sobrien				;
154979968Sobrien			c = cmdp[cpos];
155079968Sobrien			cmdp[cpos] = '\0';
155179968Sobrien			yylval.i = atoi(cp);
155279968Sobrien			cmdp[cpos] = c;
155379968Sobrien			state = STR1;
155479968Sobrien			return (NUMBER);
155579968Sobrien		}
155679968Sobrien		state = STR1;
155779968Sobrien		goto dostr1;
155879968Sobrien
155979968Sobrien	case ARGS:
156079968Sobrien		if (isdigit(cmdp[cpos])) {
156179968Sobrien			cp = &cmdp[cpos];
156279968Sobrien			while (isdigit(cmdp[++cpos]))
156379968Sobrien				;
156479968Sobrien			c = cmdp[cpos];
156579968Sobrien			cmdp[cpos] = '\0';
156679968Sobrien			yylval.i = atoi(cp);
156779968Sobrien			cmdp[cpos] = c;
156879968Sobrien			return (NUMBER);
156979968Sobrien		}
157079968Sobrien		if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0
157179968Sobrien		 && !isalnum(cmdp[cpos + 3])) {
157279968Sobrien			yylval.s = xstrdup("ALL");
157379968Sobrien			cpos += 3;
157479968Sobrien			return ALL;
157579968Sobrien		}
157679968Sobrien		switch (cmdp[cpos++]) {
157779968Sobrien
157879968Sobrien		case '\n':
157979968Sobrien			state = EOLN;
158079968Sobrien			return (CRLF);
158179968Sobrien
158279968Sobrien		case ' ':
158379968Sobrien			return (SP);
158479968Sobrien
158579968Sobrien		case ',':
158679968Sobrien			return (COMMA);
158779968Sobrien
158879968Sobrien		case 'A':
158979968Sobrien		case 'a':
159079968Sobrien			return (A);
159179968Sobrien
159279968Sobrien		case 'B':
159379968Sobrien		case 'b':
159479968Sobrien			return (B);
159579968Sobrien
159679968Sobrien		case 'C':
159779968Sobrien		case 'c':
159879968Sobrien			return (C);
159979968Sobrien
160079968Sobrien		case 'E':
160179968Sobrien		case 'e':
160279968Sobrien			return (E);
160379968Sobrien
160479968Sobrien		case 'F':
160579968Sobrien		case 'f':
160679968Sobrien			return (F);
160779968Sobrien
160879968Sobrien		case 'I':
160979968Sobrien		case 'i':
161079968Sobrien			return (I);
161179968Sobrien
161279968Sobrien		case 'L':
161379968Sobrien		case 'l':
161479968Sobrien			return (L);
161579968Sobrien
161679968Sobrien		case 'N':
161779968Sobrien		case 'n':
161879968Sobrien			return (N);
161979968Sobrien
162079968Sobrien		case 'P':
162179968Sobrien		case 'p':
162279968Sobrien			return (P);
162379968Sobrien
162479968Sobrien		case 'R':
162579968Sobrien		case 'r':
162679968Sobrien			return (R);
162779968Sobrien
162879968Sobrien		case 'S':
162979968Sobrien		case 's':
163079968Sobrien			return (S);
163179968Sobrien
163279968Sobrien		case 'T':
163379968Sobrien		case 't':
163479968Sobrien			return (T);
163579968Sobrien
163679968Sobrien		}
163779968Sobrien		break;
163879968Sobrien
163979968Sobrien	case NOARGS:
164079968Sobrien		if (cmdp[cpos] == '\n') {
164179968Sobrien			state = EOLN;
164279968Sobrien			return (CRLF);
164379968Sobrien		}
164479968Sobrien		c = cmdp[cpos];
164579968Sobrien		cmdp[cpos] = '\0';
164679968Sobrien		reply(501, "'%s' command does not take any arguments.", cmdp);
164779968Sobrien		hasyyerrored = 1;
164879968Sobrien		cmdp[cpos] = c;
164979968Sobrien		break;
165079968Sobrien
165179968Sobrien	case EOLN:
165279968Sobrien		state = CMD;
165379968Sobrien		return (0);
165479968Sobrien
165579968Sobrien	default:
165679968Sobrien		fatal("Unknown state in scanner.");
165779968Sobrien	}
165879968Sobrien	yyerror(NULL);
165979968Sobrien	state = CMD;
166079968Sobrien	is_oob = 0;
166179968Sobrien	longjmp(errcatch, 0);
166279968Sobrien	/* NOTREACHED */
166379968Sobrien}
166479968Sobrien
166579968Sobrien/* ARGSUSED */
166679968Sobrienvoid
166779968Sobrienyyerror(char *s)
166879968Sobrien{
166979968Sobrien	char *cp;
167079968Sobrien
167179968Sobrien	if (hasyyerrored || is_oob)
167279968Sobrien		return;
167379968Sobrien	if ((cp = strchr(cmdp,'\n')) != NULL)
167479968Sobrien		*cp = '\0';
167579968Sobrien	reply(500, "'%s': command not understood.", cmdp);
167679968Sobrien	hasyyerrored = 1;
167779968Sobrien}
167879968Sobrien
167979968Sobrienstatic void
168079968Sobrienhelp(struct tab *ctab, const char *s)
168179968Sobrien{
168279968Sobrien	struct tab *c;
168379968Sobrien	int width, NCMDS;
168492282Sobrien	char *htype;
168579968Sobrien
168679968Sobrien	if (ctab == sitetab)
168792282Sobrien		htype = "SITE ";
168879968Sobrien	else
168992282Sobrien		htype = "";
169079968Sobrien	width = 0, NCMDS = 0;
169179968Sobrien	for (c = ctab; c->name != NULL; c++) {
169279968Sobrien		int len = strlen(c->name);
169379968Sobrien
169479968Sobrien		if (len > width)
169579968Sobrien			width = len;
169679968Sobrien		NCMDS++;
169779968Sobrien	}
169879968Sobrien	width = (width + 8) &~ 7;
169979968Sobrien	if (s == 0) {
170079968Sobrien		int i, j, w;
170179968Sobrien		int columns, lines;
170279968Sobrien
170379968Sobrien		reply(-214, "%s", "");
170492282Sobrien		reply(0, "The following %scommands are recognized.", htype);
170579968Sobrien		reply(0, "(`-' = not implemented, `+' = supports options)");
170679968Sobrien		columns = 76 / width;
170779968Sobrien		if (columns == 0)
170879968Sobrien			columns = 1;
170979968Sobrien		lines = (NCMDS + columns - 1) / columns;
171079968Sobrien		for (i = 0; i < lines; i++) {
171179968Sobrien			cprintf(stdout, "    ");
171279968Sobrien			for (j = 0; j < columns; j++) {
171379968Sobrien				c = ctab + j * lines + i;
171479968Sobrien				cprintf(stdout, "%s", c->name);
171579968Sobrien				w = strlen(c->name);
171679968Sobrien				if (! CMD_IMPLEMENTED(c)) {
171779968Sobrien					CPUTC('-', stdout);
171879968Sobrien					w++;
171979968Sobrien				}
172079968Sobrien				if (CMD_HAS_OPTIONS(c)) {
172179968Sobrien					CPUTC('+', stdout);
172279968Sobrien					w++;
172379968Sobrien				}
172479968Sobrien				if (c + lines >= &ctab[NCMDS])
172579968Sobrien					break;
172679968Sobrien				while (w < width) {
172779968Sobrien					CPUTC(' ', stdout);
172879968Sobrien					w++;
172979968Sobrien				}
173079968Sobrien			}
173179968Sobrien			cprintf(stdout, "\r\n");
173279968Sobrien		}
173379968Sobrien		(void) fflush(stdout);
173479968Sobrien		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
173579968Sobrien		return;
173679968Sobrien	}
173779968Sobrien	c = lookup(ctab, s);
173879968Sobrien	if (c == (struct tab *)0) {
173979968Sobrien		reply(502, "Unknown command %s.", s);
174079968Sobrien		return;
174179968Sobrien	}
174279968Sobrien	if (CMD_IMPLEMENTED(c))
174392282Sobrien		reply(214, "Syntax: %s%s %s", htype, c->name, c->help);
174479968Sobrien	else
174592282Sobrien		reply(214, "%s%-*s\t%s; not implemented.", htype, width,
174679968Sobrien		    c->name, c->help);
174779968Sobrien}
174879968Sobrien
174979968Sobrien/*
175079968Sobrien * Check that the structures used for a PORT, LPRT or EPRT command are
175179968Sobrien * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks.
175279968Sobrien * If family != -1 check that his_addr.su_family == family.
175379968Sobrien */
175479968Sobrienstatic void
175579968Sobrienport_check(const char *cmd, int family)
175679968Sobrien{
175779968Sobrien	char h1[NI_MAXHOST], h2[NI_MAXHOST];
175879968Sobrien	char s1[NI_MAXHOST], s2[NI_MAXHOST];
175979968Sobrien#ifdef NI_WITHSCOPEID
176079968Sobrien	const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
176179968Sobrien#else
176279968Sobrien	const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
176379968Sobrien#endif
176479968Sobrien
176579968Sobrien	if (epsvall) {
176679968Sobrien		reply(501, "%s disallowed after EPSV ALL", cmd);
176779968Sobrien		return;
176879968Sobrien	}
176979968Sobrien
177079968Sobrien	if (family != -1 && his_addr.su_family != family) {
177179968Sobrien port_check_fail:
177279968Sobrien		reply(500, "Illegal %s command rejected", cmd);
177379968Sobrien		return;
177479968Sobrien	}
177579968Sobrien
177679968Sobrien	if (data_dest.su_family != his_addr.su_family)
177779968Sobrien		goto port_check_fail;
177879968Sobrien
177979968Sobrien			/* be paranoid, if told so */
178079968Sobrien	if (CURCLASS_FLAGS_ISSET(checkportcmd)) {
178179968Sobrien#ifdef INET6
178279968Sobrien		/*
178379968Sobrien		 * be paranoid, there are getnameinfo implementation that does
178479968Sobrien		 * not present scopeid portion
178579968Sobrien		 */
178679968Sobrien		if (data_dest.su_family == AF_INET6 &&
178779968Sobrien		    data_dest.su_scope_id != his_addr.su_scope_id)
178879968Sobrien			goto port_check_fail;
178979968Sobrien#endif
179079968Sobrien
179179968Sobrien		if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len,
179279968Sobrien		    h1, sizeof(h1), s1, sizeof(s1), niflags))
179379968Sobrien			goto port_check_fail;
179479968Sobrien		if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
179579968Sobrien		    h2, sizeof(h2), s2, sizeof(s2), niflags))
179679968Sobrien			goto port_check_fail;
179779968Sobrien
179879968Sobrien		if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0)
179979968Sobrien			goto port_check_fail;
180079968Sobrien	}
180179968Sobrien
180279968Sobrien	usedefault = 0;
180379968Sobrien	if (pdata >= 0) {
180479968Sobrien		(void) close(pdata);
180579968Sobrien		pdata = -1;
180679968Sobrien	}
180779968Sobrien	reply(200, "%s command successful.", cmd);
180879968Sobrien}
1809