xref: /freebsd/libexec/ftpd/ftpcmd.y (revision e20971500194d2f7299e9d01ca3b20e9bc6b4009)
18a16b7a1SPedro F. Giffuni /*-
28a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni  *
4ea022d16SRodney W. Grimes  * Copyright (c) 1985, 1988, 1993, 1994
5ea022d16SRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
6ea022d16SRodney W. Grimes  *
7ea022d16SRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
8ea022d16SRodney W. Grimes  * modification, are permitted provided that the following conditions
9ea022d16SRodney W. Grimes  * are met:
10ea022d16SRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
11ea022d16SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
12ea022d16SRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
13ea022d16SRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
14ea022d16SRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
155efaea4cSChristian Brueffer  * 3. Neither the name of the University nor the names of its contributors
16ea022d16SRodney W. Grimes  *    may be used to endorse or promote products derived from this software
17ea022d16SRodney W. Grimes  *    without specific prior written permission.
18ea022d16SRodney W. Grimes  *
19ea022d16SRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20ea022d16SRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21ea022d16SRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22ea022d16SRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23ea022d16SRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24ea022d16SRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25ea022d16SRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26ea022d16SRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27ea022d16SRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28ea022d16SRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29ea022d16SRodney W. Grimes  * SUCH DAMAGE.
30ea022d16SRodney W. Grimes  */
31ea022d16SRodney W. Grimes 
32ea022d16SRodney W. Grimes /*
33ea022d16SRodney W. Grimes  * Grammar for FTP commands.
34ea022d16SRodney W. Grimes  * See RFC 959.
35ea022d16SRodney W. Grimes  */
36ea022d16SRodney W. Grimes 
37ea022d16SRodney W. Grimes %{
38ea022d16SRodney W. Grimes 
39ea022d16SRodney W. Grimes #include <sys/param.h>
40ea022d16SRodney W. Grimes #include <sys/socket.h>
41ea022d16SRodney W. Grimes #include <sys/stat.h>
42ea022d16SRodney W. Grimes 
43ea022d16SRodney W. Grimes #include <netinet/in.h>
44ea022d16SRodney W. Grimes #include <arpa/ftp.h>
45ea022d16SRodney W. Grimes 
46ea022d16SRodney W. Grimes #include <ctype.h>
47ea022d16SRodney W. Grimes #include <errno.h>
48ea022d16SRodney W. Grimes #include <glob.h>
4939e99226SMaxim Konovalov #include <libutil.h>
507d0babdaSMaxim Konovalov #include <limits.h>
5139e99226SMaxim Konovalov #include <md5.h>
524dd8b5abSYoshinobu Inoue #include <netdb.h>
53ea022d16SRodney W. Grimes #include <pwd.h>
54ea022d16SRodney W. Grimes #include <signal.h>
55a57e1ef0SYaroslav Tykhiy #include <stdint.h>
56ea022d16SRodney W. Grimes #include <stdio.h>
57ea022d16SRodney W. Grimes #include <stdlib.h>
58ea022d16SRodney W. Grimes #include <string.h>
59ea022d16SRodney W. Grimes #include <syslog.h>
60ea022d16SRodney W. Grimes #include <time.h>
61ea022d16SRodney W. Grimes #include <unistd.h>
62ea022d16SRodney W. Grimes 
63ea022d16SRodney W. Grimes #include "extern.h"
64dcb4f239SYaroslav Tykhiy #include "pathnames.h"
65ea022d16SRodney W. Grimes 
66d836a9dbSJung-uk Kim #define	yylex	ftpcmd_yylex
67d836a9dbSJung-uk Kim 
68ea022d16SRodney W. Grimes off_t	restart_point;
69ea022d16SRodney W. Grimes 
70ea022d16SRodney W. Grimes static	int cmd_type;
71ea022d16SRodney W. Grimes static	int cmd_form;
72ea022d16SRodney W. Grimes static	int cmd_bytesz;
734b82fc95SYaroslav Tykhiy static	int state;
74ea022d16SRodney W. Grimes char	cbuf[512];
75aa5a9d3fSYaroslav Tykhiy char	*fromname = NULL;
76ea022d16SRodney W. Grimes 
77ea022d16SRodney W. Grimes %}
78ea022d16SRodney W. Grimes 
79ea022d16SRodney W. Grimes %union {
807d0babdaSMaxim Konovalov 	struct {
817d0babdaSMaxim Konovalov 		off_t	o;
82ea022d16SRodney W. Grimes 		int	i;
837d0babdaSMaxim Konovalov 	} u;
84ea022d16SRodney W. Grimes 	char   *s;
85ea022d16SRodney W. Grimes }
86ea022d16SRodney W. Grimes 
87ea022d16SRodney W. Grimes %token
88ea022d16SRodney W. Grimes 	A	B	C	E	F	I
89ea022d16SRodney W. Grimes 	L	N	P	R	S	T
904dd8b5abSYoshinobu Inoue 	ALL
91ea022d16SRodney W. Grimes 
92ea022d16SRodney W. Grimes 	SP	CRLF	COMMA
93ea022d16SRodney W. Grimes 
94ea022d16SRodney W. Grimes 	USER	PASS	ACCT	REIN	QUIT	PORT
95ea022d16SRodney W. Grimes 	PASV	TYPE	STRU	MODE	RETR	STOR
96ea022d16SRodney W. Grimes 	APPE	MLFL	MAIL	MSND	MSOM	MSAM
97ea022d16SRodney W. Grimes 	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
98ea022d16SRodney W. Grimes 	ABOR	DELE	CWD	LIST	NLST	SITE
99ea022d16SRodney W. Grimes 	STAT	HELP	NOOP	MKD	RMD	PWD
100ea022d16SRodney W. Grimes 	CDUP	STOU	SMNT	SYST	SIZE	MDTM
1012ea42282SYaroslav Tykhiy 	LPRT	LPSV	EPRT	EPSV	FEAT
102ea022d16SRodney W. Grimes 
10353ba84a6SPoul-Henning Kamp 	UMASK	IDLE	CHMOD	MDFIVE
104ea022d16SRodney W. Grimes 
105371348aeSYaroslav Tykhiy 	LEXERR	NOTIMPL
106ea022d16SRodney W. Grimes 
107ea022d16SRodney W. Grimes %token	<s> STRING
1087d0babdaSMaxim Konovalov %token	<u> NUMBER
109ea022d16SRodney W. Grimes 
1107d0babdaSMaxim Konovalov %type	<u.i> check_login octal_number byte_size
1117d0babdaSMaxim Konovalov %type	<u.i> check_login_ro check_login_epsv
1127d0babdaSMaxim Konovalov %type	<u.i> struct_code mode_code type_code form_code
11370825609SPeter Wemm %type	<s> pathstring pathname password username
114371348aeSYaroslav Tykhiy %type	<s> ALL NOTIMPL
115ea022d16SRodney W. Grimes 
116ea022d16SRodney W. Grimes %start	cmd_list
117ea022d16SRodney W. Grimes 
118ea022d16SRodney W. Grimes %%
119ea022d16SRodney W. Grimes 
120ea022d16SRodney W. Grimes cmd_list
121ea022d16SRodney W. Grimes 	: /* empty */
122ea022d16SRodney W. Grimes 	| cmd_list cmd
123ea022d16SRodney W. Grimes 		{
124c507cedeSDavid Malone 			if (fromname)
125c507cedeSDavid Malone 				free(fromname);
126aa5a9d3fSYaroslav Tykhiy 			fromname = NULL;
1270e519c96SYaroslav Tykhiy 			restart_point = 0;
128ea022d16SRodney W. Grimes 		}
129ea022d16SRodney W. Grimes 	| cmd_list rcmd
130ea022d16SRodney W. Grimes 	;
131ea022d16SRodney W. Grimes 
132ea022d16SRodney W. Grimes cmd
133ea022d16SRodney W. Grimes 	: USER SP username CRLF
134ea022d16SRodney W. Grimes 		{
135ea022d16SRodney W. Grimes 			user($3);
136ea022d16SRodney W. Grimes 			free($3);
137ea022d16SRodney W. Grimes 		}
138ea022d16SRodney W. Grimes 	| PASS SP password CRLF
139ea022d16SRodney W. Grimes 		{
140ea022d16SRodney W. Grimes 			pass($3);
141ea022d16SRodney W. Grimes 			free($3);
142ea022d16SRodney W. Grimes 		}
1437d6505e6SBrian Feldman 	| PASS CRLF
1447d6505e6SBrian Feldman 		{
1457d6505e6SBrian Feldman 			pass("");
1467d6505e6SBrian Feldman 		}
14761f891a6SPaul Traina 	| PORT check_login SP host_port CRLF
14861f891a6SPaul Traina 		{
1494dd8b5abSYoshinobu Inoue 			if (epsvall) {
15002c97492SYaroslav Tykhiy 				reply(501, "No PORT allowed after EPSV ALL.");
1514dd8b5abSYoshinobu Inoue 				goto port_done;
1524dd8b5abSYoshinobu Inoue 			}
1534dd8b5abSYoshinobu Inoue 			if (!$2)
1544dd8b5abSYoshinobu Inoue 				goto port_done;
1554dd8b5abSYoshinobu Inoue 			if (port_check("PORT") == 1)
1564dd8b5abSYoshinobu Inoue 				goto port_done;
1574dd8b5abSYoshinobu Inoue #ifdef INET6
1584dd8b5abSYoshinobu Inoue 			if ((his_addr.su_family != AF_INET6 ||
1594dd8b5abSYoshinobu Inoue 			     !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
1604dd8b5abSYoshinobu Inoue 				/* shoud never happen */
16161f891a6SPaul Traina 				usedefault = 1;
1624dd8b5abSYoshinobu Inoue 				reply(500, "Invalid address rejected.");
1634dd8b5abSYoshinobu Inoue 				goto port_done;
1644dd8b5abSYoshinobu Inoue 			}
1654dd8b5abSYoshinobu Inoue 			port_check_v6("pcmd");
1664dd8b5abSYoshinobu Inoue #endif
1674dd8b5abSYoshinobu Inoue 		port_done:
168012cdd2cSYaroslav Tykhiy 			;
1694dd8b5abSYoshinobu Inoue 		}
1704dd8b5abSYoshinobu Inoue 	| LPRT check_login SP host_long_port CRLF
1714dd8b5abSYoshinobu Inoue 		{
1724dd8b5abSYoshinobu Inoue 			if (epsvall) {
17302c97492SYaroslav Tykhiy 				reply(501, "No LPRT allowed after EPSV ALL.");
1744dd8b5abSYoshinobu Inoue 				goto lprt_done;
1754dd8b5abSYoshinobu Inoue 			}
1764dd8b5abSYoshinobu Inoue 			if (!$2)
1774dd8b5abSYoshinobu Inoue 				goto lprt_done;
1784dd8b5abSYoshinobu Inoue 			if (port_check("LPRT") == 1)
1794dd8b5abSYoshinobu Inoue 				goto lprt_done;
1804dd8b5abSYoshinobu Inoue #ifdef INET6
1814dd8b5abSYoshinobu Inoue 			if (his_addr.su_family != AF_INET6) {
1824dd8b5abSYoshinobu Inoue 				usedefault = 1;
1834dd8b5abSYoshinobu Inoue 				reply(500, "Invalid address rejected.");
1844dd8b5abSYoshinobu Inoue 				goto lprt_done;
1854dd8b5abSYoshinobu Inoue 			}
1864dd8b5abSYoshinobu Inoue 			if (port_check_v6("LPRT") == 1)
1874dd8b5abSYoshinobu Inoue 				goto lprt_done;
1884dd8b5abSYoshinobu Inoue #endif
1894dd8b5abSYoshinobu Inoue 		lprt_done:
190012cdd2cSYaroslav Tykhiy 			;
1914dd8b5abSYoshinobu Inoue 		}
1924dd8b5abSYoshinobu Inoue 	| EPRT check_login SP STRING CRLF
1934dd8b5abSYoshinobu Inoue 		{
1944dd8b5abSYoshinobu Inoue 			char delim;
1954dd8b5abSYoshinobu Inoue 			char *tmp = NULL;
1964dd8b5abSYoshinobu Inoue 			char *p, *q;
1974dd8b5abSYoshinobu Inoue 			char *result[3];
1984dd8b5abSYoshinobu Inoue 			struct addrinfo hints;
1994dd8b5abSYoshinobu Inoue 			struct addrinfo *res;
2004dd8b5abSYoshinobu Inoue 			int i;
2014dd8b5abSYoshinobu Inoue 
2024dd8b5abSYoshinobu Inoue 			if (epsvall) {
20302c97492SYaroslav Tykhiy 				reply(501, "No EPRT allowed after EPSV ALL.");
2044dd8b5abSYoshinobu Inoue 				goto eprt_done;
2054dd8b5abSYoshinobu Inoue 			}
2064dd8b5abSYoshinobu Inoue 			if (!$2)
2074dd8b5abSYoshinobu Inoue 				goto eprt_done;
2084dd8b5abSYoshinobu Inoue 
2094dd8b5abSYoshinobu Inoue 			memset(&data_dest, 0, sizeof(data_dest));
2104dd8b5abSYoshinobu Inoue 			tmp = strdup($4);
211618b0bbaSMark Murray 			if (ftpdebug)
2124dd8b5abSYoshinobu Inoue 				syslog(LOG_DEBUG, "%s", tmp);
2134dd8b5abSYoshinobu Inoue 			if (!tmp) {
214618b0bbaSMark Murray 				fatalerror("not enough core");
2154dd8b5abSYoshinobu Inoue 				/*NOTREACHED*/
2164dd8b5abSYoshinobu Inoue 			}
2174dd8b5abSYoshinobu Inoue 			p = tmp;
2184dd8b5abSYoshinobu Inoue 			delim = p[0];
2194dd8b5abSYoshinobu Inoue 			p++;
2204dd8b5abSYoshinobu Inoue 			memset(result, 0, sizeof(result));
2214dd8b5abSYoshinobu Inoue 			for (i = 0; i < 3; i++) {
2224dd8b5abSYoshinobu Inoue 				q = strchr(p, delim);
2234dd8b5abSYoshinobu Inoue 				if (!q || *q != delim) {
2244dd8b5abSYoshinobu Inoue 		parsefail:
22561f891a6SPaul Traina 					reply(500,
2264dd8b5abSYoshinobu Inoue 						"Invalid argument, rejected.");
2274dd8b5abSYoshinobu Inoue 					if (tmp)
2284dd8b5abSYoshinobu Inoue 						free(tmp);
2294dd8b5abSYoshinobu Inoue 					usedefault = 1;
2304dd8b5abSYoshinobu Inoue 					goto eprt_done;
231ea022d16SRodney W. Grimes 				}
2324dd8b5abSYoshinobu Inoue 				*q++ = '\0';
2334dd8b5abSYoshinobu Inoue 				result[i] = p;
234618b0bbaSMark Murray 				if (ftpdebug)
2354dd8b5abSYoshinobu Inoue 					syslog(LOG_DEBUG, "%d: %s", i, p);
2364dd8b5abSYoshinobu Inoue 				p = q;
237ea022d16SRodney W. Grimes 			}
2384dd8b5abSYoshinobu Inoue 
2394dd8b5abSYoshinobu Inoue 			/* some more sanity check */
2404dd8b5abSYoshinobu Inoue 			p = result[0];
2414dd8b5abSYoshinobu Inoue 			while (*p) {
2424dd8b5abSYoshinobu Inoue 				if (!isdigit(*p))
2434dd8b5abSYoshinobu Inoue 					goto parsefail;
2444dd8b5abSYoshinobu Inoue 				p++;
24561f891a6SPaul Traina 			}
2464dd8b5abSYoshinobu Inoue 			p = result[2];
2474dd8b5abSYoshinobu Inoue 			while (*p) {
2484dd8b5abSYoshinobu Inoue 				if (!isdigit(*p))
2494dd8b5abSYoshinobu Inoue 					goto parsefail;
2504dd8b5abSYoshinobu Inoue 				p++;
2514dd8b5abSYoshinobu Inoue 			}
2524dd8b5abSYoshinobu Inoue 
2534dd8b5abSYoshinobu Inoue 			/* grab address */
2544dd8b5abSYoshinobu Inoue 			memset(&hints, 0, sizeof(hints));
2554dd8b5abSYoshinobu Inoue 			if (atoi(result[0]) == 1)
2564dd8b5abSYoshinobu Inoue 				hints.ai_family = PF_INET;
2574dd8b5abSYoshinobu Inoue #ifdef INET6
2584dd8b5abSYoshinobu Inoue 			else if (atoi(result[0]) == 2)
2594dd8b5abSYoshinobu Inoue 				hints.ai_family = PF_INET6;
2604dd8b5abSYoshinobu Inoue #endif
2614dd8b5abSYoshinobu Inoue 			else
2624dd8b5abSYoshinobu Inoue 				hints.ai_family = PF_UNSPEC;	/*XXX*/
2634dd8b5abSYoshinobu Inoue 			hints.ai_socktype = SOCK_STREAM;
2644dd8b5abSYoshinobu Inoue 			i = getaddrinfo(result[1], result[2], &hints, &res);
2654dd8b5abSYoshinobu Inoue 			if (i)
2664dd8b5abSYoshinobu Inoue 				goto parsefail;
2674dd8b5abSYoshinobu Inoue 			memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
2684dd8b5abSYoshinobu Inoue #ifdef INET6
2694dd8b5abSYoshinobu Inoue 			if (his_addr.su_family == AF_INET6
2704dd8b5abSYoshinobu Inoue 			    && data_dest.su_family == AF_INET6) {
2714dd8b5abSYoshinobu Inoue 				/* XXX more sanity checks! */
2724dd8b5abSYoshinobu Inoue 				data_dest.su_sin6.sin6_scope_id =
2734dd8b5abSYoshinobu Inoue 					his_addr.su_sin6.sin6_scope_id;
2744dd8b5abSYoshinobu Inoue 			}
2754dd8b5abSYoshinobu Inoue #endif
2764dd8b5abSYoshinobu Inoue 			free(tmp);
2774dd8b5abSYoshinobu Inoue 			tmp = NULL;
2784dd8b5abSYoshinobu Inoue 
2794dd8b5abSYoshinobu Inoue 			if (port_check("EPRT") == 1)
2804dd8b5abSYoshinobu Inoue 				goto eprt_done;
2814dd8b5abSYoshinobu Inoue #ifdef INET6
2824dd8b5abSYoshinobu Inoue 			if (his_addr.su_family != AF_INET6) {
2834dd8b5abSYoshinobu Inoue 				usedefault = 1;
2844dd8b5abSYoshinobu Inoue 				reply(500, "Invalid address rejected.");
2854dd8b5abSYoshinobu Inoue 				goto eprt_done;
2864dd8b5abSYoshinobu Inoue 			}
2874dd8b5abSYoshinobu Inoue 			if (port_check_v6("EPRT") == 1)
2884dd8b5abSYoshinobu Inoue 				goto eprt_done;
2894dd8b5abSYoshinobu Inoue #endif
290c507cedeSDavid Malone 		eprt_done:
291c507cedeSDavid Malone 			free($4);
29261f891a6SPaul Traina 		}
29361f891a6SPaul Traina 	| PASV check_login CRLF
294ea022d16SRodney W. Grimes 		{
2954dd8b5abSYoshinobu Inoue 			if (epsvall)
29602c97492SYaroslav Tykhiy 				reply(501, "No PASV allowed after EPSV ALL.");
2974dd8b5abSYoshinobu Inoue 			else if ($2)
298ea022d16SRodney W. Grimes 				passive();
299ea022d16SRodney W. Grimes 		}
3004dd8b5abSYoshinobu Inoue 	| LPSV check_login CRLF
3014dd8b5abSYoshinobu Inoue 		{
3024dd8b5abSYoshinobu Inoue 			if (epsvall)
30302c97492SYaroslav Tykhiy 				reply(501, "No LPSV allowed after EPSV ALL.");
3044dd8b5abSYoshinobu Inoue 			else if ($2)
3054dd8b5abSYoshinobu Inoue 				long_passive("LPSV", PF_UNSPEC);
3064dd8b5abSYoshinobu Inoue 		}
307a4b77a2aSPoul-Henning Kamp 	| EPSV check_login_epsv SP NUMBER CRLF
3084dd8b5abSYoshinobu Inoue 		{
3094dd8b5abSYoshinobu Inoue 			if ($2) {
3104dd8b5abSYoshinobu Inoue 				int pf;
3117d0babdaSMaxim Konovalov 				switch ($4.i) {
3124dd8b5abSYoshinobu Inoue 				case 1:
3134dd8b5abSYoshinobu Inoue 					pf = PF_INET;
3144dd8b5abSYoshinobu Inoue 					break;
3154dd8b5abSYoshinobu Inoue #ifdef INET6
3164dd8b5abSYoshinobu Inoue 				case 2:
3174dd8b5abSYoshinobu Inoue 					pf = PF_INET6;
3184dd8b5abSYoshinobu Inoue 					break;
3194dd8b5abSYoshinobu Inoue #endif
3204dd8b5abSYoshinobu Inoue 				default:
3214dd8b5abSYoshinobu Inoue 					pf = -1;	/*junk value*/
3224dd8b5abSYoshinobu Inoue 					break;
3234dd8b5abSYoshinobu Inoue 				}
3244dd8b5abSYoshinobu Inoue 				long_passive("EPSV", pf);
3254dd8b5abSYoshinobu Inoue 			}
3264dd8b5abSYoshinobu Inoue 		}
327a4b77a2aSPoul-Henning Kamp 	| EPSV check_login_epsv SP ALL CRLF
3284dd8b5abSYoshinobu Inoue 		{
3294dd8b5abSYoshinobu Inoue 			if ($2) {
33002c97492SYaroslav Tykhiy 				reply(200, "EPSV ALL command successful.");
3314dd8b5abSYoshinobu Inoue 				epsvall++;
3324dd8b5abSYoshinobu Inoue 			}
3334dd8b5abSYoshinobu Inoue 		}
334a4b77a2aSPoul-Henning Kamp 	| EPSV check_login_epsv CRLF
3354dd8b5abSYoshinobu Inoue 		{
3364dd8b5abSYoshinobu Inoue 			if ($2)
3374dd8b5abSYoshinobu Inoue 				long_passive("EPSV", PF_UNSPEC);
3384dd8b5abSYoshinobu Inoue 		}
3393fca54b6SChris D. Faulhaber 	| TYPE check_login SP type_code CRLF
340ea022d16SRodney W. Grimes 		{
3413fca54b6SChris D. Faulhaber 			if ($2) {
342ea022d16SRodney W. Grimes 				switch (cmd_type) {
343ea022d16SRodney W. Grimes 
344ea022d16SRodney W. Grimes 				case TYPE_A:
345ea022d16SRodney W. Grimes 					if (cmd_form == FORM_N) {
346ea022d16SRodney W. Grimes 						reply(200, "Type set to A.");
347ea022d16SRodney W. Grimes 						type = cmd_type;
348ea022d16SRodney W. Grimes 						form = cmd_form;
349ea022d16SRodney W. Grimes 					} else
350ea022d16SRodney W. Grimes 						reply(504, "Form must be N.");
351ea022d16SRodney W. Grimes 					break;
352ea022d16SRodney W. Grimes 
353ea022d16SRodney W. Grimes 				case TYPE_E:
354ea022d16SRodney W. Grimes 					reply(504, "Type E not implemented.");
355ea022d16SRodney W. Grimes 					break;
356ea022d16SRodney W. Grimes 
357ea022d16SRodney W. Grimes 				case TYPE_I:
358ea022d16SRodney W. Grimes 					reply(200, "Type set to I.");
359ea022d16SRodney W. Grimes 					type = cmd_type;
360ea022d16SRodney W. Grimes 					break;
361ea022d16SRodney W. Grimes 
362ea022d16SRodney W. Grimes 				case TYPE_L:
36389fdc4e1SMike Barcroft #if CHAR_BIT == 8
364ea022d16SRodney W. Grimes 					if (cmd_bytesz == 8) {
365ea022d16SRodney W. Grimes 						reply(200,
366ea022d16SRodney W. Grimes 						    "Type set to L (byte size 8).");
367ea022d16SRodney W. Grimes 						type = cmd_type;
368ea022d16SRodney W. Grimes 					} else
369ea022d16SRodney W. Grimes 						reply(504, "Byte size must be 8.");
37089fdc4e1SMike Barcroft #else /* CHAR_BIT == 8 */
37189fdc4e1SMike Barcroft 					UNIMPLEMENTED for CHAR_BIT != 8
37289fdc4e1SMike Barcroft #endif /* CHAR_BIT == 8 */
373ea022d16SRodney W. Grimes 				}
374ea022d16SRodney W. Grimes 			}
3753fca54b6SChris D. Faulhaber 		}
3763fca54b6SChris D. Faulhaber 	| STRU check_login SP struct_code CRLF
377ea022d16SRodney W. Grimes 		{
3783fca54b6SChris D. Faulhaber 			if ($2) {
3793fca54b6SChris D. Faulhaber 				switch ($4) {
380ea022d16SRodney W. Grimes 
381ea022d16SRodney W. Grimes 				case STRU_F:
38202c97492SYaroslav Tykhiy 					reply(200, "STRU F accepted.");
383ea022d16SRodney W. Grimes 					break;
384ea022d16SRodney W. Grimes 
385ea022d16SRodney W. Grimes 				default:
386ea022d16SRodney W. Grimes 					reply(504, "Unimplemented STRU type.");
387ea022d16SRodney W. Grimes 				}
388ea022d16SRodney W. Grimes 			}
3893fca54b6SChris D. Faulhaber 		}
3903fca54b6SChris D. Faulhaber 	| MODE check_login SP mode_code CRLF
391ea022d16SRodney W. Grimes 		{
3923fca54b6SChris D. Faulhaber 			if ($2) {
3933fca54b6SChris D. Faulhaber 				switch ($4) {
394ea022d16SRodney W. Grimes 
395ea022d16SRodney W. Grimes 				case MODE_S:
39602c97492SYaroslav Tykhiy 					reply(200, "MODE S accepted.");
397ea022d16SRodney W. Grimes 					break;
398ea022d16SRodney W. Grimes 
399ea022d16SRodney W. Grimes 				default:
400ea022d16SRodney W. Grimes 					reply(502, "Unimplemented MODE type.");
401ea022d16SRodney W. Grimes 				}
402ea022d16SRodney W. Grimes 			}
4033fca54b6SChris D. Faulhaber 		}
4043fca54b6SChris D. Faulhaber 	| ALLO check_login SP NUMBER CRLF
405ea022d16SRodney W. Grimes 		{
4063fca54b6SChris D. Faulhaber 			if ($2) {
407ea022d16SRodney W. Grimes 				reply(202, "ALLO command ignored.");
408ea022d16SRodney W. Grimes 			}
4093fca54b6SChris D. Faulhaber 		}
4103fca54b6SChris D. Faulhaber 	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
411ea022d16SRodney W. Grimes 		{
4123fca54b6SChris D. Faulhaber 			if ($2) {
413ea022d16SRodney W. Grimes 				reply(202, "ALLO command ignored.");
414ea022d16SRodney W. Grimes 			}
4153fca54b6SChris D. Faulhaber 		}
416ea022d16SRodney W. Grimes 	| RETR check_login SP pathname CRLF
417ea022d16SRodney W. Grimes 		{
4181cc9f0bbSSheldon Hearn 			if (noretr || (guest && noguestretr))
41902c97492SYaroslav Tykhiy 				reply(500, "RETR command disabled.");
42062513e76SNik Clayton 			else if ($2 && $4 != NULL)
421aa5a9d3fSYaroslav Tykhiy 				retrieve(NULL, $4);
42262513e76SNik Clayton 
423ea022d16SRodney W. Grimes 			if ($4 != NULL)
424ea022d16SRodney W. Grimes 				free($4);
425ea022d16SRodney W. Grimes 		}
426a4b77a2aSPoul-Henning Kamp 	| STOR check_login_ro SP pathname CRLF
427ea022d16SRodney W. Grimes 		{
428ea022d16SRodney W. Grimes 			if ($2 && $4 != NULL)
429ea022d16SRodney W. Grimes 				store($4, "w", 0);
430ea022d16SRodney W. Grimes 			if ($4 != NULL)
431ea022d16SRodney W. Grimes 				free($4);
432ea022d16SRodney W. Grimes 		}
433a4b77a2aSPoul-Henning Kamp 	| APPE check_login_ro SP pathname CRLF
434ea022d16SRodney W. Grimes 		{
435ea022d16SRodney W. Grimes 			if ($2 && $4 != NULL)
436ea022d16SRodney W. Grimes 				store($4, "a", 0);
437ea022d16SRodney W. Grimes 			if ($4 != NULL)
438ea022d16SRodney W. Grimes 				free($4);
439ea022d16SRodney W. Grimes 		}
440ea022d16SRodney W. Grimes 	| NLST check_login CRLF
441ea022d16SRodney W. Grimes 		{
442ea022d16SRodney W. Grimes 			if ($2)
443ea022d16SRodney W. Grimes 				send_file_list(".");
444ea022d16SRodney W. Grimes 		}
4451b9f1a4bSYaroslav Tykhiy 	| NLST check_login SP pathstring CRLF
446ea022d16SRodney W. Grimes 		{
4471b9f1a4bSYaroslav Tykhiy 			if ($2)
448ea022d16SRodney W. Grimes 				send_file_list($4);
449ea022d16SRodney W. Grimes 			free($4);
450ea022d16SRodney W. Grimes 		}
451ea022d16SRodney W. Grimes 	| LIST check_login CRLF
452ea022d16SRodney W. Grimes 		{
453ea022d16SRodney W. Grimes 			if ($2)
454*e2097150SAllan Jude 				retrieve(_PATH_LS " -lA", "");
455ea022d16SRodney W. Grimes 		}
45670825609SPeter Wemm 	| LIST check_login SP pathstring CRLF
457ea022d16SRodney W. Grimes 		{
4581b9f1a4bSYaroslav Tykhiy 			if ($2)
459*e2097150SAllan Jude 				retrieve(_PATH_LS " -lA %s", $4);
460ea022d16SRodney W. Grimes 			free($4);
461ea022d16SRodney W. Grimes 		}
462ea022d16SRodney W. Grimes 	| STAT check_login SP pathname CRLF
463ea022d16SRodney W. Grimes 		{
464ea022d16SRodney W. Grimes 			if ($2 && $4 != NULL)
465ea022d16SRodney W. Grimes 				statfilecmd($4);
466ea022d16SRodney W. Grimes 			if ($4 != NULL)
467ea022d16SRodney W. Grimes 				free($4);
468ea022d16SRodney W. Grimes 		}
4693fca54b6SChris D. Faulhaber 	| STAT check_login CRLF
470ea022d16SRodney W. Grimes 		{
4713fca54b6SChris D. Faulhaber 			if ($2) {
472ea022d16SRodney W. Grimes 				statcmd();
473ea022d16SRodney W. Grimes 			}
4743fca54b6SChris D. Faulhaber 		}
475a4b77a2aSPoul-Henning Kamp 	| DELE check_login_ro SP pathname CRLF
476ea022d16SRodney W. Grimes 		{
477ea022d16SRodney W. Grimes 			if ($2 && $4 != NULL)
478ea022d16SRodney W. Grimes 				delete($4);
479ea022d16SRodney W. Grimes 			if ($4 != NULL)
480ea022d16SRodney W. Grimes 				free($4);
481ea022d16SRodney W. Grimes 		}
482a4b77a2aSPoul-Henning Kamp 	| RNTO check_login_ro SP pathname CRLF
483ea022d16SRodney W. Grimes 		{
484effa0530SYaroslav Tykhiy 			if ($2 && $4 != NULL) {
485ea022d16SRodney W. Grimes 				if (fromname) {
48661f891a6SPaul Traina 					renamecmd(fromname, $4);
487ea022d16SRodney W. Grimes 					free(fromname);
488aa5a9d3fSYaroslav Tykhiy 					fromname = NULL;
489ea022d16SRodney W. Grimes 				} else {
490ea022d16SRodney W. Grimes 					reply(503, "Bad sequence of commands.");
491ea022d16SRodney W. Grimes 				}
49261f891a6SPaul Traina 			}
493effa0530SYaroslav Tykhiy 			if ($4 != NULL)
49461f891a6SPaul Traina 				free($4);
495ea022d16SRodney W. Grimes 		}
4963fca54b6SChris D. Faulhaber 	| ABOR check_login CRLF
497ea022d16SRodney W. Grimes 		{
4983fca54b6SChris D. Faulhaber 			if ($2)
499ea022d16SRodney W. Grimes 				reply(225, "ABOR command successful.");
500ea022d16SRodney W. Grimes 		}
501ea022d16SRodney W. Grimes 	| CWD check_login CRLF
502ea022d16SRodney W. Grimes 		{
5033fbaa839SDaniel O'Callaghan 			if ($2) {
504ce9287fcSYaroslav Tykhiy 				cwd(homedir);
505ea022d16SRodney W. Grimes 			}
5063fbaa839SDaniel O'Callaghan 		}
507ea022d16SRodney W. Grimes 	| CWD check_login SP pathname CRLF
508ea022d16SRodney W. Grimes 		{
509ea022d16SRodney W. Grimes 			if ($2 && $4 != NULL)
510ea022d16SRodney W. Grimes 				cwd($4);
511ea022d16SRodney W. Grimes 			if ($4 != NULL)
512ea022d16SRodney W. Grimes 				free($4);
513ea022d16SRodney W. Grimes 		}
514ea022d16SRodney W. Grimes 	| HELP CRLF
515ea022d16SRodney W. Grimes 		{
516aa5a9d3fSYaroslav Tykhiy 			help(cmdtab, NULL);
517ea022d16SRodney W. Grimes 		}
518ea022d16SRodney W. Grimes 	| HELP SP STRING CRLF
519ea022d16SRodney W. Grimes 		{
520ea022d16SRodney W. Grimes 			char *cp = $3;
521ea022d16SRodney W. Grimes 
522ea022d16SRodney W. Grimes 			if (strncasecmp(cp, "SITE", 4) == 0) {
523ea022d16SRodney W. Grimes 				cp = $3 + 4;
524ea022d16SRodney W. Grimes 				if (*cp == ' ')
525ea022d16SRodney W. Grimes 					cp++;
526ea022d16SRodney W. Grimes 				if (*cp)
527ea022d16SRodney W. Grimes 					help(sitetab, cp);
528ea022d16SRodney W. Grimes 				else
529aa5a9d3fSYaroslav Tykhiy 					help(sitetab, NULL);
530ea022d16SRodney W. Grimes 			} else
531ea022d16SRodney W. Grimes 				help(cmdtab, $3);
532c507cedeSDavid Malone 			free($3);
533ea022d16SRodney W. Grimes 		}
534ea022d16SRodney W. Grimes 	| NOOP CRLF
535ea022d16SRodney W. Grimes 		{
536ea022d16SRodney W. Grimes 			reply(200, "NOOP command successful.");
537ea022d16SRodney W. Grimes 		}
538a4b77a2aSPoul-Henning Kamp 	| MKD check_login_ro SP pathname CRLF
539ea022d16SRodney W. Grimes 		{
540ea022d16SRodney W. Grimes 			if ($2 && $4 != NULL)
541ea022d16SRodney W. Grimes 				makedir($4);
542ea022d16SRodney W. Grimes 			if ($4 != NULL)
543ea022d16SRodney W. Grimes 				free($4);
544ea022d16SRodney W. Grimes 		}
545a4b77a2aSPoul-Henning Kamp 	| RMD check_login_ro SP pathname CRLF
546ea022d16SRodney W. Grimes 		{
547ea022d16SRodney W. Grimes 			if ($2 && $4 != NULL)
548ea022d16SRodney W. Grimes 				removedir($4);
549ea022d16SRodney W. Grimes 			if ($4 != NULL)
550ea022d16SRodney W. Grimes 				free($4);
551ea022d16SRodney W. Grimes 		}
552ea022d16SRodney W. Grimes 	| PWD check_login CRLF
553ea022d16SRodney W. Grimes 		{
554ea022d16SRodney W. Grimes 			if ($2)
555ea022d16SRodney W. Grimes 				pwd();
556ea022d16SRodney W. Grimes 		}
557ea022d16SRodney W. Grimes 	| CDUP check_login CRLF
558ea022d16SRodney W. Grimes 		{
559ea022d16SRodney W. Grimes 			if ($2)
560ea022d16SRodney W. Grimes 				cwd("..");
561ea022d16SRodney W. Grimes 		}
562ea022d16SRodney W. Grimes 	| SITE SP HELP CRLF
563ea022d16SRodney W. Grimes 		{
564aa5a9d3fSYaroslav Tykhiy 			help(sitetab, NULL);
565ea022d16SRodney W. Grimes 		}
566ea022d16SRodney W. Grimes 	| SITE SP HELP SP STRING CRLF
567ea022d16SRodney W. Grimes 		{
568ea022d16SRodney W. Grimes 			help(sitetab, $5);
569c507cedeSDavid Malone 			free($5);
570ea022d16SRodney W. Grimes 		}
57153ba84a6SPoul-Henning Kamp 	| SITE SP MDFIVE check_login SP pathname CRLF
57253ba84a6SPoul-Henning Kamp 		{
57353ba84a6SPoul-Henning Kamp 			char p[64], *q;
57453ba84a6SPoul-Henning Kamp 
575effa0530SYaroslav Tykhiy 			if ($4 && $6) {
57653ba84a6SPoul-Henning Kamp 				q = MD5File($6, p);
57753ba84a6SPoul-Henning Kamp 				if (q != NULL)
57853ba84a6SPoul-Henning Kamp 					reply(200, "MD5(%s) = %s", $6, p);
57953ba84a6SPoul-Henning Kamp 				else
58053ba84a6SPoul-Henning Kamp 					perror_reply(550, $6);
58153ba84a6SPoul-Henning Kamp 			}
582c507cedeSDavid Malone 			if ($6)
583c507cedeSDavid Malone 				free($6);
58453ba84a6SPoul-Henning Kamp 		}
585ea022d16SRodney W. Grimes 	| SITE SP UMASK check_login CRLF
586ea022d16SRodney W. Grimes 		{
587ea022d16SRodney W. Grimes 			int oldmask;
588ea022d16SRodney W. Grimes 
589ea022d16SRodney W. Grimes 			if ($4) {
590ea022d16SRodney W. Grimes 				oldmask = umask(0);
591ea022d16SRodney W. Grimes 				(void) umask(oldmask);
59202c97492SYaroslav Tykhiy 				reply(200, "Current UMASK is %03o.", oldmask);
593ea022d16SRodney W. Grimes 			}
594ea022d16SRodney W. Grimes 		}
595ea022d16SRodney W. Grimes 	| SITE SP UMASK check_login SP octal_number CRLF
596ea022d16SRodney W. Grimes 		{
597ea022d16SRodney W. Grimes 			int oldmask;
598ea022d16SRodney W. Grimes 
599ea022d16SRodney W. Grimes 			if ($4) {
600ea022d16SRodney W. Grimes 				if (($6 == -1) || ($6 > 0777)) {
60102c97492SYaroslav Tykhiy 					reply(501, "Bad UMASK value.");
602ea022d16SRodney W. Grimes 				} else {
603ea022d16SRodney W. Grimes 					oldmask = umask($6);
604ea022d16SRodney W. Grimes 					reply(200,
60502c97492SYaroslav Tykhiy 					    "UMASK set to %03o (was %03o).",
606ea022d16SRodney W. Grimes 					    $6, oldmask);
607ea022d16SRodney W. Grimes 				}
608ea022d16SRodney W. Grimes 			}
609ea022d16SRodney W. Grimes 		}
610a4b77a2aSPoul-Henning Kamp 	| SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
611ea022d16SRodney W. Grimes 		{
612ea022d16SRodney W. Grimes 			if ($4 && ($8 != NULL)) {
613c452fbe1SYaroslav Tykhiy 				if (($6 == -1 ) || ($6 > 0777))
61402c97492SYaroslav Tykhiy 					reply(501, "Bad mode value.");
615ea022d16SRodney W. Grimes 				else if (chmod($8, $6) < 0)
616ea022d16SRodney W. Grimes 					perror_reply(550, $8);
617ea022d16SRodney W. Grimes 				else
618ea022d16SRodney W. Grimes 					reply(200, "CHMOD command successful.");
619ea022d16SRodney W. Grimes 			}
620ea022d16SRodney W. Grimes 			if ($8 != NULL)
621ea022d16SRodney W. Grimes 				free($8);
622ea022d16SRodney W. Grimes 		}
6233fca54b6SChris D. Faulhaber 	| SITE SP check_login IDLE CRLF
624ea022d16SRodney W. Grimes 		{
6253fca54b6SChris D. Faulhaber 			if ($3)
626ea022d16SRodney W. Grimes 				reply(200,
62702c97492SYaroslav Tykhiy 			    	    "Current IDLE time limit is %d seconds; max %d.",
628ea022d16SRodney W. Grimes 				    timeout, maxtimeout);
629ea022d16SRodney W. Grimes 		}
6303fca54b6SChris D. Faulhaber 	| SITE SP check_login IDLE SP NUMBER CRLF
631ea022d16SRodney W. Grimes 		{
6323fca54b6SChris D. Faulhaber 			if ($3) {
6337d0babdaSMaxim Konovalov 				if ($6.i < 30 || $6.i > maxtimeout) {
634ea022d16SRodney W. Grimes 					reply(501,
63502c97492SYaroslav Tykhiy 					    "Maximum IDLE time must be between 30 and %d seconds.",
636ea022d16SRodney W. Grimes 					    maxtimeout);
637ea022d16SRodney W. Grimes 				} else {
6387d0babdaSMaxim Konovalov 					timeout = $6.i;
639e3765043SYaroslav Tykhiy 					(void) alarm(timeout);
640ea022d16SRodney W. Grimes 					reply(200,
64102c97492SYaroslav Tykhiy 					    "Maximum IDLE time set to %d seconds.",
642ea022d16SRodney W. Grimes 					    timeout);
643ea022d16SRodney W. Grimes 				}
644ea022d16SRodney W. Grimes 			}
6453fca54b6SChris D. Faulhaber 		}
646a4b77a2aSPoul-Henning Kamp 	| STOU check_login_ro SP pathname CRLF
647ea022d16SRodney W. Grimes 		{
648ea022d16SRodney W. Grimes 			if ($2 && $4 != NULL)
649ea022d16SRodney W. Grimes 				store($4, "w", 1);
650ea022d16SRodney W. Grimes 			if ($4 != NULL)
651ea022d16SRodney W. Grimes 				free($4);
652ea022d16SRodney W. Grimes 		}
6532ea42282SYaroslav Tykhiy 	| FEAT CRLF
6542ea42282SYaroslav Tykhiy 		{
6552ea42282SYaroslav Tykhiy 			lreply(211, "Extensions supported:");
6562ea42282SYaroslav Tykhiy #if 0
6572ea42282SYaroslav Tykhiy 			/* XXX these two keywords are non-standard */
6582ea42282SYaroslav Tykhiy 			printf(" EPRT\r\n");
6592ea42282SYaroslav Tykhiy 			if (!noepsv)
6602ea42282SYaroslav Tykhiy 				printf(" EPSV\r\n");
6612ea42282SYaroslav Tykhiy #endif
6622ea42282SYaroslav Tykhiy 			printf(" MDTM\r\n");
6632ea42282SYaroslav Tykhiy 			printf(" REST STREAM\r\n");
6642ea42282SYaroslav Tykhiy 			printf(" SIZE\r\n");
6652ea42282SYaroslav Tykhiy 			if (assumeutf8) {
6662ea42282SYaroslav Tykhiy 				/* TVFS requires UTF8, see RFC 3659 */
6672ea42282SYaroslav Tykhiy 				printf(" TVFS\r\n");
6682ea42282SYaroslav Tykhiy 				printf(" UTF8\r\n");
6692ea42282SYaroslav Tykhiy 			}
6702ea42282SYaroslav Tykhiy 			reply(211, "End.");
6712ea42282SYaroslav Tykhiy 		}
6723fca54b6SChris D. Faulhaber 	| SYST check_login CRLF
673ea022d16SRodney W. Grimes 		{
674a278e092SYaroslav Tykhiy 			if ($2) {
675a278e092SYaroslav Tykhiy 				if (hostinfo)
676ea022d16SRodney W. Grimes #ifdef BSD
677ea022d16SRodney W. Grimes 					reply(215, "UNIX Type: L%d Version: BSD-%d",
67889fdc4e1SMike Barcroft 					      CHAR_BIT, BSD);
679ea022d16SRodney W. Grimes #else /* BSD */
68089fdc4e1SMike Barcroft 					reply(215, "UNIX Type: L%d", CHAR_BIT);
681ea022d16SRodney W. Grimes #endif /* BSD */
682a278e092SYaroslav Tykhiy 				else
68389fdc4e1SMike Barcroft 					reply(215, "UNKNOWN Type: L%d", CHAR_BIT);
684a278e092SYaroslav Tykhiy 			}
685ea022d16SRodney W. Grimes 		}
686ea022d16SRodney W. Grimes 
687ea022d16SRodney W. Grimes 		/*
688ea022d16SRodney W. Grimes 		 * SIZE is not in RFC959, but Postel has blessed it and
689ea022d16SRodney W. Grimes 		 * it will be in the updated RFC.
690ea022d16SRodney W. Grimes 		 *
691ea022d16SRodney W. Grimes 		 * Return size of file in a format suitable for
692ea022d16SRodney W. Grimes 		 * using with RESTART (we just count bytes).
693ea022d16SRodney W. Grimes 		 */
694ea022d16SRodney W. Grimes 	| SIZE check_login SP pathname CRLF
695ea022d16SRodney W. Grimes 		{
696ea022d16SRodney W. Grimes 			if ($2 && $4 != NULL)
697ea022d16SRodney W. Grimes 				sizecmd($4);
698ea022d16SRodney W. Grimes 			if ($4 != NULL)
699ea022d16SRodney W. Grimes 				free($4);
700ea022d16SRodney W. Grimes 		}
701ea022d16SRodney W. Grimes 
702ea022d16SRodney W. Grimes 		/*
703ea022d16SRodney W. Grimes 		 * MDTM is not in RFC959, but Postel has blessed it and
704ea022d16SRodney W. Grimes 		 * it will be in the updated RFC.
705ea022d16SRodney W. Grimes 		 *
706ea022d16SRodney W. Grimes 		 * Return modification time of file as an ISO 3307
707ea022d16SRodney W. Grimes 		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
708ea022d16SRodney W. Grimes 		 * where xxx is the fractional second (of any precision,
709ea022d16SRodney W. Grimes 		 * not necessarily 3 digits)
710ea022d16SRodney W. Grimes 		 */
711ea022d16SRodney W. Grimes 	| MDTM check_login SP pathname CRLF
712ea022d16SRodney W. Grimes 		{
713ea022d16SRodney W. Grimes 			if ($2 && $4 != NULL) {
714ea022d16SRodney W. Grimes 				struct stat stbuf;
715ea022d16SRodney W. Grimes 				if (stat($4, &stbuf) < 0)
71682c03024SYaroslav Tykhiy 					perror_reply(550, $4);
717ea022d16SRodney W. Grimes 				else if (!S_ISREG(stbuf.st_mode)) {
718ea022d16SRodney W. Grimes 					reply(550, "%s: not a plain file.", $4);
719ea022d16SRodney W. Grimes 				} else {
720ea022d16SRodney W. Grimes 					struct tm *t;
721ea022d16SRodney W. Grimes 					t = gmtime(&stbuf.st_mtime);
722ea022d16SRodney W. Grimes 					reply(213,
723a5a4544eSPaul Traina 					    "%04d%02d%02d%02d%02d%02d",
724a5a4544eSPaul Traina 					    1900 + t->tm_year,
725a5a4544eSPaul Traina 					    t->tm_mon+1, t->tm_mday,
726ea022d16SRodney W. Grimes 					    t->tm_hour, t->tm_min, t->tm_sec);
727ea022d16SRodney W. Grimes 				}
728ea022d16SRodney W. Grimes 			}
729ea022d16SRodney W. Grimes 			if ($4 != NULL)
730ea022d16SRodney W. Grimes 				free($4);
731ea022d16SRodney W. Grimes 		}
732ea022d16SRodney W. Grimes 	| QUIT CRLF
733ea022d16SRodney W. Grimes 		{
734ea022d16SRodney W. Grimes 			reply(221, "Goodbye.");
735ea022d16SRodney W. Grimes 			dologout(0);
736ea022d16SRodney W. Grimes 		}
737371348aeSYaroslav Tykhiy 	| NOTIMPL
738371348aeSYaroslav Tykhiy 		{
739371348aeSYaroslav Tykhiy 			nack($1);
740371348aeSYaroslav Tykhiy 		}
7414b82fc95SYaroslav Tykhiy 	| error
742ea022d16SRodney W. Grimes 		{
7434b82fc95SYaroslav Tykhiy 			yyclearin;		/* discard lookahead data */
7444b82fc95SYaroslav Tykhiy 			yyerrok;		/* clear error condition */
745371348aeSYaroslav Tykhiy 			state = CMD;		/* reset lexer state */
746ea022d16SRodney W. Grimes 		}
747ea022d16SRodney W. Grimes 	;
748ea022d16SRodney W. Grimes rcmd
749a4b77a2aSPoul-Henning Kamp 	: RNFR check_login_ro SP pathname CRLF
750ea022d16SRodney W. Grimes 		{
7510e519c96SYaroslav Tykhiy 			restart_point = 0;
752ea022d16SRodney W. Grimes 			if ($2 && $4) {
753c507cedeSDavid Malone 				if (fromname)
754c507cedeSDavid Malone 					free(fromname);
755aa5a9d3fSYaroslav Tykhiy 				fromname = NULL;
756c507cedeSDavid Malone 				if (renamefrom($4))
757c507cedeSDavid Malone 					fromname = $4;
758c507cedeSDavid Malone 				else
759ea022d16SRodney W. Grimes 					free($4);
760c507cedeSDavid Malone 			} else if ($4) {
761c507cedeSDavid Malone 				free($4);
762ea022d16SRodney W. Grimes 			}
763ea022d16SRodney W. Grimes 		}
7647d0babdaSMaxim Konovalov 	| REST check_login SP NUMBER CRLF
765ea022d16SRodney W. Grimes 		{
7663fca54b6SChris D. Faulhaber 			if ($2) {
767c507cedeSDavid Malone 				if (fromname)
768c507cedeSDavid Malone 					free(fromname);
769aa5a9d3fSYaroslav Tykhiy 				fromname = NULL;
7707d0babdaSMaxim Konovalov 				restart_point = $4.o;
771a57e1ef0SYaroslav Tykhiy 				reply(350, "Restarting at %jd. %s",
772a57e1ef0SYaroslav Tykhiy 				    (intmax_t)restart_point,
773ea022d16SRodney W. Grimes 				    "Send STORE or RETRIEVE to initiate transfer.");
774ea022d16SRodney W. Grimes 			}
7753fca54b6SChris D. Faulhaber 		}
776ea022d16SRodney W. Grimes 	;
777ea022d16SRodney W. Grimes 
778ea022d16SRodney W. Grimes username
779ea022d16SRodney W. Grimes 	: STRING
780ea022d16SRodney W. Grimes 	;
781ea022d16SRodney W. Grimes 
782ea022d16SRodney W. Grimes password
783ea022d16SRodney W. Grimes 	: /* empty */
784ea022d16SRodney W. Grimes 		{
785ea022d16SRodney W. Grimes 			$$ = (char *)calloc(1, sizeof(char));
786ea022d16SRodney W. Grimes 		}
787ea022d16SRodney W. Grimes 	| STRING
788ea022d16SRodney W. Grimes 	;
789ea022d16SRodney W. Grimes 
790ea022d16SRodney W. Grimes byte_size
791ea022d16SRodney W. Grimes 	: NUMBER
7927d0babdaSMaxim Konovalov 		{
7937d0babdaSMaxim Konovalov 			$$ = $1.i;
7947d0babdaSMaxim Konovalov 		}
795ea022d16SRodney W. Grimes 	;
796ea022d16SRodney W. Grimes 
797ea022d16SRodney W. Grimes host_port
798ea022d16SRodney W. Grimes 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
799ea022d16SRodney W. Grimes 		NUMBER COMMA NUMBER
800ea022d16SRodney W. Grimes 		{
801ea022d16SRodney W. Grimes 			char *a, *p;
802ea022d16SRodney W. Grimes 
8034dd8b5abSYoshinobu Inoue 			data_dest.su_len = sizeof(struct sockaddr_in);
8044dd8b5abSYoshinobu Inoue 			data_dest.su_family = AF_INET;
8054dd8b5abSYoshinobu Inoue 			p = (char *)&data_dest.su_sin.sin_port;
8067d0babdaSMaxim Konovalov 			p[0] = $9.i; p[1] = $11.i;
8074dd8b5abSYoshinobu Inoue 			a = (char *)&data_dest.su_sin.sin_addr;
8087d0babdaSMaxim Konovalov 			a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
809ea022d16SRodney W. Grimes 		}
810ea022d16SRodney W. Grimes 	;
811ea022d16SRodney W. Grimes 
8124dd8b5abSYoshinobu Inoue host_long_port
8134dd8b5abSYoshinobu Inoue 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
8144dd8b5abSYoshinobu Inoue 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
8154dd8b5abSYoshinobu Inoue 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
8164dd8b5abSYoshinobu Inoue 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
8174dd8b5abSYoshinobu Inoue 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
8184dd8b5abSYoshinobu Inoue 		NUMBER
8194dd8b5abSYoshinobu Inoue 		{
8204dd8b5abSYoshinobu Inoue 			char *a, *p;
8214dd8b5abSYoshinobu Inoue 
8224dd8b5abSYoshinobu Inoue 			memset(&data_dest, 0, sizeof(data_dest));
8234dd8b5abSYoshinobu Inoue 			data_dest.su_len = sizeof(struct sockaddr_in6);
8244dd8b5abSYoshinobu Inoue 			data_dest.su_family = AF_INET6;
8254dd8b5abSYoshinobu Inoue 			p = (char *)&data_dest.su_port;
8267d0babdaSMaxim Konovalov 			p[0] = $39.i; p[1] = $41.i;
8274dd8b5abSYoshinobu Inoue 			a = (char *)&data_dest.su_sin6.sin6_addr;
8287d0babdaSMaxim Konovalov 			a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
8297d0babdaSMaxim Konovalov 			a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
8307d0babdaSMaxim Konovalov 			a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
8317d0babdaSMaxim Konovalov 			a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
8324dd8b5abSYoshinobu Inoue 			if (his_addr.su_family == AF_INET6) {
8334dd8b5abSYoshinobu Inoue 				/* XXX more sanity checks! */
8344dd8b5abSYoshinobu Inoue 				data_dest.su_sin6.sin6_scope_id =
8354dd8b5abSYoshinobu Inoue 					his_addr.su_sin6.sin6_scope_id;
8364dd8b5abSYoshinobu Inoue 			}
8377d0babdaSMaxim Konovalov 			if ($1.i != 6 || $3.i != 16 || $37.i != 2)
8384dd8b5abSYoshinobu Inoue 				memset(&data_dest, 0, sizeof(data_dest));
8394dd8b5abSYoshinobu Inoue 		}
8404dd8b5abSYoshinobu Inoue 	| NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
8414dd8b5abSYoshinobu Inoue 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
8424dd8b5abSYoshinobu Inoue 		NUMBER
8434dd8b5abSYoshinobu Inoue 		{
8444dd8b5abSYoshinobu Inoue 			char *a, *p;
8454dd8b5abSYoshinobu Inoue 
8464dd8b5abSYoshinobu Inoue 			memset(&data_dest, 0, sizeof(data_dest));
8474dd8b5abSYoshinobu Inoue 			data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
8484dd8b5abSYoshinobu Inoue 			data_dest.su_family = AF_INET;
8494dd8b5abSYoshinobu Inoue 			p = (char *)&data_dest.su_port;
8507d0babdaSMaxim Konovalov 			p[0] = $15.i; p[1] = $17.i;
8514dd8b5abSYoshinobu Inoue 			a = (char *)&data_dest.su_sin.sin_addr;
8527d0babdaSMaxim Konovalov 			a[0] =  $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
8537d0babdaSMaxim Konovalov 			if ($1.i != 4 || $3.i != 4 || $13.i != 2)
8544dd8b5abSYoshinobu Inoue 				memset(&data_dest, 0, sizeof(data_dest));
8554dd8b5abSYoshinobu Inoue 		}
8564dd8b5abSYoshinobu Inoue 	;
8574dd8b5abSYoshinobu Inoue 
858ea022d16SRodney W. Grimes form_code
859ea022d16SRodney W. Grimes 	: N
860ea022d16SRodney W. Grimes 		{
861ea022d16SRodney W. Grimes 			$$ = FORM_N;
862ea022d16SRodney W. Grimes 		}
863ea022d16SRodney W. Grimes 	| T
864ea022d16SRodney W. Grimes 		{
865ea022d16SRodney W. Grimes 			$$ = FORM_T;
866ea022d16SRodney W. Grimes 		}
867ea022d16SRodney W. Grimes 	| C
868ea022d16SRodney W. Grimes 		{
869ea022d16SRodney W. Grimes 			$$ = FORM_C;
870ea022d16SRodney W. Grimes 		}
871ea022d16SRodney W. Grimes 	;
872ea022d16SRodney W. Grimes 
873ea022d16SRodney W. Grimes type_code
874ea022d16SRodney W. Grimes 	: A
875ea022d16SRodney W. Grimes 		{
876ea022d16SRodney W. Grimes 			cmd_type = TYPE_A;
877ea022d16SRodney W. Grimes 			cmd_form = FORM_N;
878ea022d16SRodney W. Grimes 		}
879ea022d16SRodney W. Grimes 	| A SP form_code
880ea022d16SRodney W. Grimes 		{
881ea022d16SRodney W. Grimes 			cmd_type = TYPE_A;
882ea022d16SRodney W. Grimes 			cmd_form = $3;
883ea022d16SRodney W. Grimes 		}
884ea022d16SRodney W. Grimes 	| E
885ea022d16SRodney W. Grimes 		{
886ea022d16SRodney W. Grimes 			cmd_type = TYPE_E;
887ea022d16SRodney W. Grimes 			cmd_form = FORM_N;
888ea022d16SRodney W. Grimes 		}
889ea022d16SRodney W. Grimes 	| E SP form_code
890ea022d16SRodney W. Grimes 		{
891ea022d16SRodney W. Grimes 			cmd_type = TYPE_E;
892ea022d16SRodney W. Grimes 			cmd_form = $3;
893ea022d16SRodney W. Grimes 		}
894ea022d16SRodney W. Grimes 	| I
895ea022d16SRodney W. Grimes 		{
896ea022d16SRodney W. Grimes 			cmd_type = TYPE_I;
897ea022d16SRodney W. Grimes 		}
898ea022d16SRodney W. Grimes 	| L
899ea022d16SRodney W. Grimes 		{
900ea022d16SRodney W. Grimes 			cmd_type = TYPE_L;
90189fdc4e1SMike Barcroft 			cmd_bytesz = CHAR_BIT;
902ea022d16SRodney W. Grimes 		}
903ea022d16SRodney W. Grimes 	| L SP byte_size
904ea022d16SRodney W. Grimes 		{
905ea022d16SRodney W. Grimes 			cmd_type = TYPE_L;
906ea022d16SRodney W. Grimes 			cmd_bytesz = $3;
907ea022d16SRodney W. Grimes 		}
908ea022d16SRodney W. Grimes 		/* this is for a bug in the BBN ftp */
909ea022d16SRodney W. Grimes 	| L byte_size
910ea022d16SRodney W. Grimes 		{
911ea022d16SRodney W. Grimes 			cmd_type = TYPE_L;
912ea022d16SRodney W. Grimes 			cmd_bytesz = $2;
913ea022d16SRodney W. Grimes 		}
914ea022d16SRodney W. Grimes 	;
915ea022d16SRodney W. Grimes 
916ea022d16SRodney W. Grimes struct_code
917ea022d16SRodney W. Grimes 	: F
918ea022d16SRodney W. Grimes 		{
919ea022d16SRodney W. Grimes 			$$ = STRU_F;
920ea022d16SRodney W. Grimes 		}
921ea022d16SRodney W. Grimes 	| R
922ea022d16SRodney W. Grimes 		{
923ea022d16SRodney W. Grimes 			$$ = STRU_R;
924ea022d16SRodney W. Grimes 		}
925ea022d16SRodney W. Grimes 	| P
926ea022d16SRodney W. Grimes 		{
927ea022d16SRodney W. Grimes 			$$ = STRU_P;
928ea022d16SRodney W. Grimes 		}
929ea022d16SRodney W. Grimes 	;
930ea022d16SRodney W. Grimes 
931ea022d16SRodney W. Grimes mode_code
932ea022d16SRodney W. Grimes 	: S
933ea022d16SRodney W. Grimes 		{
934ea022d16SRodney W. Grimes 			$$ = MODE_S;
935ea022d16SRodney W. Grimes 		}
936ea022d16SRodney W. Grimes 	| B
937ea022d16SRodney W. Grimes 		{
938ea022d16SRodney W. Grimes 			$$ = MODE_B;
939ea022d16SRodney W. Grimes 		}
940ea022d16SRodney W. Grimes 	| C
941ea022d16SRodney W. Grimes 		{
942ea022d16SRodney W. Grimes 			$$ = MODE_C;
943ea022d16SRodney W. Grimes 		}
944ea022d16SRodney W. Grimes 	;
945ea022d16SRodney W. Grimes 
946ea022d16SRodney W. Grimes pathname
947ea022d16SRodney W. Grimes 	: pathstring
948ea022d16SRodney W. Grimes 		{
94970825609SPeter Wemm 			if (logged_in && $1) {
9506cfbc841SYaroslav Tykhiy 				char *p;
951ea022d16SRodney W. Grimes 
9526cfbc841SYaroslav Tykhiy 				/*
9536cfbc841SYaroslav Tykhiy 				 * Expand ~user manually since glob(3)
9546cfbc841SYaroslav Tykhiy 				 * will return the unexpanded pathname
9556cfbc841SYaroslav Tykhiy 				 * if the corresponding file/directory
9566cfbc841SYaroslav Tykhiy 				 * doesn't exist yet.  Using sole glob(3)
9576cfbc841SYaroslav Tykhiy 				 * would break natural commands like
9586cfbc841SYaroslav Tykhiy 				 * MKD ~user/newdir
9596cfbc841SYaroslav Tykhiy 				 * or
9606cfbc841SYaroslav Tykhiy 				 * RNTO ~/newfile
9616cfbc841SYaroslav Tykhiy 				 */
9626cfbc841SYaroslav Tykhiy 				if ((p = exptilde($1)) != NULL) {
9636cfbc841SYaroslav Tykhiy 					$$ = expglob(p);
9646cfbc841SYaroslav Tykhiy 					free(p);
9656cfbc841SYaroslav Tykhiy 				} else
966ea022d16SRodney W. Grimes 					$$ = NULL;
967ea022d16SRodney W. Grimes 				free($1);
968ea022d16SRodney W. Grimes 			} else
969ea022d16SRodney W. Grimes 				$$ = $1;
970ea022d16SRodney W. Grimes 		}
971ea022d16SRodney W. Grimes 	;
972ea022d16SRodney W. Grimes 
973ea022d16SRodney W. Grimes pathstring
974ea022d16SRodney W. Grimes 	: STRING
975ea022d16SRodney W. Grimes 	;
976ea022d16SRodney W. Grimes 
977ea022d16SRodney W. Grimes octal_number
978ea022d16SRodney W. Grimes 	: NUMBER
979ea022d16SRodney W. Grimes 		{
980ea022d16SRodney W. Grimes 			int ret, dec, multby, digit;
981ea022d16SRodney W. Grimes 
982ea022d16SRodney W. Grimes 			/*
983ea022d16SRodney W. Grimes 			 * Convert a number that was read as decimal number
984ea022d16SRodney W. Grimes 			 * to what it would be if it had been read as octal.
985ea022d16SRodney W. Grimes 			 */
9867d0babdaSMaxim Konovalov 			dec = $1.i;
987ea022d16SRodney W. Grimes 			multby = 1;
988ea022d16SRodney W. Grimes 			ret = 0;
989ea022d16SRodney W. Grimes 			while (dec) {
990ea022d16SRodney W. Grimes 				digit = dec%10;
991ea022d16SRodney W. Grimes 				if (digit > 7) {
992ea022d16SRodney W. Grimes 					ret = -1;
993ea022d16SRodney W. Grimes 					break;
994ea022d16SRodney W. Grimes 				}
995ea022d16SRodney W. Grimes 				ret += digit * multby;
996ea022d16SRodney W. Grimes 				multby *= 8;
997ea022d16SRodney W. Grimes 				dec /= 10;
998ea022d16SRodney W. Grimes 			}
999ea022d16SRodney W. Grimes 			$$ = ret;
1000ea022d16SRodney W. Grimes 		}
1001ea022d16SRodney W. Grimes 	;
1002ea022d16SRodney W. Grimes 
1003ea022d16SRodney W. Grimes 
1004ea022d16SRodney W. Grimes check_login
1005ea022d16SRodney W. Grimes 	: /* empty */
1006ea022d16SRodney W. Grimes 		{
1007a4b77a2aSPoul-Henning Kamp 		$$ = check_login1();
1008a4b77a2aSPoul-Henning Kamp 		}
1009a4b77a2aSPoul-Henning Kamp 	;
1010a4b77a2aSPoul-Henning Kamp 
1011a4b77a2aSPoul-Henning Kamp check_login_epsv
1012a4b77a2aSPoul-Henning Kamp 	: /* empty */
1013a4b77a2aSPoul-Henning Kamp 		{
1014a4b77a2aSPoul-Henning Kamp 		if (noepsv) {
101502c97492SYaroslav Tykhiy 			reply(500, "EPSV command disabled.");
1016ea022d16SRodney W. Grimes 			$$ = 0;
1017ea022d16SRodney W. Grimes 		}
1018a4b77a2aSPoul-Henning Kamp 		else
1019a4b77a2aSPoul-Henning Kamp 			$$ = check_login1();
1020a4b77a2aSPoul-Henning Kamp 		}
1021a4b77a2aSPoul-Henning Kamp 	;
1022a4b77a2aSPoul-Henning Kamp 
1023a4b77a2aSPoul-Henning Kamp check_login_ro
1024a4b77a2aSPoul-Henning Kamp 	: /* empty */
1025a4b77a2aSPoul-Henning Kamp 		{
1026a4b77a2aSPoul-Henning Kamp 		if (readonly) {
1027e22887cdSDag-Erling Smørgrav 			reply(550, "Permission denied.");
1028a4b77a2aSPoul-Henning Kamp 			$$ = 0;
1029a4b77a2aSPoul-Henning Kamp 		}
1030a4b77a2aSPoul-Henning Kamp 		else
1031a4b77a2aSPoul-Henning Kamp 			$$ = check_login1();
1032ea022d16SRodney W. Grimes 		}
1033ea022d16SRodney W. Grimes 	;
1034ea022d16SRodney W. Grimes 
1035ea022d16SRodney W. Grimes %%
1036ea022d16SRodney W. Grimes 
1037ea022d16SRodney W. Grimes #define	CMD	0	/* beginning of command */
1038ea022d16SRodney W. Grimes #define	ARGS	1	/* expect miscellaneous arguments */
1039ea022d16SRodney W. Grimes #define	STR1	2	/* expect SP followed by STRING */
1040ea022d16SRodney W. Grimes #define	STR2	3	/* expect STRING */
1041ea022d16SRodney W. Grimes #define	OSTR	4	/* optional SP then STRING */
10427d6505e6SBrian Feldman #define	ZSTR1	5	/* optional SP then optional STRING */
1043ea022d16SRodney W. Grimes #define	ZSTR2	6	/* optional STRING after SP */
1044ea022d16SRodney W. Grimes #define	SITECMD	7	/* SITE command */
1045ea022d16SRodney W. Grimes #define	NSTR	8	/* Number followed by a string */
1046ea022d16SRodney W. Grimes 
10476d3fe674SChris D. Faulhaber #define	MAXGLOBARGS	1000
10486d3fe674SChris D. Faulhaber 
1049781cfb93SYaroslav Tykhiy #define	MAXASIZE	10240	/* Deny ASCII SIZE on files larger than that */
1050781cfb93SYaroslav Tykhiy 
1051ea022d16SRodney W. Grimes struct tab {
1052ea022d16SRodney W. Grimes 	char	*name;
1053ea022d16SRodney W. Grimes 	short	token;
1054ea022d16SRodney W. Grimes 	short	state;
1055ea022d16SRodney W. Grimes 	short	implemented;	/* 1 if command is implemented */
1056ea022d16SRodney W. Grimes 	char	*help;
1057ea022d16SRodney W. Grimes };
1058ea022d16SRodney W. Grimes 
1059ea022d16SRodney W. Grimes struct tab cmdtab[] = {		/* In order defined in RFC 765 */
1060ea022d16SRodney W. Grimes 	{ "USER", USER, STR1, 1,	"<sp> username" },
10617d6505e6SBrian Feldman 	{ "PASS", PASS, ZSTR1, 1,	"[<sp> [password]]" },
1062ea022d16SRodney W. Grimes 	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
1063ea022d16SRodney W. Grimes 	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
1064ea022d16SRodney W. Grimes 	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
1065ea022d16SRodney W. Grimes 	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
1066e9b61cfeSYaroslav Tykhiy 	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4, b5" },
10674dd8b5abSYoshinobu Inoue 	{ "LPRT", LPRT, ARGS, 1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
10684dd8b5abSYoshinobu Inoue 	{ "EPRT", EPRT, STR1, 1,	"<sp> |af|addr|port|" },
1069ea022d16SRodney W. Grimes 	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
10704dd8b5abSYoshinobu Inoue 	{ "LPSV", LPSV, ARGS, 1,	"(set server in passive mode)" },
10714dd8b5abSYoshinobu Inoue 	{ "EPSV", EPSV, ARGS, 1,	"[<sp> af|ALL]" },
1072e9b61cfeSYaroslav Tykhiy 	{ "TYPE", TYPE, ARGS, 1,	"<sp> { A | E | I | L }" },
1073ea022d16SRodney W. Grimes 	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
1074ea022d16SRodney W. Grimes 	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
1075ea022d16SRodney W. Grimes 	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
1076ea022d16SRodney W. Grimes 	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
1077ea022d16SRodney W. Grimes 	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
1078ea022d16SRodney W. Grimes 	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
1079ea022d16SRodney W. Grimes 	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
1080ea022d16SRodney W. Grimes 	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
1081ea022d16SRodney W. Grimes 	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
1082ea022d16SRodney W. Grimes 	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
1083ea022d16SRodney W. Grimes 	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
1084ea022d16SRodney W. Grimes 	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
1085ea022d16SRodney W. Grimes 	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
1086ea022d16SRodney W. Grimes 	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
1087ea022d16SRodney W. Grimes 	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
1088ea022d16SRodney W. Grimes 	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
1089ea022d16SRodney W. Grimes 	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
1090ea022d16SRodney W. Grimes 	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
1091ea022d16SRodney W. Grimes 	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
1092ea022d16SRodney W. Grimes 	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
1093ea022d16SRodney W. Grimes 	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
1094ea022d16SRodney W. Grimes 	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
1095ea022d16SRodney W. Grimes 	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
1096ea022d16SRodney W. Grimes 	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
10972ea42282SYaroslav Tykhiy 	{ "FEAT", FEAT, ARGS, 1,	"(get extended features)" },
1098ea022d16SRodney W. Grimes 	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
1099ea022d16SRodney W. Grimes 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
1100ea022d16SRodney W. Grimes 	{ "NOOP", NOOP, ARGS, 1,	"" },
1101ea022d16SRodney W. Grimes 	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
1102ea022d16SRodney W. Grimes 	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
1103ea022d16SRodney W. Grimes 	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
1104ea022d16SRodney W. Grimes 	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
1105ea022d16SRodney W. Grimes 	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
1106ea022d16SRodney W. Grimes 	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
1107ea022d16SRodney W. Grimes 	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
1108ea022d16SRodney W. Grimes 	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
1109ea022d16SRodney W. Grimes 	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
1110ea022d16SRodney W. Grimes 	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
1111ea022d16SRodney W. Grimes 	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
1112ea022d16SRodney W. Grimes 	{ NULL,   0,    0,    0,	0 }
1113ea022d16SRodney W. Grimes };
1114ea022d16SRodney W. Grimes 
1115ea022d16SRodney W. Grimes struct tab sitetab[] = {
111653ba84a6SPoul-Henning Kamp 	{ "MD5", MDFIVE, STR1, 1,	"[ <sp> file-name ]" },
1117ea022d16SRodney W. Grimes 	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
1118ea022d16SRodney W. Grimes 	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
1119ea022d16SRodney W. Grimes 	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
1120ea022d16SRodney W. Grimes 	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
1121ea022d16SRodney W. Grimes 	{ NULL,   0,    0,    0,	0 }
1122ea022d16SRodney W. Grimes };
1123ea022d16SRodney W. Grimes 
1124e4bc453cSWarner Losh static char	*copy(char *);
11256cfbc841SYaroslav Tykhiy static char	*expglob(char *);
11266cfbc841SYaroslav Tykhiy static char	*exptilde(char *);
1127e4bc453cSWarner Losh static void	 help(struct tab *, char *);
1128ea022d16SRodney W. Grimes static struct tab *
1129e4bc453cSWarner Losh 		 lookup(struct tab *, char *);
1130e4bc453cSWarner Losh static int	 port_check(const char *);
113131ee80d8SYaroslav Tykhiy #ifdef INET6
1132e4bc453cSWarner Losh static int	 port_check_v6(const char *);
113331ee80d8SYaroslav Tykhiy #endif
1134e4bc453cSWarner Losh static void	 sizecmd(char *);
1135e4bc453cSWarner Losh static void	 toolong(int);
113631ee80d8SYaroslav Tykhiy #ifdef INET6
1137e4bc453cSWarner Losh static void	 v4map_data_dest(void);
113831ee80d8SYaroslav Tykhiy #endif
1139e4bc453cSWarner Losh static int	 yylex(void);
1140ea022d16SRodney W. Grimes 
1141ea022d16SRodney W. Grimes static struct tab *
1142e4bc453cSWarner Losh lookup(struct tab *p, char *cmd)
1143ea022d16SRodney W. Grimes {
1144ea022d16SRodney W. Grimes 
1145ea022d16SRodney W. Grimes 	for (; p->name != NULL; p++)
1146ea022d16SRodney W. Grimes 		if (strcmp(cmd, p->name) == 0)
1147ea022d16SRodney W. Grimes 			return (p);
1148ea022d16SRodney W. Grimes 	return (0);
1149ea022d16SRodney W. Grimes }
1150ea022d16SRodney W. Grimes 
1151ea022d16SRodney W. Grimes #include <arpa/telnet.h>
1152ea022d16SRodney W. Grimes 
1153ea022d16SRodney W. Grimes /*
1154f03ef840SBaptiste Daroussin  * get_line - a hacked up version of fgets to ignore TELNET escape codes.
1155ea022d16SRodney W. Grimes  */
1156f0b40b1cSColin Percival int
1157f03ef840SBaptiste Daroussin get_line(char *s, int n, FILE *iop)
1158ea022d16SRodney W. Grimes {
1159ea022d16SRodney W. Grimes 	int c;
1160ea022d16SRodney W. Grimes 	register char *cs;
1161e25d3184SYaroslav Tykhiy 	sigset_t sset, osset;
1162ea022d16SRodney W. Grimes 
1163ea022d16SRodney W. Grimes 	cs = s;
1164ea022d16SRodney W. Grimes /* tmpline may contain saved command from urgent mode interruption */
1165ea022d16SRodney W. Grimes 	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1166ea022d16SRodney W. Grimes 		*cs++ = tmpline[c];
1167ea022d16SRodney W. Grimes 		if (tmpline[c] == '\n') {
1168ea022d16SRodney W. Grimes 			*cs++ = '\0';
1169618b0bbaSMark Murray 			if (ftpdebug)
1170ea022d16SRodney W. Grimes 				syslog(LOG_DEBUG, "command: %s", s);
1171ea022d16SRodney W. Grimes 			tmpline[0] = '\0';
1172f0b40b1cSColin Percival 			return(0);
1173ea022d16SRodney W. Grimes 		}
1174ea022d16SRodney W. Grimes 		if (c == 0)
1175ea022d16SRodney W. Grimes 			tmpline[0] = '\0';
1176ea022d16SRodney W. Grimes 	}
1177e25d3184SYaroslav Tykhiy 	/* SIGURG would interrupt stdio if not blocked during the read loop */
1178e25d3184SYaroslav Tykhiy 	sigemptyset(&sset);
1179e25d3184SYaroslav Tykhiy 	sigaddset(&sset, SIGURG);
1180e25d3184SYaroslav Tykhiy 	sigprocmask(SIG_BLOCK, &sset, &osset);
1181ea022d16SRodney W. Grimes 	while ((c = getc(iop)) != EOF) {
1182ea022d16SRodney W. Grimes 		c &= 0377;
1183ea022d16SRodney W. Grimes 		if (c == IAC) {
118439b96ba7SYaroslav Tykhiy 			if ((c = getc(iop)) == EOF)
118539b96ba7SYaroslav Tykhiy 				goto got_eof;
1186ea022d16SRodney W. Grimes 			c &= 0377;
1187ea022d16SRodney W. Grimes 			switch (c) {
1188ea022d16SRodney W. Grimes 			case WILL:
1189ea022d16SRodney W. Grimes 			case WONT:
119039b96ba7SYaroslav Tykhiy 				if ((c = getc(iop)) == EOF)
119139b96ba7SYaroslav Tykhiy 					goto got_eof;
1192ea022d16SRodney W. Grimes 				printf("%c%c%c", IAC, DONT, 0377&c);
1193ea022d16SRodney W. Grimes 				(void) fflush(stdout);
1194ea022d16SRodney W. Grimes 				continue;
1195ea022d16SRodney W. Grimes 			case DO:
1196ea022d16SRodney W. Grimes 			case DONT:
119739b96ba7SYaroslav Tykhiy 				if ((c = getc(iop)) == EOF)
119839b96ba7SYaroslav Tykhiy 					goto got_eof;
1199ea022d16SRodney W. Grimes 				printf("%c%c%c", IAC, WONT, 0377&c);
1200ea022d16SRodney W. Grimes 				(void) fflush(stdout);
1201ea022d16SRodney W. Grimes 				continue;
1202ea022d16SRodney W. Grimes 			case IAC:
1203ea022d16SRodney W. Grimes 				break;
1204ea022d16SRodney W. Grimes 			default:
1205ea022d16SRodney W. Grimes 				continue;	/* ignore command */
1206ea022d16SRodney W. Grimes 			}
1207ea022d16SRodney W. Grimes 		}
1208ea022d16SRodney W. Grimes 		*cs++ = c;
1209f0b40b1cSColin Percival 		if (--n <= 0) {
1210f0b40b1cSColin Percival 			/*
1211f0b40b1cSColin Percival 			 * If command doesn't fit into buffer, discard the
1212f0b40b1cSColin Percival 			 * rest of the command and indicate truncation.
1213f0b40b1cSColin Percival 			 * This prevents the command to be split up into
1214f0b40b1cSColin Percival 			 * multiple commands.
1215f0b40b1cSColin Percival 			 */
1216f0b40b1cSColin Percival 			while (c != '\n' && (c = getc(iop)) != EOF)
1217f0b40b1cSColin Percival 				;
1218f0b40b1cSColin Percival 			return (-2);
1219f0b40b1cSColin Percival 		}
1220f0b40b1cSColin Percival 		if (c == '\n')
1221ea022d16SRodney W. Grimes 			break;
1222ea022d16SRodney W. Grimes 	}
122339b96ba7SYaroslav Tykhiy got_eof:
1224e25d3184SYaroslav Tykhiy 	sigprocmask(SIG_SETMASK, &osset, NULL);
1225ea022d16SRodney W. Grimes 	if (c == EOF && cs == s)
1226f0b40b1cSColin Percival 		return (-1);
1227ea022d16SRodney W. Grimes 	*cs++ = '\0';
1228618b0bbaSMark Murray 	if (ftpdebug) {
1229ea022d16SRodney W. Grimes 		if (!guest && strncasecmp("pass ", s, 5) == 0) {
1230ea022d16SRodney W. Grimes 			/* Don't syslog passwords */
1231ea022d16SRodney W. Grimes 			syslog(LOG_DEBUG, "command: %.5s ???", s);
1232ea022d16SRodney W. Grimes 		} else {
1233ea022d16SRodney W. Grimes 			register char *cp;
1234ea022d16SRodney W. Grimes 			register int len;
1235ea022d16SRodney W. Grimes 
1236ea022d16SRodney W. Grimes 			/* Don't syslog trailing CR-LF */
1237ea022d16SRodney W. Grimes 			len = strlen(s);
1238ea022d16SRodney W. Grimes 			cp = s + len - 1;
1239ea022d16SRodney W. Grimes 			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1240ea022d16SRodney W. Grimes 				--cp;
1241ea022d16SRodney W. Grimes 				--len;
1242ea022d16SRodney W. Grimes 			}
1243ea022d16SRodney W. Grimes 			syslog(LOG_DEBUG, "command: %.*s", len, s);
1244ea022d16SRodney W. Grimes 		}
1245ea022d16SRodney W. Grimes 	}
1246f0b40b1cSColin Percival 	return (0);
1247ea022d16SRodney W. Grimes }
1248ea022d16SRodney W. Grimes 
1249ea022d16SRodney W. Grimes static void
1250e4bc453cSWarner Losh toolong(int signo)
1251ea022d16SRodney W. Grimes {
1252ea022d16SRodney W. Grimes 
1253ea022d16SRodney W. Grimes 	reply(421,
1254ea022d16SRodney W. Grimes 	    "Timeout (%d seconds): closing control connection.", timeout);
1255ea022d16SRodney W. Grimes 	if (logging)
1256ea022d16SRodney W. Grimes 		syslog(LOG_INFO, "User %s timed out after %d seconds",
1257ea022d16SRodney W. Grimes 		    (pw ? pw -> pw_name : "unknown"), timeout);
1258ea022d16SRodney W. Grimes 	dologout(1);
1259ea022d16SRodney W. Grimes }
1260ea022d16SRodney W. Grimes 
1261ea022d16SRodney W. Grimes static int
1262e4bc453cSWarner Losh yylex(void)
1263ea022d16SRodney W. Grimes {
12644b82fc95SYaroslav Tykhiy 	static int cpos;
1265ea022d16SRodney W. Grimes 	char *cp, *cp2;
1266ea022d16SRodney W. Grimes 	struct tab *p;
1267ea022d16SRodney W. Grimes 	int n;
1268ea022d16SRodney W. Grimes 	char c;
1269ea022d16SRodney W. Grimes 
1270ea022d16SRodney W. Grimes 	for (;;) {
1271ea022d16SRodney W. Grimes 		switch (state) {
1272ea022d16SRodney W. Grimes 
1273ea022d16SRodney W. Grimes 		case CMD:
1274ea022d16SRodney W. Grimes 			(void) signal(SIGALRM, toolong);
1275e3765043SYaroslav Tykhiy 			(void) alarm(timeout);
1276f03ef840SBaptiste Daroussin 			n = get_line(cbuf, sizeof(cbuf)-1, stdin);
1277f0b40b1cSColin Percival 			if (n == -1) {
1278ea022d16SRodney W. Grimes 				reply(221, "You could at least say goodbye.");
1279ea022d16SRodney W. Grimes 				dologout(0);
1280f0b40b1cSColin Percival 			} else if (n == -2) {
1281f0b40b1cSColin Percival 				reply(500, "Command too long.");
1282f0b40b1cSColin Percival 				(void) alarm(0);
1283f0b40b1cSColin Percival 				continue;
1284ea022d16SRodney W. Grimes 			}
1285ea022d16SRodney W. Grimes 			(void) alarm(0);
1286ea022d16SRodney W. Grimes #ifdef SETPROCTITLE
1287d030d2d2SPoul-Henning Kamp 			if (strncasecmp(cbuf, "PASS", 4) != 0)
1288ea022d16SRodney W. Grimes 				setproctitle("%s: %s", proctitle, cbuf);
1289ea022d16SRodney W. Grimes #endif /* SETPROCTITLE */
1290ea022d16SRodney W. Grimes 			if ((cp = strchr(cbuf, '\r'))) {
1291ea022d16SRodney W. Grimes 				*cp++ = '\n';
1292ea022d16SRodney W. Grimes 				*cp = '\0';
1293ea022d16SRodney W. Grimes 			}
1294ea022d16SRodney W. Grimes 			if ((cp = strpbrk(cbuf, " \n")))
1295ea022d16SRodney W. Grimes 				cpos = cp - cbuf;
1296ea022d16SRodney W. Grimes 			if (cpos == 0)
1297ea022d16SRodney W. Grimes 				cpos = 4;
1298ea022d16SRodney W. Grimes 			c = cbuf[cpos];
1299ea022d16SRodney W. Grimes 			cbuf[cpos] = '\0';
1300ea022d16SRodney W. Grimes 			upper(cbuf);
1301ea022d16SRodney W. Grimes 			p = lookup(cmdtab, cbuf);
1302ea022d16SRodney W. Grimes 			cbuf[cpos] = c;
13030d78c1c0SPaul Traina 			if (p != 0) {
1304ea022d16SRodney W. Grimes 				yylval.s = p->name;
1305371348aeSYaroslav Tykhiy 				if (!p->implemented)
1306371348aeSYaroslav Tykhiy 					return (NOTIMPL); /* state remains CMD */
1307371348aeSYaroslav Tykhiy 				state = p->state;
1308ea022d16SRodney W. Grimes 				return (p->token);
1309ea022d16SRodney W. Grimes 			}
1310ea022d16SRodney W. Grimes 			break;
1311ea022d16SRodney W. Grimes 
1312ea022d16SRodney W. Grimes 		case SITECMD:
1313ea022d16SRodney W. Grimes 			if (cbuf[cpos] == ' ') {
1314ea022d16SRodney W. Grimes 				cpos++;
1315ea022d16SRodney W. Grimes 				return (SP);
1316ea022d16SRodney W. Grimes 			}
1317ea022d16SRodney W. Grimes 			cp = &cbuf[cpos];
1318ea022d16SRodney W. Grimes 			if ((cp2 = strpbrk(cp, " \n")))
1319ea022d16SRodney W. Grimes 				cpos = cp2 - cbuf;
1320ea022d16SRodney W. Grimes 			c = cbuf[cpos];
1321ea022d16SRodney W. Grimes 			cbuf[cpos] = '\0';
1322ea022d16SRodney W. Grimes 			upper(cp);
1323ea022d16SRodney W. Grimes 			p = lookup(sitetab, cp);
1324ea022d16SRodney W. Grimes 			cbuf[cpos] = c;
13259e53ab00SPaul Traina 			if (guest == 0 && p != 0) {
1326371348aeSYaroslav Tykhiy 				yylval.s = p->name;
1327371348aeSYaroslav Tykhiy 				if (!p->implemented) {
1328ea022d16SRodney W. Grimes 					state = CMD;
1329371348aeSYaroslav Tykhiy 					return (NOTIMPL);
1330ea022d16SRodney W. Grimes 				}
1331ea022d16SRodney W. Grimes 				state = p->state;
1332ea022d16SRodney W. Grimes 				return (p->token);
1333ea022d16SRodney W. Grimes 			}
1334ea022d16SRodney W. Grimes 			state = CMD;
1335ea022d16SRodney W. Grimes 			break;
1336ea022d16SRodney W. Grimes 
13377d6505e6SBrian Feldman 		case ZSTR1:
1338ea022d16SRodney W. Grimes 		case OSTR:
1339ea022d16SRodney W. Grimes 			if (cbuf[cpos] == '\n') {
1340ea022d16SRodney W. Grimes 				state = CMD;
1341ea022d16SRodney W. Grimes 				return (CRLF);
1342ea022d16SRodney W. Grimes 			}
1343ea022d16SRodney W. Grimes 			/* FALLTHROUGH */
1344ea022d16SRodney W. Grimes 
1345ea022d16SRodney W. Grimes 		case STR1:
1346ea022d16SRodney W. Grimes 		dostr1:
1347ea022d16SRodney W. Grimes 			if (cbuf[cpos] == ' ') {
1348ea022d16SRodney W. Grimes 				cpos++;
134909ef98c6SAlfred Perlstein 				state = state == OSTR ? STR2 : state+1;
1350ea022d16SRodney W. Grimes 				return (SP);
1351ea022d16SRodney W. Grimes 			}
1352ea022d16SRodney W. Grimes 			break;
1353ea022d16SRodney W. Grimes 
1354ea022d16SRodney W. Grimes 		case ZSTR2:
1355ea022d16SRodney W. Grimes 			if (cbuf[cpos] == '\n') {
1356ea022d16SRodney W. Grimes 				state = CMD;
1357ea022d16SRodney W. Grimes 				return (CRLF);
1358ea022d16SRodney W. Grimes 			}
1359ea022d16SRodney W. Grimes 			/* FALLTHROUGH */
1360ea022d16SRodney W. Grimes 
1361ea022d16SRodney W. Grimes 		case STR2:
1362ea022d16SRodney W. Grimes 			cp = &cbuf[cpos];
1363ea022d16SRodney W. Grimes 			n = strlen(cp);
1364ea022d16SRodney W. Grimes 			cpos += n - 1;
1365ea022d16SRodney W. Grimes 			/*
1366ea022d16SRodney W. Grimes 			 * Make sure the string is nonempty and \n terminated.
1367ea022d16SRodney W. Grimes 			 */
1368ea022d16SRodney W. Grimes 			if (n > 1 && cbuf[cpos] == '\n') {
1369ea022d16SRodney W. Grimes 				cbuf[cpos] = '\0';
1370ea022d16SRodney W. Grimes 				yylval.s = copy(cp);
1371ea022d16SRodney W. Grimes 				cbuf[cpos] = '\n';
1372ea022d16SRodney W. Grimes 				state = ARGS;
1373ea022d16SRodney W. Grimes 				return (STRING);
1374ea022d16SRodney W. Grimes 			}
1375ea022d16SRodney W. Grimes 			break;
1376ea022d16SRodney W. Grimes 
1377ea022d16SRodney W. Grimes 		case NSTR:
1378ea022d16SRodney W. Grimes 			if (cbuf[cpos] == ' ') {
1379ea022d16SRodney W. Grimes 				cpos++;
1380ea022d16SRodney W. Grimes 				return (SP);
1381ea022d16SRodney W. Grimes 			}
1382ea022d16SRodney W. Grimes 			if (isdigit(cbuf[cpos])) {
1383ea022d16SRodney W. Grimes 				cp = &cbuf[cpos];
1384ea022d16SRodney W. Grimes 				while (isdigit(cbuf[++cpos]))
1385ea022d16SRodney W. Grimes 					;
1386ea022d16SRodney W. Grimes 				c = cbuf[cpos];
1387ea022d16SRodney W. Grimes 				cbuf[cpos] = '\0';
13887d0babdaSMaxim Konovalov 				yylval.u.i = atoi(cp);
1389ea022d16SRodney W. Grimes 				cbuf[cpos] = c;
1390ea022d16SRodney W. Grimes 				state = STR1;
1391ea022d16SRodney W. Grimes 				return (NUMBER);
1392ea022d16SRodney W. Grimes 			}
1393ea022d16SRodney W. Grimes 			state = STR1;
1394ea022d16SRodney W. Grimes 			goto dostr1;
1395ea022d16SRodney W. Grimes 
1396ea022d16SRodney W. Grimes 		case ARGS:
1397ea022d16SRodney W. Grimes 			if (isdigit(cbuf[cpos])) {
1398ea022d16SRodney W. Grimes 				cp = &cbuf[cpos];
1399ea022d16SRodney W. Grimes 				while (isdigit(cbuf[++cpos]))
1400ea022d16SRodney W. Grimes 					;
1401ea022d16SRodney W. Grimes 				c = cbuf[cpos];
1402ea022d16SRodney W. Grimes 				cbuf[cpos] = '\0';
14037d0babdaSMaxim Konovalov 				yylval.u.i = atoi(cp);
14047e295315SYaroslav Tykhiy 				yylval.u.o = strtoull(cp, NULL, 10);
1405ea022d16SRodney W. Grimes 				cbuf[cpos] = c;
1406ea022d16SRodney W. Grimes 				return (NUMBER);
1407ea022d16SRodney W. Grimes 			}
14084dd8b5abSYoshinobu Inoue 			if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
14094dd8b5abSYoshinobu Inoue 			 && !isalnum(cbuf[cpos + 3])) {
14104dd8b5abSYoshinobu Inoue 				cpos += 3;
14114dd8b5abSYoshinobu Inoue 				return ALL;
14124dd8b5abSYoshinobu Inoue 			}
1413ea022d16SRodney W. Grimes 			switch (cbuf[cpos++]) {
1414ea022d16SRodney W. Grimes 
1415ea022d16SRodney W. Grimes 			case '\n':
1416ea022d16SRodney W. Grimes 				state = CMD;
1417ea022d16SRodney W. Grimes 				return (CRLF);
1418ea022d16SRodney W. Grimes 
1419ea022d16SRodney W. Grimes 			case ' ':
1420ea022d16SRodney W. Grimes 				return (SP);
1421ea022d16SRodney W. Grimes 
1422ea022d16SRodney W. Grimes 			case ',':
1423ea022d16SRodney W. Grimes 				return (COMMA);
1424ea022d16SRodney W. Grimes 
1425ea022d16SRodney W. Grimes 			case 'A':
1426ea022d16SRodney W. Grimes 			case 'a':
1427ea022d16SRodney W. Grimes 				return (A);
1428ea022d16SRodney W. Grimes 
1429ea022d16SRodney W. Grimes 			case 'B':
1430ea022d16SRodney W. Grimes 			case 'b':
1431ea022d16SRodney W. Grimes 				return (B);
1432ea022d16SRodney W. Grimes 
1433ea022d16SRodney W. Grimes 			case 'C':
1434ea022d16SRodney W. Grimes 			case 'c':
1435ea022d16SRodney W. Grimes 				return (C);
1436ea022d16SRodney W. Grimes 
1437ea022d16SRodney W. Grimes 			case 'E':
1438ea022d16SRodney W. Grimes 			case 'e':
1439ea022d16SRodney W. Grimes 				return (E);
1440ea022d16SRodney W. Grimes 
1441ea022d16SRodney W. Grimes 			case 'F':
1442ea022d16SRodney W. Grimes 			case 'f':
1443ea022d16SRodney W. Grimes 				return (F);
1444ea022d16SRodney W. Grimes 
1445ea022d16SRodney W. Grimes 			case 'I':
1446ea022d16SRodney W. Grimes 			case 'i':
1447ea022d16SRodney W. Grimes 				return (I);
1448ea022d16SRodney W. Grimes 
1449ea022d16SRodney W. Grimes 			case 'L':
1450ea022d16SRodney W. Grimes 			case 'l':
1451ea022d16SRodney W. Grimes 				return (L);
1452ea022d16SRodney W. Grimes 
1453ea022d16SRodney W. Grimes 			case 'N':
1454ea022d16SRodney W. Grimes 			case 'n':
1455ea022d16SRodney W. Grimes 				return (N);
1456ea022d16SRodney W. Grimes 
1457ea022d16SRodney W. Grimes 			case 'P':
1458ea022d16SRodney W. Grimes 			case 'p':
1459ea022d16SRodney W. Grimes 				return (P);
1460ea022d16SRodney W. Grimes 
1461ea022d16SRodney W. Grimes 			case 'R':
1462ea022d16SRodney W. Grimes 			case 'r':
1463ea022d16SRodney W. Grimes 				return (R);
1464ea022d16SRodney W. Grimes 
1465ea022d16SRodney W. Grimes 			case 'S':
1466ea022d16SRodney W. Grimes 			case 's':
1467ea022d16SRodney W. Grimes 				return (S);
1468ea022d16SRodney W. Grimes 
1469ea022d16SRodney W. Grimes 			case 'T':
1470ea022d16SRodney W. Grimes 			case 't':
1471ea022d16SRodney W. Grimes 				return (T);
1472ea022d16SRodney W. Grimes 
1473ea022d16SRodney W. Grimes 			}
1474ea022d16SRodney W. Grimes 			break;
1475ea022d16SRodney W. Grimes 
1476ea022d16SRodney W. Grimes 		default:
1477618b0bbaSMark Murray 			fatalerror("Unknown state in scanner.");
1478ea022d16SRodney W. Grimes 		}
1479ea022d16SRodney W. Grimes 		state = CMD;
14804b82fc95SYaroslav Tykhiy 		return (LEXERR);
1481ea022d16SRodney W. Grimes 	}
1482ea022d16SRodney W. Grimes }
1483ea022d16SRodney W. Grimes 
1484ea022d16SRodney W. Grimes void
1485e4bc453cSWarner Losh upper(char *s)
1486ea022d16SRodney W. Grimes {
1487ea022d16SRodney W. Grimes 	while (*s != '\0') {
1488ea022d16SRodney W. Grimes 		if (islower(*s))
1489ea022d16SRodney W. Grimes 			*s = toupper(*s);
1490ea022d16SRodney W. Grimes 		s++;
1491ea022d16SRodney W. Grimes 	}
1492ea022d16SRodney W. Grimes }
1493ea022d16SRodney W. Grimes 
1494ea022d16SRodney W. Grimes static char *
1495e4bc453cSWarner Losh copy(char *s)
1496ea022d16SRodney W. Grimes {
1497ea022d16SRodney W. Grimes 	char *p;
1498ea022d16SRodney W. Grimes 
1499e3765043SYaroslav Tykhiy 	p = malloc(strlen(s) + 1);
1500ea022d16SRodney W. Grimes 	if (p == NULL)
1501618b0bbaSMark Murray 		fatalerror("Ran out of memory.");
1502ea022d16SRodney W. Grimes 	(void) strcpy(p, s);
1503ea022d16SRodney W. Grimes 	return (p);
1504ea022d16SRodney W. Grimes }
1505ea022d16SRodney W. Grimes 
1506ea022d16SRodney W. Grimes static void
1507e4bc453cSWarner Losh help(struct tab *ctab, char *s)
1508ea022d16SRodney W. Grimes {
1509ea022d16SRodney W. Grimes 	struct tab *c;
1510ea022d16SRodney W. Grimes 	int width, NCMDS;
1511ea022d16SRodney W. Grimes 	char *type;
1512ea022d16SRodney W. Grimes 
1513ea022d16SRodney W. Grimes 	if (ctab == sitetab)
1514ea022d16SRodney W. Grimes 		type = "SITE ";
1515ea022d16SRodney W. Grimes 	else
1516ea022d16SRodney W. Grimes 		type = "";
1517ea022d16SRodney W. Grimes 	width = 0, NCMDS = 0;
1518ea022d16SRodney W. Grimes 	for (c = ctab; c->name != NULL; c++) {
1519ea022d16SRodney W. Grimes 		int len = strlen(c->name);
1520ea022d16SRodney W. Grimes 
1521ea022d16SRodney W. Grimes 		if (len > width)
1522ea022d16SRodney W. Grimes 			width = len;
1523ea022d16SRodney W. Grimes 		NCMDS++;
1524ea022d16SRodney W. Grimes 	}
1525ea022d16SRodney W. Grimes 	width = (width + 8) &~ 7;
1526ea022d16SRodney W. Grimes 	if (s == 0) {
1527ea022d16SRodney W. Grimes 		int i, j, w;
1528ea022d16SRodney W. Grimes 		int columns, lines;
1529ea022d16SRodney W. Grimes 
1530ea022d16SRodney W. Grimes 		lreply(214, "The following %scommands are recognized %s.",
1531ea022d16SRodney W. Grimes 		    type, "(* =>'s unimplemented)");
1532ea022d16SRodney W. Grimes 		columns = 76 / width;
1533ea022d16SRodney W. Grimes 		if (columns == 0)
1534ea022d16SRodney W. Grimes 			columns = 1;
1535ea022d16SRodney W. Grimes 		lines = (NCMDS + columns - 1) / columns;
1536ea022d16SRodney W. Grimes 		for (i = 0; i < lines; i++) {
1537ea022d16SRodney W. Grimes 			printf("   ");
1538ea022d16SRodney W. Grimes 			for (j = 0; j < columns; j++) {
1539ea022d16SRodney W. Grimes 				c = ctab + j * lines + i;
1540ea022d16SRodney W. Grimes 				printf("%s%c", c->name,
1541ea022d16SRodney W. Grimes 					c->implemented ? ' ' : '*');
1542ea022d16SRodney W. Grimes 				if (c + lines >= &ctab[NCMDS])
1543ea022d16SRodney W. Grimes 					break;
1544ea022d16SRodney W. Grimes 				w = strlen(c->name) + 1;
1545ea022d16SRodney W. Grimes 				while (w < width) {
1546ea022d16SRodney W. Grimes 					putchar(' ');
1547ea022d16SRodney W. Grimes 					w++;
1548ea022d16SRodney W. Grimes 				}
1549ea022d16SRodney W. Grimes 			}
1550ea022d16SRodney W. Grimes 			printf("\r\n");
1551ea022d16SRodney W. Grimes 		}
1552ea022d16SRodney W. Grimes 		(void) fflush(stdout);
1553c152df28SYaroslav Tykhiy 		if (hostinfo)
1554ea022d16SRodney W. Grimes 			reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1555c152df28SYaroslav Tykhiy 		else
1556c152df28SYaroslav Tykhiy 			reply(214, "End.");
1557ea022d16SRodney W. Grimes 		return;
1558ea022d16SRodney W. Grimes 	}
1559ea022d16SRodney W. Grimes 	upper(s);
1560ea022d16SRodney W. Grimes 	c = lookup(ctab, s);
1561aa5a9d3fSYaroslav Tykhiy 	if (c == NULL) {
1562ea022d16SRodney W. Grimes 		reply(502, "Unknown command %s.", s);
1563ea022d16SRodney W. Grimes 		return;
1564ea022d16SRodney W. Grimes 	}
1565ea022d16SRodney W. Grimes 	if (c->implemented)
1566ea022d16SRodney W. Grimes 		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1567ea022d16SRodney W. Grimes 	else
1568ea022d16SRodney W. Grimes 		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1569ea022d16SRodney W. Grimes 		    c->name, c->help);
1570ea022d16SRodney W. Grimes }
1571ea022d16SRodney W. Grimes 
1572ea022d16SRodney W. Grimes static void
1573e4bc453cSWarner Losh sizecmd(char *filename)
1574ea022d16SRodney W. Grimes {
1575ea022d16SRodney W. Grimes 	switch (type) {
1576ea022d16SRodney W. Grimes 	case TYPE_L:
1577ea022d16SRodney W. Grimes 	case TYPE_I: {
1578ea022d16SRodney W. Grimes 		struct stat stbuf;
15796200918dSDag-Erling Smørgrav 		if (stat(filename, &stbuf) < 0)
15806200918dSDag-Erling Smørgrav 			perror_reply(550, filename);
15816200918dSDag-Erling Smørgrav 		else if (!S_ISREG(stbuf.st_mode))
1582ea022d16SRodney W. Grimes 			reply(550, "%s: not a plain file.", filename);
1583ea022d16SRodney W. Grimes 		else
1584a57e1ef0SYaroslav Tykhiy 			reply(213, "%jd", (intmax_t)stbuf.st_size);
1585ea022d16SRodney W. Grimes 		break; }
1586ea022d16SRodney W. Grimes 	case TYPE_A: {
1587ea022d16SRodney W. Grimes 		FILE *fin;
1588ea022d16SRodney W. Grimes 		int c;
1589ea022d16SRodney W. Grimes 		off_t count;
1590ea022d16SRodney W. Grimes 		struct stat stbuf;
1591ea022d16SRodney W. Grimes 		fin = fopen(filename, "r");
1592ea022d16SRodney W. Grimes 		if (fin == NULL) {
1593ea022d16SRodney W. Grimes 			perror_reply(550, filename);
1594ea022d16SRodney W. Grimes 			return;
1595ea022d16SRodney W. Grimes 		}
15966200918dSDag-Erling Smørgrav 		if (fstat(fileno(fin), &stbuf) < 0) {
15976200918dSDag-Erling Smørgrav 			perror_reply(550, filename);
15986200918dSDag-Erling Smørgrav 			(void) fclose(fin);
15996200918dSDag-Erling Smørgrav 			return;
16006200918dSDag-Erling Smørgrav 		} else if (!S_ISREG(stbuf.st_mode)) {
1601ea022d16SRodney W. Grimes 			reply(550, "%s: not a plain file.", filename);
1602ea022d16SRodney W. Grimes 			(void) fclose(fin);
1603ea022d16SRodney W. Grimes 			return;
1604781cfb93SYaroslav Tykhiy 		} else if (stbuf.st_size > MAXASIZE) {
1605781cfb93SYaroslav Tykhiy 			reply(550, "%s: too large for type A SIZE.", filename);
1606781cfb93SYaroslav Tykhiy 			(void) fclose(fin);
1607781cfb93SYaroslav Tykhiy 			return;
1608ea022d16SRodney W. Grimes 		}
1609ea022d16SRodney W. Grimes 
1610ea022d16SRodney W. Grimes 		count = 0;
1611ea022d16SRodney W. Grimes 		while((c=getc(fin)) != EOF) {
1612ea022d16SRodney W. Grimes 			if (c == '\n')	/* will get expanded to \r\n */
1613ea022d16SRodney W. Grimes 				count++;
1614ea022d16SRodney W. Grimes 			count++;
1615ea022d16SRodney W. Grimes 		}
1616ea022d16SRodney W. Grimes 		(void) fclose(fin);
1617ea022d16SRodney W. Grimes 
1618a57e1ef0SYaroslav Tykhiy 		reply(213, "%jd", (intmax_t)count);
1619ea022d16SRodney W. Grimes 		break; }
1620ea022d16SRodney W. Grimes 	default:
16214454edd6SYaroslav Tykhiy 		reply(504, "SIZE not implemented for type %s.",
16224454edd6SYaroslav Tykhiy 		           typenames[type]);
1623ea022d16SRodney W. Grimes 	}
1624ea022d16SRodney W. Grimes }
16254dd8b5abSYoshinobu Inoue 
16264dd8b5abSYoshinobu Inoue /* Return 1, if port check is done. Return 0, if not yet. */
16274dd8b5abSYoshinobu Inoue static int
1628e4bc453cSWarner Losh port_check(const char *pcmd)
16294dd8b5abSYoshinobu Inoue {
16304dd8b5abSYoshinobu Inoue 	if (his_addr.su_family == AF_INET) {
16314dd8b5abSYoshinobu Inoue 		if (data_dest.su_family != AF_INET) {
16324dd8b5abSYoshinobu Inoue 			usedefault = 1;
16334dd8b5abSYoshinobu Inoue 			reply(500, "Invalid address rejected.");
16344dd8b5abSYoshinobu Inoue 			return 1;
16354dd8b5abSYoshinobu Inoue 		}
16364dd8b5abSYoshinobu Inoue 		if (paranoid &&
16374dd8b5abSYoshinobu Inoue 		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
16384dd8b5abSYoshinobu Inoue 		     memcmp(&data_dest.su_sin.sin_addr,
16394dd8b5abSYoshinobu Inoue 			    &his_addr.su_sin.sin_addr,
16404dd8b5abSYoshinobu Inoue 			    sizeof(data_dest.su_sin.sin_addr)))) {
16414dd8b5abSYoshinobu Inoue 			usedefault = 1;
16424dd8b5abSYoshinobu Inoue 			reply(500, "Illegal PORT range rejected.");
16434dd8b5abSYoshinobu Inoue 		} else {
16444dd8b5abSYoshinobu Inoue 			usedefault = 0;
16454dd8b5abSYoshinobu Inoue 			if (pdata >= 0) {
16464dd8b5abSYoshinobu Inoue 				(void) close(pdata);
16474dd8b5abSYoshinobu Inoue 				pdata = -1;
16484dd8b5abSYoshinobu Inoue 			}
16494dd8b5abSYoshinobu Inoue 			reply(200, "%s command successful.", pcmd);
16504dd8b5abSYoshinobu Inoue 		}
16514dd8b5abSYoshinobu Inoue 		return 1;
16524dd8b5abSYoshinobu Inoue 	}
16534dd8b5abSYoshinobu Inoue 	return 0;
16544dd8b5abSYoshinobu Inoue }
16554dd8b5abSYoshinobu Inoue 
1656a4b77a2aSPoul-Henning Kamp static int
1657e4bc453cSWarner Losh check_login1(void)
1658a4b77a2aSPoul-Henning Kamp {
1659a4b77a2aSPoul-Henning Kamp 	if (logged_in)
1660a4b77a2aSPoul-Henning Kamp 		return 1;
1661a4b77a2aSPoul-Henning Kamp 	else {
1662a4b77a2aSPoul-Henning Kamp 		reply(530, "Please login with USER and PASS.");
1663a4b77a2aSPoul-Henning Kamp 		return 0;
1664a4b77a2aSPoul-Henning Kamp 	}
1665a4b77a2aSPoul-Henning Kamp }
1666a4b77a2aSPoul-Henning Kamp 
16676cfbc841SYaroslav Tykhiy /*
16686cfbc841SYaroslav Tykhiy  * Replace leading "~user" in a pathname by the user's login directory.
16696cfbc841SYaroslav Tykhiy  * Returned string will be in a freshly malloced buffer unless it's NULL.
16706cfbc841SYaroslav Tykhiy  */
16716cfbc841SYaroslav Tykhiy static char *
16726cfbc841SYaroslav Tykhiy exptilde(char *s)
16736cfbc841SYaroslav Tykhiy {
16746cfbc841SYaroslav Tykhiy 	char *p, *q;
16756cfbc841SYaroslav Tykhiy 	char *path, *user;
16766cfbc841SYaroslav Tykhiy 	struct passwd *ppw;
16776cfbc841SYaroslav Tykhiy 
16786cfbc841SYaroslav Tykhiy 	if ((p = strdup(s)) == NULL)
16796cfbc841SYaroslav Tykhiy 		return (NULL);
16806cfbc841SYaroslav Tykhiy 	if (*p != '~')
16816cfbc841SYaroslav Tykhiy 		return (p);
16826cfbc841SYaroslav Tykhiy 
16836cfbc841SYaroslav Tykhiy 	user = p + 1;	/* skip tilde */
16846cfbc841SYaroslav Tykhiy 	if ((path = strchr(p, '/')) != NULL)
16856cfbc841SYaroslav Tykhiy 		*(path++) = '\0'; /* separate ~user from the rest of path */
168631f77a4bSYaroslav Tykhiy 	if (*user == '\0') /* no user specified, use the current user */
168731f77a4bSYaroslav Tykhiy 		user = pw->pw_name;
168831f77a4bSYaroslav Tykhiy 	/* read passwd even for the current user since we may be chrooted */
168931f77a4bSYaroslav Tykhiy 	if ((ppw = getpwnam(user)) != NULL) {
16906cfbc841SYaroslav Tykhiy 		/* user found, substitute login directory for ~user */
16916cfbc841SYaroslav Tykhiy 		if (path)
16926cfbc841SYaroslav Tykhiy 			asprintf(&q, "%s/%s", ppw->pw_dir, path);
16936cfbc841SYaroslav Tykhiy 		else
16946cfbc841SYaroslav Tykhiy 			q = strdup(ppw->pw_dir);
16956cfbc841SYaroslav Tykhiy 		free(p);
16966cfbc841SYaroslav Tykhiy 		p = q;
16976cfbc841SYaroslav Tykhiy 	} else {
16986cfbc841SYaroslav Tykhiy 		/* user not found, undo the damage */
16996cfbc841SYaroslav Tykhiy 		if (path)
17006cfbc841SYaroslav Tykhiy 			path[-1] = '/';
17016cfbc841SYaroslav Tykhiy 	}
17026cfbc841SYaroslav Tykhiy 	return (p);
17036cfbc841SYaroslav Tykhiy }
17046cfbc841SYaroslav Tykhiy 
17056cfbc841SYaroslav Tykhiy /*
17066cfbc841SYaroslav Tykhiy  * Expand glob(3) patterns possibly present in a pathname.
17076cfbc841SYaroslav Tykhiy  * Avoid expanding to a pathname including '\r' or '\n' in order to
17086cfbc841SYaroslav Tykhiy  * not disrupt the FTP protocol.
17096cfbc841SYaroslav Tykhiy  * The expansion found must be unique.
17101acf0dbaSUlrich Spörlein  * Return the result as a malloced string, or NULL if an error occurred.
17116cfbc841SYaroslav Tykhiy  *
17126cfbc841SYaroslav Tykhiy  * Problem: this production is used for all pathname
17136cfbc841SYaroslav Tykhiy  * processing, but only gives a 550 error reply.
17146cfbc841SYaroslav Tykhiy  * This is a valid reply in some cases but not in others.
17156cfbc841SYaroslav Tykhiy  */
17166cfbc841SYaroslav Tykhiy static char *
17176cfbc841SYaroslav Tykhiy expglob(char *s)
17186cfbc841SYaroslav Tykhiy {
17196cfbc841SYaroslav Tykhiy 	char *p, **pp, *rval;
17206cfbc841SYaroslav Tykhiy 	int flags = GLOB_BRACE | GLOB_NOCHECK;
17216cfbc841SYaroslav Tykhiy 	int n;
17226cfbc841SYaroslav Tykhiy 	glob_t gl;
17236cfbc841SYaroslav Tykhiy 
17246cfbc841SYaroslav Tykhiy 	memset(&gl, 0, sizeof(gl));
17256cfbc841SYaroslav Tykhiy 	flags |= GLOB_LIMIT;
17266cfbc841SYaroslav Tykhiy 	gl.gl_matchc = MAXGLOBARGS;
17276cfbc841SYaroslav Tykhiy 	if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) {
17286cfbc841SYaroslav Tykhiy 		for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++)
17296cfbc841SYaroslav Tykhiy 			if (*(*pp + strcspn(*pp, "\r\n")) == '\0') {
17306cfbc841SYaroslav Tykhiy 				p = *pp;
17316cfbc841SYaroslav Tykhiy 				n++;
17326cfbc841SYaroslav Tykhiy 			}
17336cfbc841SYaroslav Tykhiy 		if (n == 0)
17346cfbc841SYaroslav Tykhiy 			rval = strdup(s);
17356cfbc841SYaroslav Tykhiy 		else if (n == 1)
17366cfbc841SYaroslav Tykhiy 			rval = strdup(p);
17376cfbc841SYaroslav Tykhiy 		else {
173802c97492SYaroslav Tykhiy 			reply(550, "Wildcard is ambiguous.");
17396cfbc841SYaroslav Tykhiy 			rval = NULL;
17406cfbc841SYaroslav Tykhiy 		}
17416cfbc841SYaroslav Tykhiy 	} else {
174202c97492SYaroslav Tykhiy 		reply(550, "Wildcard expansion error.");
17436cfbc841SYaroslav Tykhiy 		rval = NULL;
17446cfbc841SYaroslav Tykhiy 	}
17456cfbc841SYaroslav Tykhiy 	globfree(&gl);
17466cfbc841SYaroslav Tykhiy 	return (rval);
17476cfbc841SYaroslav Tykhiy }
17486cfbc841SYaroslav Tykhiy 
17494dd8b5abSYoshinobu Inoue #ifdef INET6
17504dd8b5abSYoshinobu Inoue /* Return 1, if port check is done. Return 0, if not yet. */
17514dd8b5abSYoshinobu Inoue static int
1752e4bc453cSWarner Losh port_check_v6(const char *pcmd)
17534dd8b5abSYoshinobu Inoue {
17544dd8b5abSYoshinobu Inoue 	if (his_addr.su_family == AF_INET6) {
17554dd8b5abSYoshinobu Inoue 		if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
17564dd8b5abSYoshinobu Inoue 			/* Convert data_dest into v4 mapped sockaddr.*/
17574dd8b5abSYoshinobu Inoue 			v4map_data_dest();
17584dd8b5abSYoshinobu Inoue 		if (data_dest.su_family != AF_INET6) {
17594dd8b5abSYoshinobu Inoue 			usedefault = 1;
17604dd8b5abSYoshinobu Inoue 			reply(500, "Invalid address rejected.");
17614dd8b5abSYoshinobu Inoue 			return 1;
17624dd8b5abSYoshinobu Inoue 		}
17634dd8b5abSYoshinobu Inoue 		if (paranoid &&
17644dd8b5abSYoshinobu Inoue 		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
17654dd8b5abSYoshinobu Inoue 		     memcmp(&data_dest.su_sin6.sin6_addr,
17664dd8b5abSYoshinobu Inoue 			    &his_addr.su_sin6.sin6_addr,
17674dd8b5abSYoshinobu Inoue 			    sizeof(data_dest.su_sin6.sin6_addr)))) {
17684dd8b5abSYoshinobu Inoue 			usedefault = 1;
17694dd8b5abSYoshinobu Inoue 			reply(500, "Illegal PORT range rejected.");
17704dd8b5abSYoshinobu Inoue 		} else {
17714dd8b5abSYoshinobu Inoue 			usedefault = 0;
17724dd8b5abSYoshinobu Inoue 			if (pdata >= 0) {
17734dd8b5abSYoshinobu Inoue 				(void) close(pdata);
17744dd8b5abSYoshinobu Inoue 				pdata = -1;
17754dd8b5abSYoshinobu Inoue 			}
17764dd8b5abSYoshinobu Inoue 			reply(200, "%s command successful.", pcmd);
17774dd8b5abSYoshinobu Inoue 		}
17784dd8b5abSYoshinobu Inoue 		return 1;
17794dd8b5abSYoshinobu Inoue 	}
17804dd8b5abSYoshinobu Inoue 	return 0;
17814dd8b5abSYoshinobu Inoue }
17824dd8b5abSYoshinobu Inoue 
17834dd8b5abSYoshinobu Inoue static void
1784e4bc453cSWarner Losh v4map_data_dest(void)
17854dd8b5abSYoshinobu Inoue {
17864dd8b5abSYoshinobu Inoue 	struct in_addr savedaddr;
17874dd8b5abSYoshinobu Inoue 	int savedport;
17884dd8b5abSYoshinobu Inoue 
17894dd8b5abSYoshinobu Inoue 	if (data_dest.su_family != AF_INET) {
17904dd8b5abSYoshinobu Inoue 		usedefault = 1;
17914dd8b5abSYoshinobu Inoue 		reply(500, "Invalid address rejected.");
17924dd8b5abSYoshinobu Inoue 		return;
17934dd8b5abSYoshinobu Inoue 	}
17944dd8b5abSYoshinobu Inoue 
17954dd8b5abSYoshinobu Inoue 	savedaddr = data_dest.su_sin.sin_addr;
17964dd8b5abSYoshinobu Inoue 	savedport = data_dest.su_port;
17974dd8b5abSYoshinobu Inoue 
17984dd8b5abSYoshinobu Inoue 	memset(&data_dest, 0, sizeof(data_dest));
17994dd8b5abSYoshinobu Inoue 	data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
18004dd8b5abSYoshinobu Inoue 	data_dest.su_sin6.sin6_family = AF_INET6;
18014dd8b5abSYoshinobu Inoue 	data_dest.su_sin6.sin6_port = savedport;
18024dd8b5abSYoshinobu Inoue 	memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
18034dd8b5abSYoshinobu Inoue 	memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
18044dd8b5abSYoshinobu Inoue 	       (caddr_t)&savedaddr, sizeof(savedaddr));
18054dd8b5abSYoshinobu Inoue }
18064dd8b5abSYoshinobu Inoue #endif
1807