ftpcmd.y revision 109380
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 109380 2003-01-16 13:27:58Z yar $";
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>
6192090Smaxim#include <libutil.h>
6292272Smaxim#include <limits.h>
6392090Smaxim#include <md5.h>
6456668Sshin#include <netdb.h>
651592Srgrimes#include <pwd.h>
661592Srgrimes#include <signal.h>
671592Srgrimes#include <stdio.h>
681592Srgrimes#include <stdlib.h>
691592Srgrimes#include <string.h>
701592Srgrimes#include <syslog.h>
711592Srgrimes#include <time.h>
721592Srgrimes#include <unistd.h>
731592Srgrimes
741592Srgrimes#include "extern.h"
75109380Syar#include "pathnames.h"
761592Srgrimes
7756668Sshinextern	union sockunion data_dest, his_addr;
781592Srgrimesextern	int logged_in;
791592Srgrimesextern	struct passwd *pw;
801592Srgrimesextern	int guest;
8117435Spstextern 	int paranoid;
821592Srgrimesextern	int logging;
831592Srgrimesextern	int type;
841592Srgrimesextern	int form;
8576096Smarkmextern	int ftpdebug;
861592Srgrimesextern	int timeout;
871592Srgrimesextern	int maxtimeout;
881592Srgrimesextern  int pdata;
8927650Sdavidnextern	char *hostname;
9027650Sdavidnextern	char remotehost[];
911592Srgrimesextern	char proctitle[];
921592Srgrimesextern	int usedefault;
931592Srgrimesextern  int transflag;
941592Srgrimesextern  char tmpline[];
9570102Sphkextern	int readonly;
9670102Sphkextern	int noepsv;
9782460Snikextern	int noretr;
9882796Ssheldonhextern	int noguestretr;
99100684Syarextern	char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */
1001592Srgrimes
1011592Srgrimesoff_t	restart_point;
1021592Srgrimes
1031592Srgrimesstatic	int cmd_type;
1041592Srgrimesstatic	int cmd_form;
1051592Srgrimesstatic	int cmd_bytesz;
10689935Syarstatic	int state;
1071592Srgrimeschar	cbuf[512];
10888935Sdwmalonechar	*fromname = (char *) 0;
1091592Srgrimes
11056668Sshinextern int epsvall;
11156668Sshin
1121592Srgrimes%}
1131592Srgrimes
1141592Srgrimes%union {
11592272Smaxim	struct {
11692272Smaxim		off_t	o;
11792272Smaxim		int	i;
11892272Smaxim	} u;
1191592Srgrimes	char   *s;
1201592Srgrimes}
1211592Srgrimes
1221592Srgrimes%token
1231592Srgrimes	A	B	C	E	F	I
1241592Srgrimes	L	N	P	R	S	T
12556668Sshin	ALL
1261592Srgrimes
1271592Srgrimes	SP	CRLF	COMMA
1281592Srgrimes
1291592Srgrimes	USER	PASS	ACCT	REIN	QUIT	PORT
1301592Srgrimes	PASV	TYPE	STRU	MODE	RETR	STOR
1311592Srgrimes	APPE	MLFL	MAIL	MSND	MSOM	MSAM
1321592Srgrimes	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
1331592Srgrimes	ABOR	DELE	CWD	LIST	NLST	SITE
1341592Srgrimes	STAT	HELP	NOOP	MKD	RMD	PWD
1351592Srgrimes	CDUP	STOU	SMNT	SYST	SIZE	MDTM
13656668Sshin	LPRT	LPSV	EPRT	EPSV
1371592Srgrimes
13875535Sphk	UMASK	IDLE	CHMOD	MDFIVE
1391592Srgrimes
140102565Syar	LEXERR	NOTIMPL
1411592Srgrimes
1421592Srgrimes%token	<s> STRING
14392272Smaxim%token	<u> NUMBER
1441592Srgrimes
14592272Smaxim%type	<u.i> check_login octal_number byte_size
14692272Smaxim%type	<u.i> check_login_ro check_login_epsv
14792272Smaxim%type	<u.i> struct_code mode_code type_code form_code
14875567Speter%type	<s> pathstring pathname password username
149102565Syar%type	<s> ALL NOTIMPL
1501592Srgrimes
1511592Srgrimes%start	cmd_list
1521592Srgrimes
1531592Srgrimes%%
1541592Srgrimes
1551592Srgrimescmd_list
1561592Srgrimes	: /* empty */
1571592Srgrimes	| cmd_list cmd
1581592Srgrimes		{
15988935Sdwmalone			if (fromname)
16088935Sdwmalone				free(fromname);
1611592Srgrimes			fromname = (char *) 0;
1621592Srgrimes			restart_point = (off_t) 0;
1631592Srgrimes		}
1641592Srgrimes	| cmd_list rcmd
1651592Srgrimes	;
1661592Srgrimes
1671592Srgrimescmd
1681592Srgrimes	: USER SP username CRLF
1691592Srgrimes		{
1701592Srgrimes			user($3);
1711592Srgrimes			free($3);
1721592Srgrimes		}
1731592Srgrimes	| PASS SP password CRLF
1741592Srgrimes		{
1751592Srgrimes			pass($3);
1761592Srgrimes			free($3);
1771592Srgrimes		}
17875556Sgreen	| PASS CRLF
17975556Sgreen		{
18075556Sgreen			pass("");
18175556Sgreen		}
18217433Spst	| PORT check_login SP host_port CRLF
1831592Srgrimes		{
18456668Sshin			if (epsvall) {
18556668Sshin				reply(501, "no PORT allowed after EPSV ALL");
18656668Sshin				goto port_done;
18756668Sshin			}
18856668Sshin			if (!$2)
18956668Sshin				goto port_done;
19056668Sshin			if (port_check("PORT") == 1)
19156668Sshin				goto port_done;
19256668Sshin#ifdef INET6
19356668Sshin			if ((his_addr.su_family != AF_INET6 ||
19456668Sshin			     !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
19556668Sshin				/* shoud never happen */
19656668Sshin				usedefault = 1;
19756668Sshin				reply(500, "Invalid address rejected.");
19856668Sshin				goto port_done;
19956668Sshin			}
20056668Sshin			port_check_v6("pcmd");
20156668Sshin#endif
20256668Sshin		port_done:
20356668Sshin		}
20456668Sshin	| LPRT check_login SP host_long_port CRLF
20556668Sshin		{
20656668Sshin			if (epsvall) {
20756668Sshin				reply(501, "no LPRT allowed after EPSV ALL");
20856668Sshin				goto lprt_done;
20956668Sshin			}
21056668Sshin			if (!$2)
21156668Sshin				goto lprt_done;
21256668Sshin			if (port_check("LPRT") == 1)
21356668Sshin				goto lprt_done;
21456668Sshin#ifdef INET6
21556668Sshin			if (his_addr.su_family != AF_INET6) {
21656668Sshin				usedefault = 1;
21756668Sshin				reply(500, "Invalid address rejected.");
21856668Sshin				goto lprt_done;
21956668Sshin			}
22056668Sshin			if (port_check_v6("LPRT") == 1)
22156668Sshin				goto lprt_done;
22256668Sshin#endif
22356668Sshin		lprt_done:
22456668Sshin		}
22556668Sshin	| EPRT check_login SP STRING CRLF
22656668Sshin		{
22756668Sshin			char delim;
22856668Sshin			char *tmp = NULL;
22956668Sshin			char *p, *q;
23056668Sshin			char *result[3];
23156668Sshin			struct addrinfo hints;
23256668Sshin			struct addrinfo *res;
23356668Sshin			int i;
23456668Sshin
23556668Sshin			if (epsvall) {
23656668Sshin				reply(501, "no EPRT allowed after EPSV ALL");
23756668Sshin				goto eprt_done;
23856668Sshin			}
23956668Sshin			if (!$2)
24056668Sshin				goto eprt_done;
24156668Sshin
24256668Sshin			memset(&data_dest, 0, sizeof(data_dest));
24356668Sshin			tmp = strdup($4);
24476096Smarkm			if (ftpdebug)
24556668Sshin				syslog(LOG_DEBUG, "%s", tmp);
24656668Sshin			if (!tmp) {
24776096Smarkm				fatalerror("not enough core");
24856668Sshin				/*NOTREACHED*/
24956668Sshin			}
25056668Sshin			p = tmp;
25156668Sshin			delim = p[0];
25256668Sshin			p++;
25356668Sshin			memset(result, 0, sizeof(result));
25456668Sshin			for (i = 0; i < 3; i++) {
25556668Sshin				q = strchr(p, delim);
25656668Sshin				if (!q || *q != delim) {
25756668Sshin		parsefail:
25856668Sshin					reply(500,
25956668Sshin						"Invalid argument, rejected.");
26056668Sshin					if (tmp)
26156668Sshin						free(tmp);
26217433Spst					usedefault = 1;
26356668Sshin					goto eprt_done;
26417433Spst				}
26556668Sshin				*q++ = '\0';
26656668Sshin				result[i] = p;
26776096Smarkm				if (ftpdebug)
26856668Sshin					syslog(LOG_DEBUG, "%d: %s", i, p);
26956668Sshin				p = q;
2701592Srgrimes			}
27156668Sshin
27256668Sshin			/* some more sanity check */
27356668Sshin			p = result[0];
27456668Sshin			while (*p) {
27556668Sshin				if (!isdigit(*p))
27656668Sshin					goto parsefail;
27756668Sshin				p++;
27856668Sshin			}
27956668Sshin			p = result[2];
28056668Sshin			while (*p) {
28156668Sshin				if (!isdigit(*p))
28256668Sshin					goto parsefail;
28356668Sshin				p++;
28456668Sshin			}
28556668Sshin
28656668Sshin			/* grab address */
28756668Sshin			memset(&hints, 0, sizeof(hints));
28856668Sshin			if (atoi(result[0]) == 1)
28956668Sshin				hints.ai_family = PF_INET;
29056668Sshin#ifdef INET6
29156668Sshin			else if (atoi(result[0]) == 2)
29256668Sshin				hints.ai_family = PF_INET6;
29356668Sshin#endif
29456668Sshin			else
29556668Sshin				hints.ai_family = PF_UNSPEC;	/*XXX*/
29656668Sshin			hints.ai_socktype = SOCK_STREAM;
29756668Sshin			i = getaddrinfo(result[1], result[2], &hints, &res);
29856668Sshin			if (i)
29956668Sshin				goto parsefail;
30056668Sshin			memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
30156668Sshin#ifdef INET6
30256668Sshin			if (his_addr.su_family == AF_INET6
30356668Sshin			    && data_dest.su_family == AF_INET6) {
30456668Sshin				/* XXX more sanity checks! */
30556668Sshin				data_dest.su_sin6.sin6_scope_id =
30656668Sshin					his_addr.su_sin6.sin6_scope_id;
30756668Sshin			}
30856668Sshin#endif
30956668Sshin			free(tmp);
31056668Sshin			tmp = NULL;
31156668Sshin
31256668Sshin			if (port_check("EPRT") == 1)
31356668Sshin				goto eprt_done;
31456668Sshin#ifdef INET6
31556668Sshin			if (his_addr.su_family != AF_INET6) {
31656668Sshin				usedefault = 1;
31756668Sshin				reply(500, "Invalid address rejected.");
31856668Sshin				goto eprt_done;
31956668Sshin			}
32056668Sshin			if (port_check_v6("EPRT") == 1)
32156668Sshin				goto eprt_done;
32256668Sshin#endif
32388935Sdwmalone		eprt_done:
32488935Sdwmalone			free($4);
3251592Srgrimes		}
32617433Spst	| PASV check_login CRLF
3271592Srgrimes		{
32856668Sshin			if (epsvall)
32956668Sshin				reply(501, "no PASV allowed after EPSV ALL");
33056668Sshin			else if ($2)
33117433Spst				passive();
3321592Srgrimes		}
33356668Sshin	| LPSV check_login CRLF
33456668Sshin		{
33556668Sshin			if (epsvall)
33656668Sshin				reply(501, "no LPSV allowed after EPSV ALL");
33756668Sshin			else if ($2)
33856668Sshin				long_passive("LPSV", PF_UNSPEC);
33956668Sshin		}
34070102Sphk	| EPSV check_login_epsv SP NUMBER CRLF
34156668Sshin		{
34256668Sshin			if ($2) {
34356668Sshin				int pf;
34492272Smaxim				switch ($4.i) {
34556668Sshin				case 1:
34656668Sshin					pf = PF_INET;
34756668Sshin					break;
34856668Sshin#ifdef INET6
34956668Sshin				case 2:
35056668Sshin					pf = PF_INET6;
35156668Sshin					break;
35256668Sshin#endif
35356668Sshin				default:
35456668Sshin					pf = -1;	/*junk value*/
35556668Sshin					break;
35656668Sshin				}
35756668Sshin				long_passive("EPSV", pf);
35856668Sshin			}
35956668Sshin		}
36070102Sphk	| EPSV check_login_epsv SP ALL CRLF
36156668Sshin		{
36256668Sshin			if ($2) {
36356668Sshin				reply(200,
36456668Sshin				      "EPSV ALL command successful.");
36556668Sshin				epsvall++;
36656668Sshin			}
36756668Sshin		}
36870102Sphk	| EPSV check_login_epsv CRLF
36956668Sshin		{
37056668Sshin			if ($2)
37156668Sshin				long_passive("EPSV", PF_UNSPEC);
37256668Sshin		}
37371278Sjedgar	| TYPE check_login SP type_code CRLF
3741592Srgrimes		{
37571278Sjedgar			if ($2) {
37671278Sjedgar				switch (cmd_type) {
3771592Srgrimes
37871278Sjedgar				case TYPE_A:
37971278Sjedgar					if (cmd_form == FORM_N) {
38071278Sjedgar						reply(200, "Type set to A.");
38171278Sjedgar						type = cmd_type;
38271278Sjedgar						form = cmd_form;
38371278Sjedgar					} else
38471278Sjedgar						reply(504, "Form must be N.");
38571278Sjedgar					break;
3861592Srgrimes
38771278Sjedgar				case TYPE_E:
38871278Sjedgar					reply(504, "Type E not implemented.");
38971278Sjedgar					break;
3901592Srgrimes
39171278Sjedgar				case TYPE_I:
39271278Sjedgar					reply(200, "Type set to I.");
39371278Sjedgar					type = cmd_type;
39471278Sjedgar					break;
3951592Srgrimes
39671278Sjedgar				case TYPE_L:
397103949Smike#if CHAR_BIT == 8
39871278Sjedgar					if (cmd_bytesz == 8) {
39971278Sjedgar						reply(200,
40071278Sjedgar						    "Type set to L (byte size 8).");
40171278Sjedgar						type = cmd_type;
40271278Sjedgar					} else
40371278Sjedgar						reply(504, "Byte size must be 8.");
404103949Smike#else /* CHAR_BIT == 8 */
405103949Smike					UNIMPLEMENTED for CHAR_BIT != 8
406103949Smike#endif /* CHAR_BIT == 8 */
40771278Sjedgar				}
4081592Srgrimes			}
4091592Srgrimes		}
41071278Sjedgar	| STRU check_login SP struct_code CRLF
4111592Srgrimes		{
41271278Sjedgar			if ($2) {
41371278Sjedgar				switch ($4) {
4141592Srgrimes
41571278Sjedgar				case STRU_F:
41671278Sjedgar					reply(200, "STRU F ok.");
41771278Sjedgar					break;
4181592Srgrimes
41971278Sjedgar				default:
42071278Sjedgar					reply(504, "Unimplemented STRU type.");
42171278Sjedgar				}
4221592Srgrimes			}
4231592Srgrimes		}
42471278Sjedgar	| MODE check_login SP mode_code CRLF
4251592Srgrimes		{
42671278Sjedgar			if ($2) {
42771278Sjedgar				switch ($4) {
4281592Srgrimes
42971278Sjedgar				case MODE_S:
43071278Sjedgar					reply(200, "MODE S ok.");
43171278Sjedgar					break;
43271278Sjedgar
43371278Sjedgar				default:
43471278Sjedgar					reply(502, "Unimplemented MODE type.");
43571278Sjedgar				}
4361592Srgrimes			}
4371592Srgrimes		}
43871278Sjedgar	| ALLO check_login SP NUMBER CRLF
4391592Srgrimes		{
44071278Sjedgar			if ($2) {
44171278Sjedgar				reply(202, "ALLO command ignored.");
44271278Sjedgar			}
4431592Srgrimes		}
44471278Sjedgar	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
4451592Srgrimes		{
44671278Sjedgar			if ($2) {
44771278Sjedgar				reply(202, "ALLO command ignored.");
44871278Sjedgar			}
4491592Srgrimes		}
4501592Srgrimes	| RETR check_login SP pathname CRLF
4511592Srgrimes		{
45282796Ssheldonh			if (noretr || (guest && noguestretr))
45382460Snik				reply(500, "RETR command is disabled");
45482460Snik			else if ($2 && $4 != NULL)
4551592Srgrimes				retrieve((char *) 0, $4);
45682460Snik
4571592Srgrimes			if ($4 != NULL)
4581592Srgrimes				free($4);
4591592Srgrimes		}
46070102Sphk	| STOR check_login_ro SP pathname CRLF
4611592Srgrimes		{
4621592Srgrimes			if ($2 && $4 != NULL)
4631592Srgrimes				store($4, "w", 0);
4641592Srgrimes			if ($4 != NULL)
4651592Srgrimes				free($4);
4661592Srgrimes		}
46770102Sphk	| APPE check_login_ro SP pathname CRLF
4681592Srgrimes		{
4691592Srgrimes			if ($2 && $4 != NULL)
4701592Srgrimes				store($4, "a", 0);
4711592Srgrimes			if ($4 != NULL)
4721592Srgrimes				free($4);
4731592Srgrimes		}
4741592Srgrimes	| NLST check_login CRLF
4751592Srgrimes		{
4761592Srgrimes			if ($2)
4771592Srgrimes				send_file_list(".");
4781592Srgrimes		}
479101395Syar	| NLST check_login SP pathstring CRLF
4801592Srgrimes		{
481101395Syar			if ($2)
4821592Srgrimes				send_file_list($4);
483101395Syar			free($4);
4841592Srgrimes		}
4851592Srgrimes	| LIST check_login CRLF
4861592Srgrimes		{
4871592Srgrimes			if ($2)
488109380Syar				retrieve(_PATH_LS " -lgA", "");
4891592Srgrimes		}
49075567Speter	| LIST check_login SP pathstring CRLF
4911592Srgrimes		{
492101395Syar			if ($2)
493109380Syar				retrieve(_PATH_LS " -lgA %s", $4);
494101395Syar			free($4);
4951592Srgrimes		}
4961592Srgrimes	| STAT check_login SP pathname CRLF
4971592Srgrimes		{
4981592Srgrimes			if ($2 && $4 != NULL)
4991592Srgrimes				statfilecmd($4);
5001592Srgrimes			if ($4 != NULL)
5011592Srgrimes				free($4);
5021592Srgrimes		}
50371278Sjedgar	| STAT check_login CRLF
5041592Srgrimes		{
50571278Sjedgar			if ($2) {
50671278Sjedgar				statcmd();
50771278Sjedgar			}
5081592Srgrimes		}
50970102Sphk	| DELE check_login_ro SP pathname CRLF
5101592Srgrimes		{
5111592Srgrimes			if ($2 && $4 != NULL)
5121592Srgrimes				delete($4);
5131592Srgrimes			if ($4 != NULL)
5141592Srgrimes				free($4);
5151592Srgrimes		}
51670102Sphk	| RNTO check_login_ro SP pathname CRLF
5171592Srgrimes		{
518101379Syar			if ($2 && $4 != NULL) {
51917433Spst				if (fromname) {
52017433Spst					renamecmd(fromname, $4);
52117433Spst					free(fromname);
52217433Spst					fromname = (char *) 0;
52317433Spst				} else {
52417433Spst					reply(503, "Bad sequence of commands.");
52517433Spst				}
5261592Srgrimes			}
527101379Syar			if ($4 != NULL)
528101379Syar				free($4);
5291592Srgrimes		}
53071278Sjedgar	| ABOR check_login CRLF
5311592Srgrimes		{
53271278Sjedgar			if ($2)
53371278Sjedgar				reply(225, "ABOR command successful.");
5341592Srgrimes		}
5351592Srgrimes	| CWD check_login CRLF
5361592Srgrimes		{
53769234Sdanny			if ($2) {
53869234Sdanny				if (guest)
53969234Sdanny					cwd("/");
54069234Sdanny				else
54169234Sdanny					cwd(pw->pw_dir);
54269234Sdanny			}
5431592Srgrimes		}
5441592Srgrimes	| CWD check_login SP pathname CRLF
5451592Srgrimes		{
5461592Srgrimes			if ($2 && $4 != NULL)
5471592Srgrimes				cwd($4);
5481592Srgrimes			if ($4 != NULL)
5491592Srgrimes				free($4);
5501592Srgrimes		}
5511592Srgrimes	| HELP CRLF
5521592Srgrimes		{
5531592Srgrimes			help(cmdtab, (char *) 0);
5541592Srgrimes		}
5551592Srgrimes	| HELP SP STRING CRLF
5561592Srgrimes		{
5571592Srgrimes			char *cp = $3;
5581592Srgrimes
5591592Srgrimes			if (strncasecmp(cp, "SITE", 4) == 0) {
5601592Srgrimes				cp = $3 + 4;
5611592Srgrimes				if (*cp == ' ')
5621592Srgrimes					cp++;
5631592Srgrimes				if (*cp)
5641592Srgrimes					help(sitetab, cp);
5651592Srgrimes				else
5661592Srgrimes					help(sitetab, (char *) 0);
5671592Srgrimes			} else
5681592Srgrimes				help(cmdtab, $3);
56988935Sdwmalone			free($3);
5701592Srgrimes		}
5711592Srgrimes	| NOOP CRLF
5721592Srgrimes		{
5731592Srgrimes			reply(200, "NOOP command successful.");
5741592Srgrimes		}
57570102Sphk	| MKD check_login_ro SP pathname CRLF
5761592Srgrimes		{
5771592Srgrimes			if ($2 && $4 != NULL)
5781592Srgrimes				makedir($4);
5791592Srgrimes			if ($4 != NULL)
5801592Srgrimes				free($4);
5811592Srgrimes		}
58270102Sphk	| RMD check_login_ro SP pathname CRLF
5831592Srgrimes		{
5841592Srgrimes			if ($2 && $4 != NULL)
5851592Srgrimes				removedir($4);
5861592Srgrimes			if ($4 != NULL)
5871592Srgrimes				free($4);
5881592Srgrimes		}
5891592Srgrimes	| PWD check_login CRLF
5901592Srgrimes		{
5911592Srgrimes			if ($2)
5921592Srgrimes				pwd();
5931592Srgrimes		}
5941592Srgrimes	| CDUP check_login CRLF
5951592Srgrimes		{
5961592Srgrimes			if ($2)
5971592Srgrimes				cwd("..");
5981592Srgrimes		}
5991592Srgrimes	| SITE SP HELP CRLF
6001592Srgrimes		{
6011592Srgrimes			help(sitetab, (char *) 0);
6021592Srgrimes		}
6031592Srgrimes	| SITE SP HELP SP STRING CRLF
6041592Srgrimes		{
6051592Srgrimes			help(sitetab, $5);
60688935Sdwmalone			free($5);
6071592Srgrimes		}
60875535Sphk	| SITE SP MDFIVE check_login SP pathname CRLF
60975535Sphk		{
61075535Sphk			char p[64], *q;
61175535Sphk
612101379Syar			if ($4 && $6) {
61375535Sphk				q = MD5File($6, p);
61475535Sphk				if (q != NULL)
61575535Sphk					reply(200, "MD5(%s) = %s", $6, p);
61675535Sphk				else
61775535Sphk					perror_reply(550, $6);
61875535Sphk			}
61988935Sdwmalone			if ($6)
62088935Sdwmalone				free($6);
62175535Sphk		}
6221592Srgrimes	| SITE SP UMASK check_login CRLF
6231592Srgrimes		{
6241592Srgrimes			int oldmask;
6251592Srgrimes
6261592Srgrimes			if ($4) {
6271592Srgrimes				oldmask = umask(0);
6281592Srgrimes				(void) umask(oldmask);
6291592Srgrimes				reply(200, "Current UMASK is %03o", oldmask);
6301592Srgrimes			}
6311592Srgrimes		}
6321592Srgrimes	| SITE SP UMASK check_login SP octal_number CRLF
6331592Srgrimes		{
6341592Srgrimes			int oldmask;
6351592Srgrimes
6361592Srgrimes			if ($4) {
6371592Srgrimes				if (($6 == -1) || ($6 > 0777)) {
6381592Srgrimes					reply(501, "Bad UMASK value");
6391592Srgrimes				} else {
6401592Srgrimes					oldmask = umask($6);
6411592Srgrimes					reply(200,
6421592Srgrimes					    "UMASK set to %03o (was %03o)",
6431592Srgrimes					    $6, oldmask);
6441592Srgrimes				}
6451592Srgrimes			}
6461592Srgrimes		}
64770102Sphk	| SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
6481592Srgrimes		{
6491592Srgrimes			if ($4 && ($8 != NULL)) {
650101378Syar				if (($6 == -1 ) || ($6 > 0777))
651101378Syar					reply(501, "Bad mode value");
6521592Srgrimes				else if (chmod($8, $6) < 0)
6531592Srgrimes					perror_reply(550, $8);
6541592Srgrimes				else
6551592Srgrimes					reply(200, "CHMOD command successful.");
6561592Srgrimes			}
6571592Srgrimes			if ($8 != NULL)
6581592Srgrimes				free($8);
6591592Srgrimes		}
66071278Sjedgar	| SITE SP check_login IDLE CRLF
6611592Srgrimes		{
66271278Sjedgar			if ($3)
66371278Sjedgar				reply(200,
66471278Sjedgar			    	    "Current IDLE time limit is %d seconds; max %d",
66571278Sjedgar				    timeout, maxtimeout);
6661592Srgrimes		}
66771278Sjedgar	| SITE SP check_login IDLE SP NUMBER CRLF
6681592Srgrimes		{
66971278Sjedgar			if ($3) {
67092272Smaxim				if ($6.i < 30 || $6.i > maxtimeout) {
67171278Sjedgar					reply(501,
67271278Sjedgar					    "Maximum IDLE time must be between 30 and %d seconds",
67371278Sjedgar					    maxtimeout);
67471278Sjedgar				} else {
67592272Smaxim					timeout = $6.i;
67671278Sjedgar					(void) alarm((unsigned) timeout);
67771278Sjedgar					reply(200,
67871278Sjedgar					    "Maximum IDLE time set to %d seconds",
67971278Sjedgar					    timeout);
68071278Sjedgar				}
6811592Srgrimes			}
6821592Srgrimes		}
68370102Sphk	| STOU check_login_ro SP pathname CRLF
6841592Srgrimes		{
6851592Srgrimes			if ($2 && $4 != NULL)
6861592Srgrimes				store($4, "w", 1);
6871592Srgrimes			if ($4 != NULL)
6881592Srgrimes				free($4);
6891592Srgrimes		}
69071278Sjedgar	| SYST check_login CRLF
6911592Srgrimes		{
69271278Sjedgar			if ($2)
6931592Srgrimes#ifdef unix
6941592Srgrimes#ifdef BSD
6951592Srgrimes			reply(215, "UNIX Type: L%d Version: BSD-%d",
696103949Smike				CHAR_BIT, BSD);
6971592Srgrimes#else /* BSD */
698103949Smike			reply(215, "UNIX Type: L%d", CHAR_BIT);
6991592Srgrimes#endif /* BSD */
7001592Srgrimes#else /* unix */
701103949Smike			reply(215, "UNKNOWN Type: L%d", CHAR_BIT);
7021592Srgrimes#endif /* unix */
7031592Srgrimes		}
7041592Srgrimes
7051592Srgrimes		/*
7061592Srgrimes		 * SIZE is not in RFC959, but Postel has blessed it and
7071592Srgrimes		 * it will be in the updated RFC.
7081592Srgrimes		 *
7091592Srgrimes		 * Return size of file in a format suitable for
7101592Srgrimes		 * using with RESTART (we just count bytes).
7111592Srgrimes		 */
7121592Srgrimes	| SIZE check_login SP pathname CRLF
7131592Srgrimes		{
7141592Srgrimes			if ($2 && $4 != NULL)
7151592Srgrimes				sizecmd($4);
7161592Srgrimes			if ($4 != NULL)
7171592Srgrimes				free($4);
7181592Srgrimes		}
7191592Srgrimes
7201592Srgrimes		/*
7211592Srgrimes		 * MDTM is not in RFC959, but Postel has blessed it and
7221592Srgrimes		 * it will be in the updated RFC.
7231592Srgrimes		 *
7241592Srgrimes		 * Return modification time of file as an ISO 3307
7251592Srgrimes		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
7261592Srgrimes		 * where xxx is the fractional second (of any precision,
7271592Srgrimes		 * not necessarily 3 digits)
7281592Srgrimes		 */
7291592Srgrimes	| MDTM check_login SP pathname CRLF
7301592Srgrimes		{
7311592Srgrimes			if ($2 && $4 != NULL) {
7321592Srgrimes				struct stat stbuf;
7331592Srgrimes				if (stat($4, &stbuf) < 0)
7341592Srgrimes					reply(550, "%s: %s",
7351592Srgrimes					    $4, strerror(errno));
7361592Srgrimes				else if (!S_ISREG(stbuf.st_mode)) {
7371592Srgrimes					reply(550, "%s: not a plain file.", $4);
7381592Srgrimes				} else {
7391592Srgrimes					struct tm *t;
7401592Srgrimes					t = gmtime(&stbuf.st_mtime);
7411592Srgrimes					reply(213,
74217435Spst					    "%04d%02d%02d%02d%02d%02d",
74317435Spst					    1900 + t->tm_year,
74417435Spst					    t->tm_mon+1, t->tm_mday,
7451592Srgrimes					    t->tm_hour, t->tm_min, t->tm_sec);
7461592Srgrimes				}
7471592Srgrimes			}
7481592Srgrimes			if ($4 != NULL)
7491592Srgrimes				free($4);
7501592Srgrimes		}
7511592Srgrimes	| QUIT CRLF
7521592Srgrimes		{
7531592Srgrimes			reply(221, "Goodbye.");
7541592Srgrimes			dologout(0);
7551592Srgrimes		}
756102565Syar	| NOTIMPL
757102565Syar		{
758102565Syar			nack($1);
759102565Syar		}
76089935Syar	| error
7611592Srgrimes		{
76289935Syar			yyclearin;		/* discard lookahead data */
76389935Syar			yyerrok;		/* clear error condition */
764102565Syar			state = CMD;		/* reset lexer state */
7651592Srgrimes		}
7661592Srgrimes	;
7671592Srgrimesrcmd
76870102Sphk	: RNFR check_login_ro SP pathname CRLF
7691592Srgrimes		{
7701592Srgrimes			restart_point = (off_t) 0;
7711592Srgrimes			if ($2 && $4) {
77288935Sdwmalone				if (fromname)
77388935Sdwmalone					free(fromname);
77488935Sdwmalone				fromname = (char *) 0;
77588935Sdwmalone				if (renamefrom($4))
77688935Sdwmalone					fromname = $4;
77788935Sdwmalone				else
7781592Srgrimes					free($4);
77988935Sdwmalone			} else if ($4) {
78088935Sdwmalone				free($4);
7811592Srgrimes			}
7821592Srgrimes		}
78392272Smaxim	| REST check_login SP NUMBER CRLF
7841592Srgrimes		{
78571278Sjedgar			if ($2) {
78688935Sdwmalone				if (fromname)
78788935Sdwmalone					free(fromname);
78871278Sjedgar				fromname = (char *) 0;
78992272Smaxim				restart_point = $4.o;
79092272Smaxim				reply(350, "Restarting at %llu. %s",
79171278Sjedgar				    restart_point,
79271278Sjedgar				    "Send STORE or RETRIEVE to initiate transfer.");
79371278Sjedgar			}
7941592Srgrimes		}
7951592Srgrimes	;
7961592Srgrimes
7971592Srgrimesusername
7981592Srgrimes	: STRING
7991592Srgrimes	;
8001592Srgrimes
8011592Srgrimespassword
8021592Srgrimes	: /* empty */
8031592Srgrimes		{
8041592Srgrimes			$$ = (char *)calloc(1, sizeof(char));
8051592Srgrimes		}
8061592Srgrimes	| STRING
8071592Srgrimes	;
8081592Srgrimes
8091592Srgrimesbyte_size
8101592Srgrimes	: NUMBER
81192272Smaxim		{
81292272Smaxim			$$ = $1.i;
81392272Smaxim		}
8141592Srgrimes	;
8151592Srgrimes
8161592Srgrimeshost_port
8171592Srgrimes	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
8181592Srgrimes		NUMBER COMMA NUMBER
8191592Srgrimes		{
8201592Srgrimes			char *a, *p;
8211592Srgrimes
82256668Sshin			data_dest.su_len = sizeof(struct sockaddr_in);
82356668Sshin			data_dest.su_family = AF_INET;
82456668Sshin			p = (char *)&data_dest.su_sin.sin_port;
82592272Smaxim			p[0] = $9.i; p[1] = $11.i;
82656668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
82792272Smaxim			a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
8281592Srgrimes		}
8291592Srgrimes	;
8301592Srgrimes
83156668Sshinhost_long_port
83256668Sshin	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
83356668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
83456668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
83556668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
83656668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
83756668Sshin		NUMBER
83856668Sshin		{
83956668Sshin			char *a, *p;
84056668Sshin
84156668Sshin			memset(&data_dest, 0, sizeof(data_dest));
84256668Sshin			data_dest.su_len = sizeof(struct sockaddr_in6);
84356668Sshin			data_dest.su_family = AF_INET6;
84456668Sshin			p = (char *)&data_dest.su_port;
84592272Smaxim			p[0] = $39.i; p[1] = $41.i;
84656668Sshin			a = (char *)&data_dest.su_sin6.sin6_addr;
84792272Smaxim			a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
84892272Smaxim			a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
84992272Smaxim			a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
85092272Smaxim			a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
85156668Sshin			if (his_addr.su_family == AF_INET6) {
85256668Sshin				/* XXX more sanity checks! */
85356668Sshin				data_dest.su_sin6.sin6_scope_id =
85456668Sshin					his_addr.su_sin6.sin6_scope_id;
85556668Sshin			}
85692272Smaxim			if ($1.i != 6 || $3.i != 16 || $37.i != 2)
85756668Sshin				memset(&data_dest, 0, sizeof(data_dest));
85856668Sshin		}
85956668Sshin	| NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
86056668Sshin		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
86156668Sshin		NUMBER
86256668Sshin		{
86356668Sshin			char *a, *p;
86456668Sshin
86556668Sshin			memset(&data_dest, 0, sizeof(data_dest));
86656668Sshin			data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
86756668Sshin			data_dest.su_family = AF_INET;
86856668Sshin			p = (char *)&data_dest.su_port;
86992272Smaxim			p[0] = $15.i; p[1] = $17.i;
87056668Sshin			a = (char *)&data_dest.su_sin.sin_addr;
87192272Smaxim			a[0] =  $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
87292272Smaxim			if ($1.i != 4 || $3.i != 4 || $13.i != 2)
87356668Sshin				memset(&data_dest, 0, sizeof(data_dest));
87456668Sshin		}
87556668Sshin	;
87656668Sshin
8771592Srgrimesform_code
8781592Srgrimes	: N
8791592Srgrimes		{
8801592Srgrimes			$$ = FORM_N;
8811592Srgrimes		}
8821592Srgrimes	| T
8831592Srgrimes		{
8841592Srgrimes			$$ = FORM_T;
8851592Srgrimes		}
8861592Srgrimes	| C
8871592Srgrimes		{
8881592Srgrimes			$$ = FORM_C;
8891592Srgrimes		}
8901592Srgrimes	;
8911592Srgrimes
8921592Srgrimestype_code
8931592Srgrimes	: A
8941592Srgrimes		{
8951592Srgrimes			cmd_type = TYPE_A;
8961592Srgrimes			cmd_form = FORM_N;
8971592Srgrimes		}
8981592Srgrimes	| A SP form_code
8991592Srgrimes		{
9001592Srgrimes			cmd_type = TYPE_A;
9011592Srgrimes			cmd_form = $3;
9021592Srgrimes		}
9031592Srgrimes	| E
9041592Srgrimes		{
9051592Srgrimes			cmd_type = TYPE_E;
9061592Srgrimes			cmd_form = FORM_N;
9071592Srgrimes		}
9081592Srgrimes	| E SP form_code
9091592Srgrimes		{
9101592Srgrimes			cmd_type = TYPE_E;
9111592Srgrimes			cmd_form = $3;
9121592Srgrimes		}
9131592Srgrimes	| I
9141592Srgrimes		{
9151592Srgrimes			cmd_type = TYPE_I;
9161592Srgrimes		}
9171592Srgrimes	| L
9181592Srgrimes		{
9191592Srgrimes			cmd_type = TYPE_L;
920103949Smike			cmd_bytesz = CHAR_BIT;
9211592Srgrimes		}
9221592Srgrimes	| L SP byte_size
9231592Srgrimes		{
9241592Srgrimes			cmd_type = TYPE_L;
9251592Srgrimes			cmd_bytesz = $3;
9261592Srgrimes		}
9271592Srgrimes		/* this is for a bug in the BBN ftp */
9281592Srgrimes	| L byte_size
9291592Srgrimes		{
9301592Srgrimes			cmd_type = TYPE_L;
9311592Srgrimes			cmd_bytesz = $2;
9321592Srgrimes		}
9331592Srgrimes	;
9341592Srgrimes
9351592Srgrimesstruct_code
9361592Srgrimes	: F
9371592Srgrimes		{
9381592Srgrimes			$$ = STRU_F;
9391592Srgrimes		}
9401592Srgrimes	| R
9411592Srgrimes		{
9421592Srgrimes			$$ = STRU_R;
9431592Srgrimes		}
9441592Srgrimes	| P
9451592Srgrimes		{
9461592Srgrimes			$$ = STRU_P;
9471592Srgrimes		}
9481592Srgrimes	;
9491592Srgrimes
9501592Srgrimesmode_code
9511592Srgrimes	: S
9521592Srgrimes		{
9531592Srgrimes			$$ = MODE_S;
9541592Srgrimes		}
9551592Srgrimes	| B
9561592Srgrimes		{
9571592Srgrimes			$$ = MODE_B;
9581592Srgrimes		}
9591592Srgrimes	| C
9601592Srgrimes		{
9611592Srgrimes			$$ = MODE_C;
9621592Srgrimes		}
9631592Srgrimes	;
9641592Srgrimes
9651592Srgrimespathname
9661592Srgrimes	: pathstring
9671592Srgrimes		{
9681592Srgrimes			/*
9691592Srgrimes			 * Problem: this production is used for all pathname
9701592Srgrimes			 * processing, but only gives a 550 error reply.
9711592Srgrimes			 * This is a valid reply in some cases but not in others.
9721592Srgrimes			 */
97375567Speter			if (logged_in && $1) {
9741592Srgrimes				glob_t gl;
9751592Srgrimes				int flags =
976100222Smikeh				 GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
9771592Srgrimes
9781592Srgrimes				memset(&gl, 0, sizeof(gl));
97975560Sjedgar				flags |= GLOB_MAXPATH;
98075560Sjedgar				gl.gl_matchc = MAXGLOBARGS;
9811592Srgrimes				if (glob($1, flags, NULL, &gl) ||
9821592Srgrimes				    gl.gl_pathc == 0) {
983101380Syar					reply(550, "wildcard expansion error");
9841592Srgrimes					$$ = NULL;
98575567Speter				} else if (gl.gl_pathc > 1) {
98675567Speter					reply(550, "ambiguous");
98775567Speter					$$ = NULL;
9881592Srgrimes				} else {
9891592Srgrimes					$$ = strdup(gl.gl_pathv[0]);
9901592Srgrimes				}
9911592Srgrimes				globfree(&gl);
9921592Srgrimes				free($1);
9931592Srgrimes			} else
9941592Srgrimes				$$ = $1;
9951592Srgrimes		}
9961592Srgrimes	;
9971592Srgrimes
9981592Srgrimespathstring
9991592Srgrimes	: STRING
10001592Srgrimes	;
10011592Srgrimes
10021592Srgrimesoctal_number
10031592Srgrimes	: NUMBER
10041592Srgrimes		{
10051592Srgrimes			int ret, dec, multby, digit;
10061592Srgrimes
10071592Srgrimes			/*
10081592Srgrimes			 * Convert a number that was read as decimal number
10091592Srgrimes			 * to what it would be if it had been read as octal.
10101592Srgrimes			 */
101192272Smaxim			dec = $1.i;
10121592Srgrimes			multby = 1;
10131592Srgrimes			ret = 0;
10141592Srgrimes			while (dec) {
10151592Srgrimes				digit = dec%10;
10161592Srgrimes				if (digit > 7) {
10171592Srgrimes					ret = -1;
10181592Srgrimes					break;
10191592Srgrimes				}
10201592Srgrimes				ret += digit * multby;
10211592Srgrimes				multby *= 8;
10221592Srgrimes				dec /= 10;
10231592Srgrimes			}
10241592Srgrimes			$$ = ret;
10251592Srgrimes		}
10261592Srgrimes	;
10271592Srgrimes
10281592Srgrimes
10291592Srgrimescheck_login
10301592Srgrimes	: /* empty */
10311592Srgrimes		{
103270102Sphk		$$ = check_login1();
10331592Srgrimes		}
10341592Srgrimes	;
10351592Srgrimes
103670102Sphkcheck_login_epsv
103770102Sphk	: /* empty */
103870102Sphk		{
103970102Sphk		if (noepsv) {
104070102Sphk			reply(500, "EPSV command disabled");
104170102Sphk			$$ = 0;
104270102Sphk		}
104370102Sphk		else
104470102Sphk			$$ = check_login1();
104570102Sphk		}
104670102Sphk	;
104770102Sphk
104870102Sphkcheck_login_ro
104970102Sphk	: /* empty */
105070102Sphk		{
105170102Sphk		if (readonly) {
105272710Sdes			reply(550, "Permission denied.");
105370102Sphk			$$ = 0;
105470102Sphk		}
105570102Sphk		else
105670102Sphk			$$ = check_login1();
105770102Sphk		}
105870102Sphk	;
105970102Sphk
10601592Srgrimes%%
10611592Srgrimes
10621592Srgrimes#define	CMD	0	/* beginning of command */
10631592Srgrimes#define	ARGS	1	/* expect miscellaneous arguments */
10641592Srgrimes#define	STR1	2	/* expect SP followed by STRING */
10651592Srgrimes#define	STR2	3	/* expect STRING */
10661592Srgrimes#define	OSTR	4	/* optional SP then STRING */
106775556Sgreen#define	ZSTR1	5	/* optional SP then optional STRING */
10681592Srgrimes#define	ZSTR2	6	/* optional STRING after SP */
10691592Srgrimes#define	SITECMD	7	/* SITE command */
10701592Srgrimes#define	NSTR	8	/* Number followed by a string */
10711592Srgrimes
107275560Sjedgar#define	MAXGLOBARGS	1000
107375560Sjedgar
1074101034Syar#define	MAXASIZE	10240	/* Deny ASCII SIZE on files larger than that */
1075101034Syar
10761592Srgrimesstruct tab {
10771592Srgrimes	char	*name;
10781592Srgrimes	short	token;
10791592Srgrimes	short	state;
10801592Srgrimes	short	implemented;	/* 1 if command is implemented */
10811592Srgrimes	char	*help;
10821592Srgrimes};
10831592Srgrimes
10841592Srgrimesstruct tab cmdtab[] = {		/* In order defined in RFC 765 */
10851592Srgrimes	{ "USER", USER, STR1, 1,	"<sp> username" },
108675556Sgreen	{ "PASS", PASS, ZSTR1, 1,	"[<sp> [password]]" },
10871592Srgrimes	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
10881592Srgrimes	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
10891592Srgrimes	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
10901592Srgrimes	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
1091101806Syar	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4, b5" },
109256668Sshin	{ "LPRT", LPRT, ARGS, 1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
109356668Sshin	{ "EPRT", EPRT, STR1, 1,	"<sp> |af|addr|port|" },
10941592Srgrimes	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
109556668Sshin	{ "LPSV", LPSV, ARGS, 1,	"(set server in passive mode)" },
109656668Sshin	{ "EPSV", EPSV, ARGS, 1,	"[<sp> af|ALL]" },
1097101806Syar	{ "TYPE", TYPE, ARGS, 1,	"<sp> { A | E | I | L }" },
10981592Srgrimes	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
10991592Srgrimes	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
11001592Srgrimes	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
11011592Srgrimes	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
11021592Srgrimes	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
11031592Srgrimes	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
11041592Srgrimes	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
11051592Srgrimes	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
11061592Srgrimes	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
11071592Srgrimes	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
11081592Srgrimes	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
11091592Srgrimes	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
11101592Srgrimes	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
11111592Srgrimes	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
11121592Srgrimes	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
11131592Srgrimes	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
11141592Srgrimes	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
11151592Srgrimes	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
11161592Srgrimes	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
11171592Srgrimes	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
11181592Srgrimes	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
11191592Srgrimes	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
11201592Srgrimes	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
11211592Srgrimes	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
11221592Srgrimes	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
11231592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
11241592Srgrimes	{ "NOOP", NOOP, ARGS, 1,	"" },
11251592Srgrimes	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
11261592Srgrimes	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
11271592Srgrimes	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
11281592Srgrimes	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
11291592Srgrimes	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
11301592Srgrimes	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
11311592Srgrimes	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
11321592Srgrimes	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
11331592Srgrimes	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
11341592Srgrimes	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
11351592Srgrimes	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
11361592Srgrimes	{ NULL,   0,    0,    0,	0 }
11371592Srgrimes};
11381592Srgrimes
11391592Srgrimesstruct tab sitetab[] = {
114075535Sphk	{ "MD5", MDFIVE, STR1, 1,	"[ <sp> file-name ]" },
11411592Srgrimes	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
11421592Srgrimes	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
11431592Srgrimes	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
11441592Srgrimes	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
11451592Srgrimes	{ NULL,   0,    0,    0,	0 }
11461592Srgrimes};
11471592Srgrimes
114890148Simpstatic char	*copy(char *);
114990148Simpstatic void	 help(struct tab *, char *);
11501592Srgrimesstatic struct tab *
115190148Simp		 lookup(struct tab *, char *);
115290148Simpstatic int	 port_check(const char *);
115390148Simpstatic int	 port_check_v6(const char *);
115490148Simpstatic void	 sizecmd(char *);
115590148Simpstatic void	 toolong(int);
115690148Simpstatic void	 v4map_data_dest(void);
115790148Simpstatic int	 yylex(void);
11581592Srgrimes
11591592Srgrimesstatic struct tab *
116090148Simplookup(struct tab *p, char *cmd)
11611592Srgrimes{
11621592Srgrimes
11631592Srgrimes	for (; p->name != NULL; p++)
11641592Srgrimes		if (strcmp(cmd, p->name) == 0)
11651592Srgrimes			return (p);
11661592Srgrimes	return (0);
11671592Srgrimes}
11681592Srgrimes
11691592Srgrimes#include <arpa/telnet.h>
11701592Srgrimes
11711592Srgrimes/*
11721592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes.
11731592Srgrimes */
11741592Srgrimeschar *
117590148Simpgetline(char *s, int n, FILE *iop)
11761592Srgrimes{
11771592Srgrimes	int c;
11781592Srgrimes	register char *cs;
11791592Srgrimes
11801592Srgrimes	cs = s;
11811592Srgrimes/* tmpline may contain saved command from urgent mode interruption */
11821592Srgrimes	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
11831592Srgrimes		*cs++ = tmpline[c];
11841592Srgrimes		if (tmpline[c] == '\n') {
11851592Srgrimes			*cs++ = '\0';
118676096Smarkm			if (ftpdebug)
11871592Srgrimes				syslog(LOG_DEBUG, "command: %s", s);
11881592Srgrimes			tmpline[0] = '\0';
11891592Srgrimes			return(s);
11901592Srgrimes		}
11911592Srgrimes		if (c == 0)
11921592Srgrimes			tmpline[0] = '\0';
11931592Srgrimes	}
11941592Srgrimes	while ((c = getc(iop)) != EOF) {
11951592Srgrimes		c &= 0377;
11961592Srgrimes		if (c == IAC) {
11971592Srgrimes		    if ((c = getc(iop)) != EOF) {
11981592Srgrimes			c &= 0377;
11991592Srgrimes			switch (c) {
12001592Srgrimes			case WILL:
12011592Srgrimes			case WONT:
12021592Srgrimes				c = getc(iop);
12031592Srgrimes				printf("%c%c%c", IAC, DONT, 0377&c);
12041592Srgrimes				(void) fflush(stdout);
12051592Srgrimes				continue;
12061592Srgrimes			case DO:
12071592Srgrimes			case DONT:
12081592Srgrimes				c = getc(iop);
12091592Srgrimes				printf("%c%c%c", IAC, WONT, 0377&c);
12101592Srgrimes				(void) fflush(stdout);
12111592Srgrimes				continue;
12121592Srgrimes			case IAC:
12131592Srgrimes				break;
12141592Srgrimes			default:
12151592Srgrimes				continue;	/* ignore command */
12161592Srgrimes			}
12171592Srgrimes		    }
12181592Srgrimes		}
12191592Srgrimes		*cs++ = c;
12201592Srgrimes		if (--n <= 0 || c == '\n')
12211592Srgrimes			break;
12221592Srgrimes	}
12231592Srgrimes	if (c == EOF && cs == s)
12241592Srgrimes		return (NULL);
12251592Srgrimes	*cs++ = '\0';
122676096Smarkm	if (ftpdebug) {
12271592Srgrimes		if (!guest && strncasecmp("pass ", s, 5) == 0) {
12281592Srgrimes			/* Don't syslog passwords */
12291592Srgrimes			syslog(LOG_DEBUG, "command: %.5s ???", s);
12301592Srgrimes		} else {
12311592Srgrimes			register char *cp;
12321592Srgrimes			register int len;
12331592Srgrimes
12341592Srgrimes			/* Don't syslog trailing CR-LF */
12351592Srgrimes			len = strlen(s);
12361592Srgrimes			cp = s + len - 1;
12371592Srgrimes			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
12381592Srgrimes				--cp;
12391592Srgrimes				--len;
12401592Srgrimes			}
12411592Srgrimes			syslog(LOG_DEBUG, "command: %.*s", len, s);
12421592Srgrimes		}
12431592Srgrimes	}
12441592Srgrimes	return (s);
12451592Srgrimes}
12461592Srgrimes
12471592Srgrimesstatic void
124890148Simptoolong(int signo)
12491592Srgrimes{
12501592Srgrimes
12511592Srgrimes	reply(421,
12521592Srgrimes	    "Timeout (%d seconds): closing control connection.", timeout);
12531592Srgrimes	if (logging)
12541592Srgrimes		syslog(LOG_INFO, "User %s timed out after %d seconds",
12551592Srgrimes		    (pw ? pw -> pw_name : "unknown"), timeout);
12561592Srgrimes	dologout(1);
12571592Srgrimes}
12581592Srgrimes
12591592Srgrimesstatic int
126090148Simpyylex(void)
12611592Srgrimes{
126289935Syar	static int cpos;
12631592Srgrimes	char *cp, *cp2;
12641592Srgrimes	struct tab *p;
12651592Srgrimes	int n;
12661592Srgrimes	char c;
12671592Srgrimes
12681592Srgrimes	for (;;) {
12691592Srgrimes		switch (state) {
12701592Srgrimes
12711592Srgrimes		case CMD:
12721592Srgrimes			(void) signal(SIGALRM, toolong);
12731592Srgrimes			(void) alarm((unsigned) timeout);
12741592Srgrimes			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
12751592Srgrimes				reply(221, "You could at least say goodbye.");
12761592Srgrimes				dologout(0);
12771592Srgrimes			}
12781592Srgrimes			(void) alarm(0);
12791592Srgrimes#ifdef SETPROCTITLE
128029574Sphk			if (strncasecmp(cbuf, "PASS", 4) != 0)
12811592Srgrimes				setproctitle("%s: %s", proctitle, cbuf);
12821592Srgrimes#endif /* SETPROCTITLE */
12831592Srgrimes			if ((cp = strchr(cbuf, '\r'))) {
12841592Srgrimes				*cp++ = '\n';
12851592Srgrimes				*cp = '\0';
12861592Srgrimes			}
12871592Srgrimes			if ((cp = strpbrk(cbuf, " \n")))
12881592Srgrimes				cpos = cp - cbuf;
12891592Srgrimes			if (cpos == 0)
12901592Srgrimes				cpos = 4;
12911592Srgrimes			c = cbuf[cpos];
12921592Srgrimes			cbuf[cpos] = '\0';
12931592Srgrimes			upper(cbuf);
12941592Srgrimes			p = lookup(cmdtab, cbuf);
12951592Srgrimes			cbuf[cpos] = c;
12963776Spst			if (p != 0) {
1297102565Syar				yylval.s = p->name;
1298102565Syar				if (!p->implemented)
1299102565Syar					return (NOTIMPL); /* state remains CMD */
13001592Srgrimes				state = p->state;
13011592Srgrimes				return (p->token);
13021592Srgrimes			}
13031592Srgrimes			break;
13041592Srgrimes
13051592Srgrimes		case SITECMD:
13061592Srgrimes			if (cbuf[cpos] == ' ') {
13071592Srgrimes				cpos++;
13081592Srgrimes				return (SP);
13091592Srgrimes			}
13101592Srgrimes			cp = &cbuf[cpos];
13111592Srgrimes			if ((cp2 = strpbrk(cp, " \n")))
13121592Srgrimes				cpos = cp2 - cbuf;
13131592Srgrimes			c = cbuf[cpos];
13141592Srgrimes			cbuf[cpos] = '\0';
13151592Srgrimes			upper(cp);
13161592Srgrimes			p = lookup(sitetab, cp);
13171592Srgrimes			cbuf[cpos] = c;
13183777Spst			if (guest == 0 && p != 0) {
1319102565Syar				yylval.s = p->name;
1320102565Syar				if (!p->implemented) {
13211592Srgrimes					state = CMD;
1322102565Syar					return (NOTIMPL);
13231592Srgrimes				}
13241592Srgrimes				state = p->state;
13251592Srgrimes				return (p->token);
13261592Srgrimes			}
13271592Srgrimes			state = CMD;
13281592Srgrimes			break;
13291592Srgrimes
133075556Sgreen		case ZSTR1:
13311592Srgrimes		case OSTR:
13321592Srgrimes			if (cbuf[cpos] == '\n') {
13331592Srgrimes				state = CMD;
13341592Srgrimes				return (CRLF);
13351592Srgrimes			}
13361592Srgrimes			/* FALLTHROUGH */
13371592Srgrimes
13381592Srgrimes		case STR1:
13391592Srgrimes		dostr1:
13401592Srgrimes			if (cbuf[cpos] == ' ') {
13411592Srgrimes				cpos++;
134251979Salfred				state = state == OSTR ? STR2 : state+1;
13431592Srgrimes				return (SP);
13441592Srgrimes			}
13451592Srgrimes			break;
13461592Srgrimes
13471592Srgrimes		case ZSTR2:
13481592Srgrimes			if (cbuf[cpos] == '\n') {
13491592Srgrimes				state = CMD;
13501592Srgrimes				return (CRLF);
13511592Srgrimes			}
13521592Srgrimes			/* FALLTHROUGH */
13531592Srgrimes
13541592Srgrimes		case STR2:
13551592Srgrimes			cp = &cbuf[cpos];
13561592Srgrimes			n = strlen(cp);
13571592Srgrimes			cpos += n - 1;
13581592Srgrimes			/*
13591592Srgrimes			 * Make sure the string is nonempty and \n terminated.
13601592Srgrimes			 */
13611592Srgrimes			if (n > 1 && cbuf[cpos] == '\n') {
13621592Srgrimes				cbuf[cpos] = '\0';
13631592Srgrimes				yylval.s = copy(cp);
13641592Srgrimes				cbuf[cpos] = '\n';
13651592Srgrimes				state = ARGS;
13661592Srgrimes				return (STRING);
13671592Srgrimes			}
13681592Srgrimes			break;
13691592Srgrimes
13701592Srgrimes		case NSTR:
13711592Srgrimes			if (cbuf[cpos] == ' ') {
13721592Srgrimes				cpos++;
13731592Srgrimes				return (SP);
13741592Srgrimes			}
13751592Srgrimes			if (isdigit(cbuf[cpos])) {
13761592Srgrimes				cp = &cbuf[cpos];
13771592Srgrimes				while (isdigit(cbuf[++cpos]))
13781592Srgrimes					;
13791592Srgrimes				c = cbuf[cpos];
13801592Srgrimes				cbuf[cpos] = '\0';
138192272Smaxim				yylval.u.i = atoi(cp);
13821592Srgrimes				cbuf[cpos] = c;
13831592Srgrimes				state = STR1;
13841592Srgrimes				return (NUMBER);
13851592Srgrimes			}
13861592Srgrimes			state = STR1;
13871592Srgrimes			goto dostr1;
13881592Srgrimes
13891592Srgrimes		case ARGS:
13901592Srgrimes			if (isdigit(cbuf[cpos])) {
13911592Srgrimes				cp = &cbuf[cpos];
13921592Srgrimes				while (isdigit(cbuf[++cpos]))
13931592Srgrimes					;
13941592Srgrimes				c = cbuf[cpos];
13951592Srgrimes				cbuf[cpos] = '\0';
139692272Smaxim				yylval.u.i = atoi(cp);
139792272Smaxim				yylval.u.o = strtoull(cp, (char **)NULL, 10);
13981592Srgrimes				cbuf[cpos] = c;
13991592Srgrimes				return (NUMBER);
14001592Srgrimes			}
140156668Sshin			if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
140256668Sshin			 && !isalnum(cbuf[cpos + 3])) {
140356668Sshin				cpos += 3;
140456668Sshin				return ALL;
140556668Sshin			}
14061592Srgrimes			switch (cbuf[cpos++]) {
14071592Srgrimes
14081592Srgrimes			case '\n':
14091592Srgrimes				state = CMD;
14101592Srgrimes				return (CRLF);
14111592Srgrimes
14121592Srgrimes			case ' ':
14131592Srgrimes				return (SP);
14141592Srgrimes
14151592Srgrimes			case ',':
14161592Srgrimes				return (COMMA);
14171592Srgrimes
14181592Srgrimes			case 'A':
14191592Srgrimes			case 'a':
14201592Srgrimes				return (A);
14211592Srgrimes
14221592Srgrimes			case 'B':
14231592Srgrimes			case 'b':
14241592Srgrimes				return (B);
14251592Srgrimes
14261592Srgrimes			case 'C':
14271592Srgrimes			case 'c':
14281592Srgrimes				return (C);
14291592Srgrimes
14301592Srgrimes			case 'E':
14311592Srgrimes			case 'e':
14321592Srgrimes				return (E);
14331592Srgrimes
14341592Srgrimes			case 'F':
14351592Srgrimes			case 'f':
14361592Srgrimes				return (F);
14371592Srgrimes
14381592Srgrimes			case 'I':
14391592Srgrimes			case 'i':
14401592Srgrimes				return (I);
14411592Srgrimes
14421592Srgrimes			case 'L':
14431592Srgrimes			case 'l':
14441592Srgrimes				return (L);
14451592Srgrimes
14461592Srgrimes			case 'N':
14471592Srgrimes			case 'n':
14481592Srgrimes				return (N);
14491592Srgrimes
14501592Srgrimes			case 'P':
14511592Srgrimes			case 'p':
14521592Srgrimes				return (P);
14531592Srgrimes
14541592Srgrimes			case 'R':
14551592Srgrimes			case 'r':
14561592Srgrimes				return (R);
14571592Srgrimes
14581592Srgrimes			case 'S':
14591592Srgrimes			case 's':
14601592Srgrimes				return (S);
14611592Srgrimes
14621592Srgrimes			case 'T':
14631592Srgrimes			case 't':
14641592Srgrimes				return (T);
14651592Srgrimes
14661592Srgrimes			}
14671592Srgrimes			break;
14681592Srgrimes
14691592Srgrimes		default:
147076096Smarkm			fatalerror("Unknown state in scanner.");
14711592Srgrimes		}
14721592Srgrimes		state = CMD;
147389935Syar		return (LEXERR);
14741592Srgrimes	}
14751592Srgrimes}
14761592Srgrimes
14771592Srgrimesvoid
147890148Simpupper(char *s)
14791592Srgrimes{
14801592Srgrimes	while (*s != '\0') {
14811592Srgrimes		if (islower(*s))
14821592Srgrimes			*s = toupper(*s);
14831592Srgrimes		s++;
14841592Srgrimes	}
14851592Srgrimes}
14861592Srgrimes
14871592Srgrimesstatic char *
148890148Simpcopy(char *s)
14891592Srgrimes{
14901592Srgrimes	char *p;
14911592Srgrimes
14921592Srgrimes	p = malloc((unsigned) strlen(s) + 1);
14931592Srgrimes	if (p == NULL)
149476096Smarkm		fatalerror("Ran out of memory.");
14951592Srgrimes	(void) strcpy(p, s);
14961592Srgrimes	return (p);
14971592Srgrimes}
14981592Srgrimes
14991592Srgrimesstatic void
150090148Simphelp(struct tab *ctab, char *s)
15011592Srgrimes{
15021592Srgrimes	struct tab *c;
15031592Srgrimes	int width, NCMDS;
15041592Srgrimes	char *type;
15051592Srgrimes
15061592Srgrimes	if (ctab == sitetab)
15071592Srgrimes		type = "SITE ";
15081592Srgrimes	else
15091592Srgrimes		type = "";
15101592Srgrimes	width = 0, NCMDS = 0;
15111592Srgrimes	for (c = ctab; c->name != NULL; c++) {
15121592Srgrimes		int len = strlen(c->name);
15131592Srgrimes
15141592Srgrimes		if (len > width)
15151592Srgrimes			width = len;
15161592Srgrimes		NCMDS++;
15171592Srgrimes	}
15181592Srgrimes	width = (width + 8) &~ 7;
15191592Srgrimes	if (s == 0) {
15201592Srgrimes		int i, j, w;
15211592Srgrimes		int columns, lines;
15221592Srgrimes
15231592Srgrimes		lreply(214, "The following %scommands are recognized %s.",
15241592Srgrimes		    type, "(* =>'s unimplemented)");
15251592Srgrimes		columns = 76 / width;
15261592Srgrimes		if (columns == 0)
15271592Srgrimes			columns = 1;
15281592Srgrimes		lines = (NCMDS + columns - 1) / columns;
15291592Srgrimes		for (i = 0; i < lines; i++) {
15301592Srgrimes			printf("   ");
15311592Srgrimes			for (j = 0; j < columns; j++) {
15321592Srgrimes				c = ctab + j * lines + i;
15331592Srgrimes				printf("%s%c", c->name,
15341592Srgrimes					c->implemented ? ' ' : '*');
15351592Srgrimes				if (c + lines >= &ctab[NCMDS])
15361592Srgrimes					break;
15371592Srgrimes				w = strlen(c->name) + 1;
15381592Srgrimes				while (w < width) {
15391592Srgrimes					putchar(' ');
15401592Srgrimes					w++;
15411592Srgrimes				}
15421592Srgrimes			}
15431592Srgrimes			printf("\r\n");
15441592Srgrimes		}
15451592Srgrimes		(void) fflush(stdout);
15461592Srgrimes		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
15471592Srgrimes		return;
15481592Srgrimes	}
15491592Srgrimes	upper(s);
15501592Srgrimes	c = lookup(ctab, s);
15511592Srgrimes	if (c == (struct tab *)0) {
15521592Srgrimes		reply(502, "Unknown command %s.", s);
15531592Srgrimes		return;
15541592Srgrimes	}
15551592Srgrimes	if (c->implemented)
15561592Srgrimes		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
15571592Srgrimes	else
15581592Srgrimes		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
15591592Srgrimes		    c->name, c->help);
15601592Srgrimes}
15611592Srgrimes
15621592Srgrimesstatic void
156390148Simpsizecmd(char *filename)
15641592Srgrimes{
15651592Srgrimes	switch (type) {
15661592Srgrimes	case TYPE_L:
15671592Srgrimes	case TYPE_I: {
15681592Srgrimes		struct stat stbuf;
156963350Sdes		if (stat(filename, &stbuf) < 0)
157063350Sdes			perror_reply(550, filename);
157163350Sdes		else if (!S_ISREG(stbuf.st_mode))
15721592Srgrimes			reply(550, "%s: not a plain file.", filename);
15731592Srgrimes		else
15741592Srgrimes			reply(213, "%qu", stbuf.st_size);
15751592Srgrimes		break; }
15761592Srgrimes	case TYPE_A: {
15771592Srgrimes		FILE *fin;
15781592Srgrimes		int c;
15791592Srgrimes		off_t count;
15801592Srgrimes		struct stat stbuf;
15811592Srgrimes		fin = fopen(filename, "r");
15821592Srgrimes		if (fin == NULL) {
15831592Srgrimes			perror_reply(550, filename);
15841592Srgrimes			return;
15851592Srgrimes		}
158663350Sdes		if (fstat(fileno(fin), &stbuf) < 0) {
158763350Sdes			perror_reply(550, filename);
158863350Sdes			(void) fclose(fin);
158963350Sdes			return;
159063350Sdes		} else if (!S_ISREG(stbuf.st_mode)) {
15911592Srgrimes			reply(550, "%s: not a plain file.", filename);
15921592Srgrimes			(void) fclose(fin);
15931592Srgrimes			return;
1594101034Syar		} else if (stbuf.st_size > MAXASIZE) {
1595101034Syar			reply(550, "%s: too large for type A SIZE.", filename);
1596101034Syar			(void) fclose(fin);
1597101034Syar			return;
15981592Srgrimes		}
15991592Srgrimes
16001592Srgrimes		count = 0;
16011592Srgrimes		while((c=getc(fin)) != EOF) {
16021592Srgrimes			if (c == '\n')	/* will get expanded to \r\n */
16031592Srgrimes				count++;
16041592Srgrimes			count++;
16051592Srgrimes		}
16061592Srgrimes		(void) fclose(fin);
16071592Srgrimes
16081592Srgrimes		reply(213, "%qd", count);
16091592Srgrimes		break; }
16101592Srgrimes	default:
1611100684Syar		reply(504, "SIZE not implemented for type %s.",
1612100684Syar		           typenames[type]);
16131592Srgrimes	}
16141592Srgrimes}
161556668Sshin
161656668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
161756668Sshinstatic int
161890148Simpport_check(const char *pcmd)
161956668Sshin{
162056668Sshin	if (his_addr.su_family == AF_INET) {
162156668Sshin		if (data_dest.su_family != AF_INET) {
162256668Sshin			usedefault = 1;
162356668Sshin			reply(500, "Invalid address rejected.");
162456668Sshin			return 1;
162556668Sshin		}
162656668Sshin		if (paranoid &&
162756668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
162856668Sshin		     memcmp(&data_dest.su_sin.sin_addr,
162956668Sshin			    &his_addr.su_sin.sin_addr,
163056668Sshin			    sizeof(data_dest.su_sin.sin_addr)))) {
163156668Sshin			usedefault = 1;
163256668Sshin			reply(500, "Illegal PORT range rejected.");
163356668Sshin		} else {
163456668Sshin			usedefault = 0;
163556668Sshin			if (pdata >= 0) {
163656668Sshin				(void) close(pdata);
163756668Sshin				pdata = -1;
163856668Sshin			}
163956668Sshin			reply(200, "%s command successful.", pcmd);
164056668Sshin		}
164156668Sshin		return 1;
164256668Sshin	}
164356668Sshin	return 0;
164456668Sshin}
164556668Sshin
164670102Sphkstatic int
164790148Simpcheck_login1(void)
164870102Sphk{
164970102Sphk	if (logged_in)
165070102Sphk		return 1;
165170102Sphk	else {
165270102Sphk		reply(530, "Please login with USER and PASS.");
165370102Sphk		return 0;
165470102Sphk	}
165570102Sphk}
165670102Sphk
165756668Sshin#ifdef INET6
165856668Sshin/* Return 1, if port check is done. Return 0, if not yet. */
165956668Sshinstatic int
166090148Simpport_check_v6(const char *pcmd)
166156668Sshin{
166256668Sshin	if (his_addr.su_family == AF_INET6) {
166356668Sshin		if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
166456668Sshin			/* Convert data_dest into v4 mapped sockaddr.*/
166556668Sshin			v4map_data_dest();
166656668Sshin		if (data_dest.su_family != AF_INET6) {
166756668Sshin			usedefault = 1;
166856668Sshin			reply(500, "Invalid address rejected.");
166956668Sshin			return 1;
167056668Sshin		}
167156668Sshin		if (paranoid &&
167256668Sshin		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
167356668Sshin		     memcmp(&data_dest.su_sin6.sin6_addr,
167456668Sshin			    &his_addr.su_sin6.sin6_addr,
167556668Sshin			    sizeof(data_dest.su_sin6.sin6_addr)))) {
167656668Sshin			usedefault = 1;
167756668Sshin			reply(500, "Illegal PORT range rejected.");
167856668Sshin		} else {
167956668Sshin			usedefault = 0;
168056668Sshin			if (pdata >= 0) {
168156668Sshin				(void) close(pdata);
168256668Sshin				pdata = -1;
168356668Sshin			}
168456668Sshin			reply(200, "%s command successful.", pcmd);
168556668Sshin		}
168656668Sshin		return 1;
168756668Sshin	}
168856668Sshin	return 0;
168956668Sshin}
169056668Sshin
169156668Sshinstatic void
169290148Simpv4map_data_dest(void)
169356668Sshin{
169456668Sshin	struct in_addr savedaddr;
169556668Sshin	int savedport;
169656668Sshin
169756668Sshin	if (data_dest.su_family != AF_INET) {
169856668Sshin		usedefault = 1;
169956668Sshin		reply(500, "Invalid address rejected.");
170056668Sshin		return;
170156668Sshin	}
170256668Sshin
170356668Sshin	savedaddr = data_dest.su_sin.sin_addr;
170456668Sshin	savedport = data_dest.su_port;
170556668Sshin
170656668Sshin	memset(&data_dest, 0, sizeof(data_dest));
170756668Sshin	data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
170856668Sshin	data_dest.su_sin6.sin6_family = AF_INET6;
170956668Sshin	data_dest.su_sin6.sin6_port = savedport;
171056668Sshin	memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
171156668Sshin	memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
171256668Sshin	       (caddr_t)&savedaddr, sizeof(savedaddr));
171356668Sshin}
171456668Sshin#endif
1715