1234313Sbapt/*	$NetBSD: ftpcmd.y,v 1.84 2006/02/01 14:20:12 christos Exp $	*/
2263020Sbapt
3257353Sbdrewery/*-
4234313Sbapt * Copyright (c) 1997-2005 The NetBSD Foundation, Inc.
5234313Sbapt * All rights reserved.
6234313Sbapt *
7234313Sbapt * This code is derived from software contributed to The NetBSD Foundation
8234313Sbapt * by Luke Mewburn.
9234313Sbapt *
10234313Sbapt * Redistribution and use in source and binary forms, with or without
11234313Sbapt * modification, are permitted provided that the following conditions
12234313Sbapt * are met:
13234313Sbapt * 1. Redistributions of source code must retain the above copyright
14234313Sbapt *    notice, this list of conditions and the following disclaimer.
15234313Sbapt * 2. Redistributions in binary form must reproduce the above copyright
16234313Sbapt *    notice, this list of conditions and the following disclaimer in the
17234313Sbapt *    documentation and/or other materials provided with the distribution.
18234313Sbapt * 3. All advertising materials mentioning features or use of this software
19234313Sbapt *    must display the following acknowledgement:
20234313Sbapt *        This product includes software developed by the NetBSD
21234313Sbapt *        Foundation, Inc. and its contributors.
22234313Sbapt * 4. Neither the name of The NetBSD Foundation nor the names of its
23234313Sbapt *    contributors may be used to endorse or promote products derived
24234313Sbapt *    from this software without specific prior written permission.
25234313Sbapt *
26234313Sbapt * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27234313Sbapt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28234313Sbapt * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29234313Sbapt * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30234313Sbapt * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31234313Sbapt * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32257353Sbdrewery * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33257353Sbdrewery * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34257353Sbdrewery * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35234322Sbapt * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36234313Sbapt * POSSIBILITY OF SUCH DAMAGE.
37257353Sbdrewery */
38234313Sbapt
39234313Sbapt/*
40257353Sbdrewery * Copyright (c) 1985, 1988, 1993, 1994
41234313Sbapt *	The Regents of the University of California.  All rights reserved.
42234313Sbapt *
43257309Sbapt * Redistribution and use in source and binary forms, with or without
44234322Sbapt * modification, are permitted provided that the following conditions
45234351Sbapt * are met:
46247841Sbapt * 1. Redistributions of source code must retain the above copyright
47234313Sbapt *    notice, this list of conditions and the following disclaimer.
48234313Sbapt * 2. Redistributions in binary form must reproduce the above copyright
49234313Sbapt *    notice, this list of conditions and the following disclaimer in the
50234313Sbapt *    documentation and/or other materials provided with the distribution.
51263020Sbapt * 3. Neither the name of the University nor the names of its contributors
52234313Sbapt *    may be used to endorse or promote products derived from this software
53257353Sbdrewery *    without specific prior written permission.
54257353Sbdrewery *
55257353Sbdrewery * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56243883Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57247841Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58234313Sbapt * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59257353Sbdrewery * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60257353Sbdrewery * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61257353Sbdrewery * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62257353Sbdrewery * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63257353Sbdrewery * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64257353Sbdrewery * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65257353Sbdrewery * SUCH DAMAGE.
66257353Sbdrewery *
67257353Sbdrewery *	@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94
68287872Sdelphij */
69287872Sdelphij
70287872Sdelphij/*
71287872Sdelphij * Grammar for FTP commands.
72287872Sdelphij * See RFC 959.
73257353Sbdrewery */
74257353Sbdrewery
75257353Sbdrewery%{
76257353Sbdrewery#include <sys/cdefs.h>
77257353Sbdrewery
78257353Sbdrewery#ifndef lint
79257353Sbdrewery#if 0
80257353Sbdrewerystatic char sccsid[] = "@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94";
81257353Sbdrewery#else
82257353Sbdrewery__RCSID("$NetBSD: ftpcmd.y,v 1.84 2006/02/01 14:20:12 christos Exp $");
83257353Sbdrewery#endif
84257353Sbdrewery#endif /* not lint */
85257353Sbdrewery
86257353Sbdrewery#include <sys/param.h>
87234313Sbapt#include <sys/socket.h>
88234313Sbapt#include <sys/stat.h>
89234313Sbapt
90234313Sbapt#include <netinet/in.h>
91234313Sbapt#include <arpa/ftp.h>
92234313Sbapt#include <arpa/inet.h>
93234313Sbapt
94234313Sbapt#include <ctype.h>
95234351Sbapt#include <errno.h>
96234313Sbapt#include <pwd.h>
97234351Sbapt#include <stdio.h>
98234351Sbapt#include <stdlib.h>
99234351Sbapt#include <string.h>
100234351Sbapt#include <syslog.h>
101247060Sbapt#include <time.h>
102234313Sbapt#include <tzfile.h>
103234313Sbapt#include <unistd.h>
104234351Sbapt#include <netdb.h>
105234351Sbapt
106234351Sbapt#ifdef KERBEROS5
107234351Sbapt#include <krb5/krb5.h>
108234313Sbapt#endif
109234313Sbapt
110234351Sbapt#include "extern.h"
111234313Sbapt#include "version.h"
112234313Sbapt
113234313Sbaptstatic	int cmd_type;
114234313Sbaptstatic	int cmd_form;
115234313Sbaptstatic	int cmd_bytesz;
116234313Sbapt
117234313Sbaptchar	cbuf[FTP_BUFLEN];
118234313Sbaptchar	*cmdp;
119234313Sbaptchar	*fromname;
120234313Sbapt
121234313Sbaptextern int	epsvall;
122234322Sbaptstruct tab	sitetab[];
123234322Sbapt
124234322Sbaptstatic	int	check_write(const char *, int);
125234351Sbaptstatic	void	help(struct tab *, const char *);
126234313Sbaptstatic	void	port_check(const char *, int);
127234313Sbapt	int	yylex(void);
128234313Sbapt
129234313Sbapt%}
130234351Sbapt
131234351Sbapt%union {
132234351Sbapt	struct {
133269937Sgavin		LLT	ll;
134269937Sgavin		int	i;
135234313Sbapt	} u;
136234313Sbapt	char   *s;
137247060Sbapt}
138234322Sbapt
139234313Sbapt%token
140234313Sbapt	A	B	C	E	F	I
141234313Sbapt	L	N	P	R	S	T
142234313Sbapt
143257632Sbdrewery	SP	CRLF	COMMA	ALL
144234313Sbapt
145234313Sbapt	USER	PASS	ACCT	CWD	CDUP	SMNT
146234313Sbapt	QUIT	REIN	PORT	PASV	TYPE	STRU
147234313Sbapt	MODE	RETR	STOR	STOU	APPE	ALLO
148234313Sbapt	REST	RNFR	RNTO	ABOR	DELE	RMD
149234351Sbapt	MKD	PWD	LIST	NLST	SITE	SYST
150234351Sbapt	STAT	HELP	NOOP
151234351Sbapt
152257632Sbdrewery	AUTH	ADAT	PROT	PBSZ	CCC	MIC
153257632Sbdrewery	CONF	ENC
154257632Sbdrewery
155257632Sbdrewery	FEAT	OPTS
156257632Sbdrewery
157257632Sbdrewery	SIZE	MDTM	MLST	MLSD
158234351Sbapt
159234351Sbapt	LPRT	LPSV	EPRT	EPSV
160234351Sbapt
161234313Sbapt	MAIL	MLFL	MRCP	MRSQ	MSAM	MSND
162234313Sbapt	MSOM
163234351Sbapt
164234313Sbapt	CHMOD	IDLE	RATEGET	RATEPUT	UMASK
165234313Sbapt
166234313Sbapt	LEXERR
167234351Sbapt
168234351Sbapt%token	<s> STRING
169234351Sbapt%token	<u> NUMBER
170234351Sbapt
171234351Sbapt%type	<u.i> check_login octal_number byte_size
172234313Sbapt%type	<u.i> struct_code mode_code type_code form_code decimal_integer
173234313Sbapt%type	<s> pathstring pathname password username
174234313Sbapt%type	<s> mechanism_name base64data prot_code
175257353Sbdrewery
176234313Sbapt%start	cmd_sel
177243883Sbapt
178257353Sbdrewery%%
179257353Sbdrewery
180234313Sbaptcmd_sel
181243883Sbapt	: cmd
182257353Sbdrewery		{
183257353Sbdrewery			REASSIGN(fromname, NULL);
184283793Sbapt			restart_point = (off_t) 0;
185257353Sbdrewery		}
186243883Sbapt
187257353Sbdrewery	| rcmd
188234313Sbapt
189243883Sbapt	;
190257353Sbdrewery
191234313Sbaptcmd
192234313Sbapt						/* RFC 959 */
193257353Sbdrewery	: USER SP username CRLF
194257353Sbdrewery		{
195247841Sbapt			user($3);
196247841Sbapt			free($3);
197247841Sbapt		}
198257328Sbdrewery
199257353Sbdrewery	| PASS SP password CRLF
200234313Sbapt		{
201234322Sbapt			pass($3);
202234313Sbapt			memset($3, 0, strlen($3));
203234313Sbapt			free($3);
204243883Sbapt		}
205234313Sbapt
206278564Sbapt	| CWD check_login CRLF
207278564Sbapt		{
208278564Sbapt			if ($2)
209278564Sbapt				cwd(homedir);
210278564Sbapt		}
211243883Sbapt
212243883Sbapt	| CWD check_login SP pathname CRLF
213247841Sbapt		{
214247841Sbapt			if ($2 && $4 != NULL)
215243883Sbapt				cwd($4);
216243883Sbapt			if ($4 != NULL)
217243883Sbapt				free($4);
218243883Sbapt		}
219243883Sbapt
220243883Sbapt	| CDUP check_login CRLF
221243883Sbapt		{
222257309Sbapt			if ($2)
223243883Sbapt				cwd("..");
224257309Sbapt		}
225257309Sbapt
226243883Sbapt	| QUIT CRLF
227243883Sbapt		{
228243883Sbapt			if (logged_in) {
229243883Sbapt				reply(-221, "%s", "");
230243883Sbapt				reply(0,
231243883Sbapt "Data traffic for this session was " LLF " byte%s in " LLF " file%s.",
232243883Sbapt				    (LLT)total_data, PLURAL(total_data),
233243883Sbapt				    (LLT)total_files, PLURAL(total_files));
234243883Sbapt				reply(0,
235243883Sbapt "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.",
236243883Sbapt				    (LLT)total_bytes, PLURAL(total_bytes),
237243883Sbapt				    (LLT)total_xfers, PLURAL(total_xfers));
238243883Sbapt			}
239243883Sbapt			reply(221,
240243883Sbapt			    "Thank you for using the FTP service on %s.",
241243883Sbapt			    hostname);
242283793Sbapt			if (logged_in && logging) {
243234313Sbapt				syslog(LOG_INFO,
244234313Sbapt		"Data traffic: " LLF " byte%s in " LLF " file%s",
245257353Sbdrewery				    (LLT)total_data, PLURAL(total_data),
246234313Sbapt				    (LLT)total_files, PLURAL(total_files));
247283793Sbapt				syslog(LOG_INFO,
248234313Sbapt		"Total traffic: " LLF " byte%s in " LLF " transfer%s",
249283793Sbapt				    (LLT)total_bytes, PLURAL(total_bytes),
250283793Sbapt				    (LLT)total_xfers, PLURAL(total_xfers));
251283793Sbapt			}
252234313Sbapt
253234313Sbapt			dologout(0);
254234351Sbapt		}
255234351Sbapt
256234313Sbapt	| PORT check_login SP host_port CRLF
257257353Sbdrewery		{
258257353Sbdrewery			if ($2)
259257353Sbdrewery				port_check("PORT", AF_INET);
260257353Sbdrewery		}
261257353Sbdrewery
262257353Sbdrewery	| LPRT check_login SP host_long_port4 CRLF
263257353Sbdrewery		{
264257353Sbdrewery			if ($2)
265257353Sbdrewery				port_check("LPRT", AF_INET);
266257353Sbdrewery		}
267257353Sbdrewery
268257353Sbdrewery	| LPRT check_login SP host_long_port6 CRLF
269257353Sbdrewery		{
270257353Sbdrewery#ifdef INET6
271257353Sbdrewery			if ($2)
272257353Sbdrewery				port_check("LPRT", AF_INET6);
273257353Sbdrewery#else
274263020Sbapt			reply(500, "IPv6 support not available.");
275257353Sbdrewery#endif
276268896Sbapt		}
277263020Sbapt
278263020Sbapt	| EPRT check_login SP STRING CRLF
279257353Sbdrewery		{
280257353Sbdrewery			if ($2) {
281257353Sbdrewery				if (extended_port($4) == 0)
282257353Sbdrewery					port_check("EPRT", -1);
283257353Sbdrewery			}
284263020Sbapt			free($4);
285263020Sbapt		}
286263020Sbapt
287257353Sbdrewery	| PASV check_login CRLF
288263020Sbapt		{
289263020Sbapt			if ($2) {
290263020Sbapt				if (CURCLASS_FLAGS_ISSET(passive))
291257353Sbdrewery					passive();
292263020Sbapt				else
293263020Sbapt					reply(500, "PASV mode not available.");
294257353Sbdrewery			}
295257353Sbdrewery		}
296257353Sbdrewery
297257353Sbdrewery	| LPSV check_login CRLF
298257353Sbdrewery		{
299257353Sbdrewery			if ($2) {
300257353Sbdrewery				if (CURCLASS_FLAGS_ISSET(passive)) {
301257353Sbdrewery					if (epsvall)
302257353Sbdrewery						reply(501,
303257353Sbdrewery						    "LPSV disallowed after EPSV ALL");
304257353Sbdrewery					else
305263020Sbapt						long_passive("LPSV", PF_UNSPEC);
306257353Sbdrewery				} else
307257353Sbdrewery					reply(500, "LPSV mode not available.");
308257353Sbdrewery			}
309257353Sbdrewery		}
310257353Sbdrewery
311257353Sbdrewery	| EPSV check_login SP NUMBER CRLF
312257353Sbdrewery		{
313257353Sbdrewery			if ($2) {
314257353Sbdrewery				if (CURCLASS_FLAGS_ISSET(passive))
315257353Sbdrewery					long_passive("EPSV",
316257353Sbdrewery					    epsvproto2af($4.i));
317257353Sbdrewery				else
318257353Sbdrewery					reply(500, "EPSV mode not available.");
319258126Sglebius			}
320257353Sbdrewery		}
321258126Sglebius
322283788Sbapt	| EPSV check_login SP ALL CRLF
323257353Sbdrewery		{
324257353Sbdrewery			if ($2) {
325257353Sbdrewery				if (CURCLASS_FLAGS_ISSET(passive)) {
326257353Sbdrewery					reply(200,
327257353Sbdrewery					    "EPSV ALL command successful.");
328257353Sbdrewery					epsvall++;
329257353Sbdrewery				} else
330257353Sbdrewery					reply(500, "EPSV mode not available.");
331263020Sbapt			}
332263020Sbapt		}
333257353Sbdrewery
334257353Sbdrewery	| EPSV check_login CRLF
335257353Sbdrewery		{
336257353Sbdrewery			if ($2) {
337257353Sbdrewery				if (CURCLASS_FLAGS_ISSET(passive))
338257353Sbdrewery					long_passive("EPSV", PF_UNSPEC);
339257353Sbdrewery				else
340263020Sbapt					reply(500, "EPSV mode not available.");
341263020Sbapt			}
342263020Sbapt		}
343263020Sbapt
344257353Sbdrewery	| TYPE check_login SP type_code CRLF
345263020Sbapt		{
346257353Sbdrewery			if ($2) {
347263020Sbapt
348257353Sbdrewery			switch (cmd_type) {
349263020Sbapt
350263020Sbapt			case TYPE_A:
351257353Sbdrewery				if (cmd_form == FORM_N) {
352263020Sbapt					reply(200, "Type set to A.");
353263020Sbapt					type = cmd_type;
354257353Sbdrewery					form = cmd_form;
355268896Sbapt				} else
356263020Sbapt					reply(504, "Form must be N.");
357257353Sbdrewery				break;
358257353Sbdrewery
359257353Sbdrewery			case TYPE_E:
360257353Sbdrewery				reply(504, "Type E not implemented.");
361257353Sbdrewery				break;
362257353Sbdrewery
363257353Sbdrewery			case TYPE_I:
364257353Sbdrewery				reply(200, "Type set to I.");
365257353Sbdrewery				type = cmd_type;
366257353Sbdrewery				break;
367257353Sbdrewery
368257353Sbdrewery			case TYPE_L:
369257353Sbdrewery#if NBBY == 8
370257353Sbdrewery				if (cmd_bytesz == 8) {
371257353Sbdrewery					reply(200,
372257353Sbdrewery					    "Type set to L (byte size 8).");
373257353Sbdrewery					type = cmd_type;
374257353Sbdrewery				} else
375257353Sbdrewery					reply(504, "Byte size must be 8.");
376278563Sbapt#else /* NBBY == 8 */
377278563Sbapt				UNIMPLEMENTED for NBBY != 8
378278563Sbapt#endif /* NBBY == 8 */
379257353Sbdrewery			}
380278563Sbapt
381257353Sbdrewery			}
382257353Sbdrewery		}
383257353Sbdrewery
384257353Sbdrewery	| STRU check_login SP struct_code CRLF
385257353Sbdrewery		{
386257353Sbdrewery			if ($2) {
387257353Sbdrewery				switch ($4) {
388257353Sbdrewery
389257353Sbdrewery				case STRU_F:
390257353Sbdrewery					reply(200, "STRU F ok.");
391257353Sbdrewery					break;
392257353Sbdrewery
393257353Sbdrewery				default:
394257353Sbdrewery					reply(504, "Unimplemented STRU type.");
395257353Sbdrewery				}
396257353Sbdrewery			}
397257353Sbdrewery		}
398257353Sbdrewery
399257353Sbdrewery	| MODE check_login SP mode_code CRLF
400257353Sbdrewery		{
401257353Sbdrewery			if ($2) {
402257353Sbdrewery				switch ($4) {
403257353Sbdrewery
404257353Sbdrewery				case MODE_S:
405257353Sbdrewery					reply(200, "MODE S ok.");
406257353Sbdrewery					break;
407257353Sbdrewery
408257353Sbdrewery				default:
409257353Sbdrewery					reply(502, "Unimplemented MODE type.");
410257353Sbdrewery				}
411257353Sbdrewery			}
412257353Sbdrewery		}
413257353Sbdrewery
414257353Sbdrewery	| RETR check_login SP pathname CRLF
415257353Sbdrewery		{
416257353Sbdrewery			if ($2 && $4 != NULL)
417257353Sbdrewery				retrieve(NULL, $4);
418257353Sbdrewery			if ($4 != NULL)
419257353Sbdrewery				free($4);
420257353Sbdrewery		}
421257353Sbdrewery
422257353Sbdrewery	| STOR SP pathname CRLF
423257353Sbdrewery		{
424257353Sbdrewery			if (check_write($3, 1))
425257353Sbdrewery				store($3, "w", 0);
426257353Sbdrewery			if ($3 != NULL)
427257353Sbdrewery				free($3);
428257353Sbdrewery		}
429257353Sbdrewery
430257353Sbdrewery	| STOU SP pathname CRLF
431257353Sbdrewery		{
432257353Sbdrewery			if (check_write($3, 1))
433257353Sbdrewery				store($3, "w", 1);
434257353Sbdrewery			if ($3 != NULL)
435257353Sbdrewery				free($3);
436257353Sbdrewery		}
437257353Sbdrewery
438257353Sbdrewery	| APPE SP pathname CRLF
439257353Sbdrewery		{
440257353Sbdrewery			if (check_write($3, 1))
441257353Sbdrewery				store($3, "a", 0);
442257353Sbdrewery			if ($3 != NULL)
443257353Sbdrewery				free($3);
444257353Sbdrewery		}
445257353Sbdrewery
446257353Sbdrewery	| ALLO check_login SP NUMBER CRLF
447257353Sbdrewery		{
448257353Sbdrewery			if ($2)
449257353Sbdrewery				reply(202, "ALLO command ignored.");
450257353Sbdrewery		}
451257353Sbdrewery
452257353Sbdrewery	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
453257353Sbdrewery		{
454257353Sbdrewery			if ($2)
455257353Sbdrewery				reply(202, "ALLO command ignored.");
456257353Sbdrewery		}
457257353Sbdrewery
458257353Sbdrewery	| RNTO SP pathname CRLF
459257353Sbdrewery		{
460257353Sbdrewery			if (check_write($3, 0)) {
461257353Sbdrewery				if (fromname) {
462257353Sbdrewery					renamecmd(fromname, $3);
463257353Sbdrewery					REASSIGN(fromname, NULL);
464257353Sbdrewery				} else {
465257353Sbdrewery					reply(503, "Bad sequence of commands.");
466257353Sbdrewery				}
467257353Sbdrewery			}
468257353Sbdrewery			if ($3 != NULL)
469257353Sbdrewery				free($3);
470257353Sbdrewery		}
471257353Sbdrewery
472257353Sbdrewery	| ABOR check_login CRLF
473257353Sbdrewery		{
474257353Sbdrewery			if (is_oob)
475257353Sbdrewery				abor();
476257353Sbdrewery			else if ($2)
477257353Sbdrewery				reply(225, "ABOR command successful.");
478287872Sdelphij		}
479287872Sdelphij
480287872Sdelphij	| DELE SP pathname CRLF
481287872Sdelphij		{
482287872Sdelphij			if (check_write($3, 0))
483287872Sdelphij				delete($3);
484287872Sdelphij			if ($3 != NULL)
485287872Sdelphij				free($3);
486287872Sdelphij		}
487287872Sdelphij
488287872Sdelphij	| RMD SP pathname CRLF
489287872Sdelphij		{
490287872Sdelphij			if (check_write($3, 0))
491287872Sdelphij				removedir($3);
492287872Sdelphij			if ($3 != NULL)
493287872Sdelphij				free($3);
494287872Sdelphij		}
495287872Sdelphij
496287872Sdelphij	| MKD SP pathname CRLF
497257353Sbdrewery		{
498257353Sbdrewery			if (check_write($3, 0))
499257353Sbdrewery				makedir($3);
500257353Sbdrewery			if ($3 != NULL)
501257353Sbdrewery				free($3);
502257353Sbdrewery		}
503257353Sbdrewery
504257353Sbdrewery	| PWD check_login CRLF
505257353Sbdrewery		{
506257353Sbdrewery			if ($2)
507257353Sbdrewery				pwd();
508257353Sbdrewery		}
509257353Sbdrewery
510257353Sbdrewery	| LIST check_login CRLF
511257353Sbdrewery		{
512257353Sbdrewery			char *argv[] = { INTERNAL_LS, "-lgA", NULL };
513257353Sbdrewery
514287872Sdelphij			if (CURCLASS_FLAGS_ISSET(hidesymlinks))
515287872Sdelphij				argv[1] = "-LlgA";
516257353Sbdrewery			if ($2)
517257353Sbdrewery				retrieve(argv, "");
518257353Sbdrewery		}
519257353Sbdrewery
520257353Sbdrewery	| LIST check_login SP pathname CRLF
521257353Sbdrewery		{
522257353Sbdrewery			char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL };
523257353Sbdrewery
524257353Sbdrewery			if (CURCLASS_FLAGS_ISSET(hidesymlinks))
525257353Sbdrewery				argv[1] = "-LlgA";
526257353Sbdrewery			if ($2 && $4 != NULL) {
527287872Sdelphij				argv[2] = $4;
528287872Sdelphij				retrieve(argv, $4);
529257353Sbdrewery			}
530257353Sbdrewery			if ($4 != NULL)
531257353Sbdrewery				free($4);
532257353Sbdrewery		}
533257353Sbdrewery
534257353Sbdrewery	| NLST check_login CRLF
535257353Sbdrewery		{
536257353Sbdrewery			if ($2)
537257353Sbdrewery				send_file_list(".");
538257353Sbdrewery		}
539287872Sdelphij
540287872Sdelphij	| NLST check_login SP pathname CRLF
541287872Sdelphij		{
542287872Sdelphij			if ($2)
543287872Sdelphij				send_file_list($4);
544287872Sdelphij			free($4);
545287872Sdelphij		}
546287872Sdelphij
547287872Sdelphij	| SITE SP HELP CRLF
548287872Sdelphij		{
549257353Sbdrewery			help(sitetab, NULL);
550257353Sbdrewery		}
551257353Sbdrewery
552257353Sbdrewery	| SITE SP CHMOD SP octal_number SP pathname CRLF
553257353Sbdrewery		{
554257353Sbdrewery			if (check_write($7, 0)) {
555257353Sbdrewery				if (($5 == -1) || ($5 > 0777))
556257353Sbdrewery					reply(501,
557257353Sbdrewery				"CHMOD: Mode value must be between 0 and 0777");
558257353Sbdrewery				else if (chmod($7, $5) < 0)
559257353Sbdrewery					perror_reply(550, $7);
560257353Sbdrewery				else
561257353Sbdrewery					reply(200, "CHMOD command successful.");
562257353Sbdrewery			}
563257353Sbdrewery			if ($7 != NULL)
564257353Sbdrewery				free($7);
565257353Sbdrewery		}
566257353Sbdrewery
567257353Sbdrewery	| SITE SP HELP SP STRING CRLF
568257353Sbdrewery		{
569257353Sbdrewery			help(sitetab, $5);
570257353Sbdrewery			free($5);
571257353Sbdrewery		}
572257353Sbdrewery
573257353Sbdrewery	| SITE SP IDLE check_login CRLF
574257353Sbdrewery		{
575257353Sbdrewery			if ($4) {
576257353Sbdrewery				reply(200,
577257353Sbdrewery				    "Current IDLE time limit is " LLF
578257353Sbdrewery				    " seconds; max " LLF,
579257353Sbdrewery				    (LLT)curclass.timeout,
580257353Sbdrewery				    (LLT)curclass.maxtimeout);
581257353Sbdrewery			}
582257353Sbdrewery		}
583257353Sbdrewery
584257353Sbdrewery	| SITE SP IDLE check_login SP NUMBER CRLF
585257353Sbdrewery		{
586257353Sbdrewery			if ($4) {
587257353Sbdrewery				if ($6.i < 30 || $6.i > curclass.maxtimeout) {
588287872Sdelphij					reply(501,
589287872Sdelphij				"IDLE time limit must be between 30 and "
590287872Sdelphij					    LLF " seconds",
591287872Sdelphij					    (LLT)curclass.maxtimeout);
592287872Sdelphij				} else {
593287872Sdelphij					curclass.timeout = $6.i;
594287872Sdelphij					(void) alarm(curclass.timeout);
595287872Sdelphij					reply(200,
596287872Sdelphij					    "IDLE time limit set to "
597287872Sdelphij					    LLF " seconds",
598287872Sdelphij					    (LLT)curclass.timeout);
599287872Sdelphij				}
600287872Sdelphij			}
601287872Sdelphij		}
602287872Sdelphij
603287872Sdelphij	| SITE SP RATEGET check_login CRLF
604287872Sdelphij		{
605287872Sdelphij			if ($4) {
606287872Sdelphij				reply(200,
607287872Sdelphij				    "Current RATEGET is " LLF " bytes/sec",
608287872Sdelphij				    (LLT)curclass.rateget);
609287872Sdelphij			}
610287872Sdelphij		}
611287872Sdelphij
612287872Sdelphij	| SITE SP RATEGET check_login SP STRING CRLF
613287872Sdelphij		{
614287872Sdelphij			char errbuf[100];
615287872Sdelphij			char *p = $6;
616287872Sdelphij			LLT rate;
617257353Sbdrewery
618257353Sbdrewery			if ($4) {
619257353Sbdrewery				rate = strsuftollx("RATEGET", p, 0,
620257353Sbdrewery				    curclass.maxrateget
621257353Sbdrewery				    ? curclass.maxrateget
622257353Sbdrewery				    : LLTMAX, errbuf, sizeof(errbuf));
623257353Sbdrewery				if (errbuf[0])
624257353Sbdrewery					reply(501, "%s", errbuf);
625257353Sbdrewery				else {
626257353Sbdrewery					curclass.rateget = rate;
627257353Sbdrewery					reply(200,
628257353Sbdrewery					    "RATEGET set to " LLF " bytes/sec",
629257353Sbdrewery					    (LLT)curclass.rateget);
630257353Sbdrewery				}
631257353Sbdrewery			}
632257353Sbdrewery			free($6);
633257353Sbdrewery		}
634257353Sbdrewery
635257353Sbdrewery	| SITE SP RATEPUT check_login CRLF
636257353Sbdrewery		{
637257353Sbdrewery			if ($4) {
638257353Sbdrewery				reply(200,
639257353Sbdrewery				    "Current RATEPUT is " LLF " bytes/sec",
640257353Sbdrewery				    (LLT)curclass.rateput);
641257353Sbdrewery			}
642257353Sbdrewery		}
643257353Sbdrewery
644257353Sbdrewery	| SITE SP RATEPUT check_login SP STRING CRLF
645257353Sbdrewery		{
646257353Sbdrewery			char errbuf[100];
647257353Sbdrewery			char *p = $6;
648257353Sbdrewery			LLT rate;
649257353Sbdrewery
650257353Sbdrewery			if ($4) {
651257353Sbdrewery				rate = strsuftollx("RATEPUT", p, 0,
652257353Sbdrewery				    curclass.maxrateput
653257353Sbdrewery				    ? curclass.maxrateput
654257353Sbdrewery				    : LLTMAX, errbuf, sizeof(errbuf));
655257353Sbdrewery				if (errbuf[0])
656257353Sbdrewery					reply(501, "%s", errbuf);
657257353Sbdrewery				else {
658257353Sbdrewery					curclass.rateput = rate;
659257353Sbdrewery					reply(200,
660257353Sbdrewery					    "RATEPUT set to " LLF " bytes/sec",
661257353Sbdrewery					    (LLT)curclass.rateput);
662257353Sbdrewery				}
663257353Sbdrewery			}
664257353Sbdrewery			free($6);
665257353Sbdrewery		}
666257353Sbdrewery
667257353Sbdrewery	| SITE SP UMASK check_login CRLF
668257353Sbdrewery		{
669257353Sbdrewery			int oldmask;
670257353Sbdrewery
671257353Sbdrewery			if ($4) {
672257353Sbdrewery				oldmask = umask(0);
673257353Sbdrewery				(void) umask(oldmask);
674257353Sbdrewery				reply(200, "Current UMASK is %03o", oldmask);
675257353Sbdrewery			}
676257353Sbdrewery		}
677257353Sbdrewery
678257353Sbdrewery	| SITE SP UMASK check_login SP octal_number CRLF
679257353Sbdrewery		{
680257353Sbdrewery			int oldmask;
681257353Sbdrewery
682257353Sbdrewery			if ($4 && check_write("", 0)) {
683257353Sbdrewery				if (($6 == -1) || ($6 > 0777)) {
684257353Sbdrewery					reply(501, "Bad UMASK value");
685257353Sbdrewery				} else {
686257353Sbdrewery					oldmask = umask($6);
687257353Sbdrewery					reply(200,
688257353Sbdrewery					    "UMASK set to %03o (was %03o)",
689257353Sbdrewery					    $6, oldmask);
690287872Sdelphij				}
691287872Sdelphij			}
692287872Sdelphij		}
693287872Sdelphij
694287872Sdelphij	| SYST CRLF
695287872Sdelphij		{
696287872Sdelphij			if (EMPTYSTR(version))
697287872Sdelphij				reply(215, "UNIX Type: L%d", NBBY);
698287872Sdelphij			else
699287872Sdelphij				reply(215, "UNIX Type: L%d Version: %s", NBBY,
700287872Sdelphij				    version);
701287872Sdelphij		}
702287872Sdelphij
703287872Sdelphij	| STAT check_login SP pathname CRLF
704287872Sdelphij		{
705287872Sdelphij			if ($2 && $4 != NULL)
706287872Sdelphij				statfilecmd($4);
707287872Sdelphij			if ($4 != NULL)
708287872Sdelphij				free($4);
709287872Sdelphij		}
710287872Sdelphij
711287872Sdelphij	| STAT CRLF
712287872Sdelphij		{
713287872Sdelphij			if (is_oob)
714287872Sdelphij				statxfer();
715287872Sdelphij			else
716287872Sdelphij				statcmd();
717287872Sdelphij		}
718287872Sdelphij
719287872Sdelphij	| HELP CRLF
720287872Sdelphij		{
721287872Sdelphij			help(cmdtab, NULL);
722287872Sdelphij		}
723287872Sdelphij
724287872Sdelphij	| HELP SP STRING CRLF
725287872Sdelphij		{
726287872Sdelphij			char *cp = $3;
727287872Sdelphij
728287872Sdelphij			if (strncasecmp(cp, "SITE", 4) == 0) {
729257353Sbdrewery				cp = $3 + 4;
730257353Sbdrewery				if (*cp == ' ')
731257353Sbdrewery					cp++;
732257353Sbdrewery				if (*cp)
733257353Sbdrewery					help(sitetab, cp);
734257353Sbdrewery				else
735257353Sbdrewery					help(sitetab, NULL);
736257353Sbdrewery			} else
737257353Sbdrewery				help(cmdtab, $3);
738257353Sbdrewery			free($3);
739257353Sbdrewery		}
740257353Sbdrewery
741257353Sbdrewery	| NOOP CRLF
742257353Sbdrewery		{
743257353Sbdrewery			reply(200, "NOOP command successful.");
744257353Sbdrewery		}
745257353Sbdrewery
746257353Sbdrewery						/* RFC 2228 */
747257353Sbdrewery	| AUTH SP mechanism_name CRLF
748257353Sbdrewery		{
749257353Sbdrewery			reply(502, "RFC 2228 authentication not implemented.");
750257353Sbdrewery			free($3);
751257353Sbdrewery		}
752257353Sbdrewery
753257353Sbdrewery	| ADAT SP base64data CRLF
754257353Sbdrewery		{
755257353Sbdrewery			reply(503,
756257353Sbdrewery			    "Please set authentication state with AUTH.");
757257353Sbdrewery			free($3);
758257353Sbdrewery		}
759257353Sbdrewery
760257353Sbdrewery	| PROT SP prot_code CRLF
761257353Sbdrewery		{
762257353Sbdrewery			reply(503,
763257353Sbdrewery			    "Please set protection buffer size with PBSZ.");
764257353Sbdrewery			free($3);
765257353Sbdrewery		}
766257353Sbdrewery
767257353Sbdrewery	| PBSZ SP decimal_integer CRLF
768257353Sbdrewery		{
769257353Sbdrewery			reply(503,
770257353Sbdrewery			    "Please set authentication state with AUTH.");
771257353Sbdrewery		}
772257353Sbdrewery
773257353Sbdrewery	| CCC CRLF
774257353Sbdrewery		{
775257353Sbdrewery			reply(533, "No protection enabled.");
776257353Sbdrewery		}
777257353Sbdrewery
778257353Sbdrewery	| MIC SP base64data CRLF
779257353Sbdrewery		{
780257353Sbdrewery			reply(502, "RFC 2228 authentication not implemented.");
781257353Sbdrewery			free($3);
782257353Sbdrewery		}
783257353Sbdrewery
784257353Sbdrewery	| CONF SP base64data CRLF
785257353Sbdrewery		{
786257353Sbdrewery			reply(502, "RFC 2228 authentication not implemented.");
787257353Sbdrewery			free($3);
788257353Sbdrewery		}
789257353Sbdrewery
790257353Sbdrewery	| ENC SP base64data CRLF
791257353Sbdrewery		{
792257353Sbdrewery			reply(502, "RFC 2228 authentication not implemented.");
793257353Sbdrewery			free($3);
794257353Sbdrewery		}
795257353Sbdrewery
796257353Sbdrewery						/* RFC 2389 */
797257353Sbdrewery	| FEAT CRLF
798257353Sbdrewery		{
799257353Sbdrewery
800257353Sbdrewery			feat();
801257353Sbdrewery		}
802257353Sbdrewery
803257353Sbdrewery	| OPTS SP STRING CRLF
804257353Sbdrewery		{
805257353Sbdrewery
806287872Sdelphij			opts($3);
807257353Sbdrewery			free($3);
808257353Sbdrewery		}
809257353Sbdrewery
810257353Sbdrewery
811257353Sbdrewery				/* extensions from draft-ietf-ftpext-mlst-11 */
812257353Sbdrewery
813257353Sbdrewery		/*
814257353Sbdrewery		 * Return size of file in a format suitable for
815257353Sbdrewery		 * using with RESTART (we just count bytes).
816257353Sbdrewery		 */
817257353Sbdrewery	| SIZE check_login SP pathname CRLF
818257353Sbdrewery		{
819257353Sbdrewery			if ($2 && $4 != NULL)
820283788Sbapt				sizecmd($4);
821283788Sbapt			if ($4 != NULL)
822283788Sbapt				free($4);
823257353Sbdrewery		}
824257353Sbdrewery
825257353Sbdrewery		/*
826257353Sbdrewery		 * Return modification time of file as an ISO 3307
827257353Sbdrewery		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
828257353Sbdrewery		 * where xxx is the fractional second (of any precision,
829257353Sbdrewery		 * not necessarily 3 digits)
830257632Sbdrewery		 */
831257353Sbdrewery	| MDTM check_login SP pathname CRLF
832257353Sbdrewery		{
833257353Sbdrewery			if ($2 && $4 != NULL) {
834257353Sbdrewery				struct stat stbuf;
835257353Sbdrewery				if (stat($4, &stbuf) < 0)
836257353Sbdrewery					perror_reply(550, $4);
837257353Sbdrewery				else if (!S_ISREG(stbuf.st_mode)) {
838257353Sbdrewery					reply(550, "%s: not a plain file.", $4);
839257353Sbdrewery				} else {
840257353Sbdrewery					struct tm *t;
841257353Sbdrewery
842257353Sbdrewery					t = gmtime(&stbuf.st_mtime);
843257353Sbdrewery					reply(213,
844257353Sbdrewery					    "%04d%02d%02d%02d%02d%02d",
845257353Sbdrewery					    TM_YEAR_BASE + t->tm_year,
846257353Sbdrewery					    t->tm_mon+1, t->tm_mday,
847257353Sbdrewery					    t->tm_hour, t->tm_min, t->tm_sec);
848257353Sbdrewery				}
849257353Sbdrewery			}
850257353Sbdrewery			if ($4 != NULL)
851257353Sbdrewery				free($4);
852257353Sbdrewery		}
853257353Sbdrewery
854257353Sbdrewery	| MLST check_login SP pathname CRLF
855257353Sbdrewery		{
856257353Sbdrewery			if ($2 && $4 != NULL)
857257353Sbdrewery				mlst($4);
858257353Sbdrewery			if ($4 != NULL)
859257353Sbdrewery				free($4);
860257353Sbdrewery		}
861257353Sbdrewery
862257353Sbdrewery	| MLST check_login CRLF
863257353Sbdrewery		{
864257353Sbdrewery			mlst(NULL);
865257353Sbdrewery		}
866257353Sbdrewery
867257353Sbdrewery	| MLSD check_login SP pathname CRLF
868257353Sbdrewery		{
869257353Sbdrewery			if ($2 && $4 != NULL)
870257353Sbdrewery				mlsd($4);
871287145Sdelphij			if ($4 != NULL)
872287872Sdelphij				free($4);
873287872Sdelphij		}
874287872Sdelphij
875287872Sdelphij	| MLSD check_login CRLF
876287872Sdelphij		{
877287872Sdelphij			mlsd(NULL);
878287872Sdelphij		}
879287872Sdelphij
880287872Sdelphij	| error CRLF
881287872Sdelphij		{
882287872Sdelphij			yyerrok;
883287872Sdelphij		}
884287872Sdelphij	;
885287872Sdelphij
886287872Sdelphijrcmd
887287872Sdelphij	: REST check_login SP NUMBER CRLF
888287872Sdelphij		{
889287872Sdelphij			if ($2) {
890287872Sdelphij				REASSIGN(fromname, NULL);
891287872Sdelphij				restart_point = (off_t)$4.ll;
892287872Sdelphij				reply(350,
893287872Sdelphij    "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.",
894287872Sdelphij				    (LLT)restart_point);
895287872Sdelphij			}
896287872Sdelphij		}
897287872Sdelphij
898287872Sdelphij	| RNFR SP pathname CRLF
899287872Sdelphij		{
900287872Sdelphij			restart_point = (off_t) 0;
901287872Sdelphij			if (check_write($3, 0)) {
902287872Sdelphij				REASSIGN(fromname, NULL);
903287872Sdelphij				fromname = renamefrom($3);
904287145Sdelphij			}
905287145Sdelphij			if ($3 != NULL)
906287145Sdelphij				free($3);
907287145Sdelphij		}
908257353Sbdrewery	;
909257353Sbdrewery
910257353Sbdreweryusername
911257632Sbdrewery	: STRING
912234313Sbapt	;
913234351Sbapt
914234351Sbaptpassword
915234351Sbapt	: /* empty */
916234351Sbapt		{
917257353Sbdrewery			$$ = (char *)calloc(1, sizeof(char));
918257353Sbdrewery		}
919257353Sbdrewery
920257353Sbdrewery	| STRING
921234351Sbapt	;
922234313Sbapt
923257353Sbdrewerybyte_size
924257353Sbdrewery	: NUMBER
925257353Sbdrewery		{
926257353Sbdrewery			$$ = $1.i;
927234313Sbapt		}
928278563Sbapt	;
929278563Sbapt
930278563Sbapthost_port
931278563Sbapt	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
932278563Sbapt		NUMBER COMMA NUMBER
933234351Sbapt		{
934234313Sbapt			char *a, *p;
935234313Sbapt
936238461Skan			memset(&data_dest, 0, sizeof(data_dest));
937238461Skan			data_dest.su_len = sizeof(struct sockaddr_in);
938238461Skan			data_dest.su_family = AF_INET;
939238461Skan			p = (char *)&data_dest.su_port;
940283790Sbapt			p[0] = $9.i; p[1] = $11.i;
941283790Sbapt			a = (char *)&data_dest.su_addr;
942283790Sbapt			a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
943283790Sbapt		}
944283790Sbapt	;
945238461Skan
946238461Skanhost_long_port4
947238461Skan	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
948238461Skan		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
949238461Skan		NUMBER
950238461Skan		{
951238461Skan			char *a, *p;
952238461Skan
953238461Skan			memset(&data_dest, 0, sizeof(data_dest));
954238461Skan			data_dest.su_len = sizeof(struct sockaddr_in);
955238461Skan			data_dest.su_family = AF_INET;
956238461Skan			p = (char *)&data_dest.su_port;
957238461Skan			p[0] = $15.i; p[1] = $17.i;
958238461Skan			a = (char *)&data_dest.su_addr;
959238461Skan			a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
960238461Skan
961238461Skan			/* reject invalid LPRT command */
962238461Skan			if ($1.i != 4 || $3.i != 4 || $13.i != 2)
963257353Sbdrewery				memset(&data_dest, 0, sizeof(data_dest));
964257632Sbdrewery		}
965257353Sbdrewery	;
966257353Sbdrewery
967257353Sbdreweryhost_long_port6
968257353Sbdrewery	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
969257353Sbdrewery		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
970257353Sbdrewery		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
971257353Sbdrewery		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
972257353Sbdrewery		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
973257353Sbdrewery		NUMBER
974257353Sbdrewery		{
975257353Sbdrewery#ifdef INET6
976257353Sbdrewery			char *a, *p;
977257353Sbdrewery
978257353Sbdrewery			memset(&data_dest, 0, sizeof(data_dest));
979257353Sbdrewery			data_dest.su_len = sizeof(struct sockaddr_in6);
980278563Sbapt			data_dest.su_family = AF_INET6;
981257353Sbdrewery			p = (char *)&data_dest.su_port;
982257353Sbdrewery			p[0] = $39.i; p[1] = $41.i;
983287145Sdelphij			a = (char *)&data_dest.si_su.su_sin6.sin6_addr;
984287872Sdelphij			a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
985287872Sdelphij			a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
986287872Sdelphij			a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
987287872Sdelphij			a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
988287872Sdelphij			if (his_addr.su_family == AF_INET6) {
989287872Sdelphij				/* XXX: more sanity checks! */
990287872Sdelphij				data_dest.su_scope_id = his_addr.su_scope_id;
991287872Sdelphij			}
992287872Sdelphij#else
993287872Sdelphij			memset(&data_dest, 0, sizeof(data_dest));
994287872Sdelphij#endif /* INET6 */
995287872Sdelphij			/* reject invalid LPRT command */
996287872Sdelphij			if ($1.i != 6 || $3.i != 16 || $37.i != 2)
997287872Sdelphij				memset(&data_dest, 0, sizeof(data_dest));
998287872Sdelphij		}
999287872Sdelphij	;
1000287872Sdelphij
1001287872Sdelphijform_code
1002287872Sdelphij	: N
1003287872Sdelphij		{
1004287872Sdelphij			$$ = FORM_N;
1005287872Sdelphij		}
1006287872Sdelphij
1007287872Sdelphij	| T
1008287872Sdelphij		{
1009287872Sdelphij			$$ = FORM_T;
1010287872Sdelphij		}
1011287145Sdelphij
1012287145Sdelphij	| C
1013287145Sdelphij		{
1014287145Sdelphij			$$ = FORM_C;
1015257353Sbdrewery		}
1016257353Sbdrewery	;
1017257353Sbdrewery
1018257632Sbdrewerytype_code
1019257353Sbdrewery	: A
1020257353Sbdrewery		{
1021257353Sbdrewery			cmd_type = TYPE_A;
1022257353Sbdrewery			cmd_form = FORM_N;
1023257353Sbdrewery		}
1024257353Sbdrewery
1025257353Sbdrewery	| A SP form_code
1026257353Sbdrewery		{
1027257353Sbdrewery			cmd_type = TYPE_A;
1028234313Sbapt			cmd_form = $3;
1029283789Sbapt		}
1030234313Sbapt
1031234313Sbapt	| E
1032257632Sbdrewery		{
1033257632Sbdrewery			cmd_type = TYPE_E;
1034234313Sbapt			cmd_form = FORM_N;
1035257632Sbdrewery		}
1036257632Sbdrewery
1037257632Sbdrewery	| E SP form_code
1038257632Sbdrewery		{
1039257632Sbdrewery			cmd_type = TYPE_E;
1040234313Sbapt			cmd_form = $3;
1041234322Sbapt		}
1042234313Sbapt
1043257632Sbdrewery	| I
1044257632Sbdrewery		{
1045257632Sbdrewery			cmd_type = TYPE_I;
1046257632Sbdrewery		}
1047257632Sbdrewery
1048257632Sbdrewery	| L
1049257632Sbdrewery		{
1050244553Smatthew			cmd_type = TYPE_L;
1051244594Smatthew			cmd_bytesz = NBBY;
1052244553Smatthew		}
1053244553Smatthew
1054244553Smatthew	| L SP byte_size
1055244639Smatthew		{
1056244639Smatthew			cmd_type = TYPE_L;
1057244553Smatthew			cmd_bytesz = $3;
1058257353Sbdrewery		}
1059257353Sbdrewery
1060257632Sbdrewery		/* this is for a bug in the BBN ftp */
1061257632Sbdrewery	| L byte_size
1062257632Sbdrewery		{
1063257632Sbdrewery			cmd_type = TYPE_L;
1064257632Sbdrewery			cmd_bytesz = $2;
1065257632Sbdrewery		}
1066257632Sbdrewery	;
1067257632Sbdrewery
1068257309Sbaptstruct_code
1069257632Sbdrewery	: F
1070257632Sbdrewery		{
1071257632Sbdrewery			$$ = STRU_F;
1072257632Sbdrewery		}
1073257632Sbdrewery
1074257632Sbdrewery	| R
1075257632Sbdrewery		{
1076257309Sbapt			$$ = STRU_R;
1077257309Sbapt		}
1078238461Skan
1079238461Skan	| P
1080238461Skan		{
1081238461Skan			$$ = STRU_P;
1082238461Skan		}
1083247841Sbapt	;
1084247841Sbapt
1085283790Sbaptmode_code
1086283790Sbapt	: S
1087238461Skan		{
1088283790Sbapt			$$ = MODE_S;
1089239664Sbapt		}
1090283790Sbapt
1091239664Sbapt	| B
1092239663Sbapt		{
1093238461Skan			$$ = MODE_B;
1094257632Sbdrewery		}
1095234351Sbapt
1096247841Sbapt	| C
1097257571Sbdrewery		{
1098257632Sbdrewery			$$ = MODE_C;
1099257571Sbdrewery		}
1100257632Sbdrewery	;
1101257632Sbdrewery
1102257632Sbdrewerypathname
1103238461Skan	: pathstring
1104234313Sbapt		{
1105234313Sbapt			/*
1106234313Sbapt			 * Problem: this production is used for all pathname
1107234351Sbapt			 * processing, but only gives a 550 error reply.
1108234322Sbapt			 * This is a valid reply in some cases but not in
1109234313Sbapt			 * others.
1110			 */
1111			if (logged_in && $1 && *$1 == '~') {
1112				char	*path, *home, *result;
1113				size_t	len;
1114
1115				path = strchr($1 + 1, '/');
1116				if (path != NULL)
1117					*path++ = '\0';
1118				if ($1[1] == '\0')
1119					home = homedir;
1120				else {
1121					struct passwd	*hpw;
1122
1123					if ((hpw = getpwnam($1 + 1)) != NULL)
1124						home = hpw->pw_dir;
1125					else
1126						home = $1;
1127				}
1128				len = strlen(home) + 1;
1129				if (path != NULL)
1130					len += strlen(path) + 1;
1131				if ((result = malloc(len)) == NULL)
1132					fatal("Local resource failure: malloc");
1133				strlcpy(result, home, len);
1134				if (path != NULL) {
1135					strlcat(result, "/", len);
1136					strlcat(result, path, len);
1137				}
1138				$$ = result;
1139				free($1);
1140			} else
1141				$$ = $1;
1142		}
1143	;
1144
1145pathstring
1146	: STRING
1147	;
1148
1149octal_number
1150	: NUMBER
1151		{
1152			int ret, dec, multby, digit;
1153
1154			/*
1155			 * Convert a number that was read as decimal number
1156			 * to what it would be if it had been read as octal.
1157			 */
1158			dec = $1.i;
1159			multby = 1;
1160			ret = 0;
1161			while (dec) {
1162				digit = dec%10;
1163				if (digit > 7) {
1164					ret = -1;
1165					break;
1166				}
1167				ret += digit * multby;
1168				multby *= 8;
1169				dec /= 10;
1170			}
1171			$$ = ret;
1172		}
1173	;
1174
1175mechanism_name
1176	: STRING
1177	;
1178
1179base64data
1180	: STRING
1181	;
1182
1183prot_code
1184	: STRING
1185	;
1186
1187decimal_integer
1188	: NUMBER
1189		{
1190			$$ = $1.i;
1191		}
1192	;
1193
1194check_login
1195	: /* empty */
1196		{
1197			if (logged_in)
1198				$$ = 1;
1199			else {
1200				reply(530, "Please login with USER and PASS.");
1201				$$ = 0;
1202				hasyyerrored = 1;
1203			}
1204		}
1205	;
1206
1207%%
1208
1209#define	CMD	0	/* beginning of command */
1210#define	ARGS	1	/* expect miscellaneous arguments */
1211#define	STR1	2	/* expect SP followed by STRING */
1212#define	STR2	3	/* expect STRING */
1213#define	OSTR	4	/* optional SP then STRING */
1214#define	ZSTR1	5	/* SP then optional STRING */
1215#define	ZSTR2	6	/* optional STRING after SP */
1216#define	SITECMD	7	/* SITE command */
1217#define	NSTR	8	/* Number followed by a string */
1218#define NOARGS	9	/* No arguments allowed */
1219#define EOLN	10	/* End of line */
1220
1221struct tab cmdtab[] = {
1222				/* From RFC 959, in order defined (5.3.1) */
1223	{ "USER", USER, STR1,	1,	"<sp> username" },
1224	{ "PASS", PASS, ZSTR1,	1,	"<sp> password" },
1225	{ "ACCT", ACCT, STR1,	0,	"(specify account)" },
1226	{ "CWD",  CWD,  OSTR,	1,	"[ <sp> directory-name ]" },
1227	{ "CDUP", CDUP, NOARGS,	1,	"(change to parent directory)" },
1228	{ "SMNT", SMNT, ARGS,	0,	"(structure mount)" },
1229	{ "QUIT", QUIT, NOARGS,	1,	"(terminate service)" },
1230	{ "REIN", REIN, NOARGS,	0,	"(reinitialize server state)" },
1231	{ "PORT", PORT, ARGS,	1,	"<sp> b0, b1, b2, b3, b4, b5" },
1232	{ "LPRT", LPRT, ARGS,	1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1233	{ "EPRT", EPRT, STR1,	1,	"<sp> |af|addr|port|" },
1234	{ "PASV", PASV, NOARGS,	1,	"(set server in passive mode)" },
1235	{ "LPSV", LPSV, ARGS,	1,	"(set server in passive mode)" },
1236	{ "EPSV", EPSV, ARGS,	1,	"[<sp> af|ALL]" },
1237	{ "TYPE", TYPE, ARGS,	1,	"<sp> [ A | E | I | L ]" },
1238	{ "STRU", STRU, ARGS,	1,	"(specify file structure)" },
1239	{ "MODE", MODE, ARGS,	1,	"(specify transfer mode)" },
1240	{ "RETR", RETR, STR1,	1,	"<sp> file-name" },
1241	{ "STOR", STOR, STR1,	1,	"<sp> file-name" },
1242	{ "STOU", STOU, STR1,	1,	"<sp> file-name" },
1243	{ "APPE", APPE, STR1,	1,	"<sp> file-name" },
1244	{ "ALLO", ALLO, ARGS,	1,	"allocate storage (vacuously)" },
1245	{ "REST", REST, ARGS,	1,	"<sp> offset (restart command)" },
1246	{ "RNFR", RNFR, STR1,	1,	"<sp> file-name" },
1247	{ "RNTO", RNTO, STR1,	1,	"<sp> file-name" },
1248	{ "ABOR", ABOR, NOARGS,	4,	"(abort operation)" },
1249	{ "DELE", DELE, STR1,	1,	"<sp> file-name" },
1250	{ "RMD",  RMD,  STR1,	1,	"<sp> path-name" },
1251	{ "MKD",  MKD,  STR1,	1,	"<sp> path-name" },
1252	{ "PWD",  PWD,  NOARGS,	1,	"(return current directory)" },
1253	{ "LIST", LIST, OSTR,	1,	"[ <sp> path-name ]" },
1254	{ "NLST", NLST, OSTR,	1,	"[ <sp> path-name ]" },
1255	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
1256	{ "SYST", SYST, NOARGS,	1,	"(get type of operating system)" },
1257	{ "STAT", STAT, OSTR,	4,	"[ <sp> path-name ]" },
1258	{ "HELP", HELP, OSTR,	1,	"[ <sp> <string> ]" },
1259	{ "NOOP", NOOP, NOARGS,	2,	"" },
1260
1261				/* From RFC 2228, in order defined */
1262	{ "AUTH", AUTH, STR1,	1,	"<sp> mechanism-name" },
1263	{ "ADAT", ADAT, STR1,	1,	"<sp> base-64-data" },
1264	{ "PROT", PROT, STR1,	1,	"<sp> prot-code" },
1265	{ "PBSZ", PBSZ, ARGS,	1,	"<sp> decimal-integer" },
1266	{ "CCC",  CCC,  NOARGS,	1,	"(Disable data protection)" },
1267	{ "MIC",  MIC,  STR1,	4,	"<sp> base64data" },
1268	{ "CONF", CONF, STR1,	4,	"<sp> base64data" },
1269	{ "ENC",  ENC,  STR1,	4,	"<sp> base64data" },
1270
1271				/* From RFC 2389, in order defined */
1272	{ "FEAT", FEAT, NOARGS,	1,	"(display extended features)" },
1273	{ "OPTS", OPTS, STR1,	1,	"<sp> command [ <sp> options ]" },
1274
1275				/* from draft-ietf-ftpext-mlst-11 */
1276	{ "MDTM", MDTM, OSTR,	1,	"<sp> path-name" },
1277	{ "SIZE", SIZE, OSTR,	1,	"<sp> path-name" },
1278	{ "MLST", MLST, OSTR,	2,	"[ <sp> path-name ]" },
1279	{ "MLSD", MLSD, OSTR,	1,	"[ <sp> directory-name ]" },
1280
1281				/* obsolete commands */
1282	{ "MAIL", MAIL, OSTR,	0,	"(mail to user)" },
1283	{ "MLFL", MLFL, OSTR,	0,	"(mail file)" },
1284	{ "MRCP", MRCP, STR1,	0,	"(mail recipient)" },
1285	{ "MRSQ", MRSQ, OSTR,	0,	"(mail recipient scheme question)" },
1286	{ "MSAM", MSAM, OSTR,	0,	"(mail send to terminal and mailbox)" },
1287	{ "MSND", MSND, OSTR,	0,	"(mail send to terminal)" },
1288	{ "MSOM", MSOM, OSTR,	0,	"(mail send to terminal or mailbox)" },
1289	{ "XCUP", CDUP, NOARGS,	1,	"(change to parent directory)" },
1290	{ "XCWD", CWD,  OSTR,	1,	"[ <sp> directory-name ]" },
1291	{ "XMKD", MKD,  STR1,	1,	"<sp> path-name" },
1292	{ "XPWD", PWD,  NOARGS,	1,	"(return current directory)" },
1293	{ "XRMD", RMD,  STR1,	1,	"<sp> path-name" },
1294
1295	{  NULL,  0,	0,	0,	0 }
1296};
1297
1298struct tab sitetab[] = {
1299	{ "CHMOD",	CHMOD,	NSTR,	1,	"<sp> mode <sp> file-name" },
1300	{ "HELP",	HELP,	OSTR,	1,	"[ <sp> <string> ]" },
1301	{ "IDLE",	IDLE,	ARGS,	1,	"[ <sp> maximum-idle-time ]" },
1302	{ "RATEGET",	RATEGET,OSTR,	1,	"[ <sp> get-throttle-rate ]" },
1303	{ "RATEPUT",	RATEPUT,OSTR,	1,	"[ <sp> put-throttle-rate ]" },
1304	{ "UMASK",	UMASK,	ARGS,	1,	"[ <sp> umask ]" },
1305	{ NULL,		0,	0,	0,	NULL }
1306};
1307
1308/*
1309 * Check if a filename is allowed to be modified (isupload == 0) or
1310 * uploaded (isupload == 1), and if necessary, check the filename is `sane'.
1311 * If the filename is NULL, fail.
1312 * If the filename is "", don't do the sane name check.
1313 */
1314static int
1315check_write(const char *file, int isupload)
1316{
1317	if (file == NULL)
1318		return (0);
1319	if (! logged_in) {
1320		reply(530, "Please login with USER and PASS.");
1321		return (0);
1322	}
1323		/* checking modify */
1324	if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) {
1325		reply(502, "No permission to use this command.");
1326		return (0);
1327	}
1328		/* checking upload */
1329	if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) {
1330		reply(502, "No permission to use this command.");
1331		return (0);
1332	}
1333
1334		/* checking sanenames */
1335	if (file[0] != '\0' && CURCLASS_FLAGS_ISSET(sanenames)) {
1336		const char *p;
1337
1338		if (file[0] == '.')
1339			goto insane_name;
1340		for (p = file; *p; p++) {
1341			if (isalnum((unsigned char)*p) || *p == '-' || *p == '+' ||
1342			    *p == ',' || *p == '.' || *p == '_')
1343				continue;
1344 insane_name:
1345			reply(553, "File name `%s' not allowed.", file);
1346			return (0);
1347		}
1348	}
1349	return (1);
1350}
1351
1352struct tab *
1353lookup(struct tab *p, const char *cmd)
1354{
1355
1356	for (; p->name != NULL; p++)
1357		if (strcasecmp(cmd, p->name) == 0)
1358			return (p);
1359	return (0);
1360}
1361
1362#include <arpa/telnet.h>
1363
1364/*
1365 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1366 *	`s' is the buffer to read into.
1367 *	`n' is the 1 less than the size of the buffer, to allow trailing NUL
1368 *	`iop' is the FILE to read from.
1369 *	Returns 0 on success, -1 on EOF, -2 if the command was too long.
1370 */
1371int
1372getline(char *s, int n, FILE *iop)
1373{
1374	int c;
1375	char *cs;
1376
1377	cs = s;
1378/* tmpline may contain saved command from urgent mode interruption */
1379	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1380		*cs++ = tmpline[c];
1381		if (tmpline[c] == '\n') {
1382			*cs++ = '\0';
1383			if (ftpd_debug)
1384				syslog(LOG_DEBUG, "command: %s", s);
1385			tmpline[0] = '\0';
1386			return(0);
1387		}
1388		if (c == 0)
1389			tmpline[0] = '\0';
1390	}
1391	while ((c = getc(iop)) != EOF) {
1392		total_bytes++;
1393		total_bytes_in++;
1394		c &= 0377;
1395		if (c == IAC) {
1396		    if ((c = getc(iop)) != EOF) {
1397			total_bytes++;
1398			total_bytes_in++;
1399			c &= 0377;
1400			switch (c) {
1401			case WILL:
1402			case WONT:
1403				c = getc(iop);
1404				total_bytes++;
1405				total_bytes_in++;
1406				cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c);
1407				(void) fflush(stdout);
1408				continue;
1409			case DO:
1410			case DONT:
1411				c = getc(iop);
1412				total_bytes++;
1413				total_bytes_in++;
1414				cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c);
1415				(void) fflush(stdout);
1416				continue;
1417			case IAC:
1418				break;
1419			default:
1420				continue;	/* ignore command */
1421			}
1422		    }
1423		}
1424		*cs++ = c;
1425		if (--n <= 0) {
1426			/*
1427			 * If command doesn't fit into buffer, discard the
1428			 * rest of the command and indicate truncation.
1429			 * This prevents the command to be split up into
1430			 * multiple commands.
1431			 */
1432			if (ftpd_debug)
1433				syslog(LOG_DEBUG,
1434				    "command too long, last char: %d", c);
1435			while (c != '\n' && (c = getc(iop)) != EOF)
1436				continue;
1437			return (-2);
1438		}
1439		if (c == '\n')
1440			break;
1441	}
1442	if (c == EOF && cs == s)
1443		return (-1);
1444	*cs++ = '\0';
1445	if (ftpd_debug) {
1446		if ((curclass.type != CLASS_GUEST &&
1447		    strncasecmp(s, "PASS ", 5) == 0) ||
1448		    strncasecmp(s, "ACCT ", 5) == 0) {
1449			/* Don't syslog passwords */
1450			syslog(LOG_DEBUG, "command: %.4s ???", s);
1451		} else {
1452			char *cp;
1453			int len;
1454
1455			/* Don't syslog trailing CR-LF */
1456			len = strlen(s);
1457			cp = s + len - 1;
1458			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1459				--cp;
1460				--len;
1461			}
1462			syslog(LOG_DEBUG, "command: %.*s", len, s);
1463		}
1464	}
1465	return (0);
1466}
1467
1468void
1469ftp_handle_line(char *cp)
1470{
1471
1472	cmdp = cp;
1473	yyparse();
1474}
1475
1476void
1477ftp_loop(void)
1478{
1479	int ret;
1480
1481	while (1) {
1482		(void) alarm(curclass.timeout);
1483		ret = getline(cbuf, sizeof(cbuf)-1, stdin);
1484		(void) alarm(0);
1485		if (ret == -1) {
1486			reply(221, "You could at least say goodbye.");
1487			dologout(0);
1488		} else if (ret == -2) {
1489			reply(500, "Command too long.");
1490		} else {
1491			ftp_handle_line(cbuf);
1492		}
1493	}
1494	/*NOTREACHED*/
1495}
1496
1497int
1498yylex(void)
1499{
1500	static int cpos, state;
1501	char *cp, *cp2;
1502	struct tab *p;
1503	int n;
1504	char c;
1505
1506	switch (state) {
1507
1508	case CMD:
1509		hasyyerrored = 0;
1510		if ((cp = strchr(cmdp, '\r'))) {
1511			*cp = '\0';
1512#if HAVE_SETPROCTITLE
1513			if (strncasecmp(cmdp, "PASS", 4) != 0 &&
1514			    strncasecmp(cmdp, "ACCT", 4) != 0)
1515				setproctitle("%s: %s", proctitle, cmdp);
1516#endif /* HAVE_SETPROCTITLE */
1517			*cp++ = '\n';
1518			*cp = '\0';
1519		}
1520		if ((cp = strpbrk(cmdp, " \n")))
1521			cpos = cp - cmdp;
1522		if (cpos == 0)
1523			cpos = 4;
1524		c = cmdp[cpos];
1525		cmdp[cpos] = '\0';
1526		p = lookup(cmdtab, cmdp);
1527		cmdp[cpos] = c;
1528		if (p != NULL) {
1529			if (is_oob && ! CMD_OOB(p)) {
1530				/* command will be handled in-band */
1531				return (0);
1532			} else if (! CMD_IMPLEMENTED(p)) {
1533				reply(502, "%s command not implemented.",
1534				    p->name);
1535				hasyyerrored = 1;
1536				break;
1537			}
1538			state = p->state;
1539			yylval.s = p->name;
1540			return (p->token);
1541		}
1542		break;
1543
1544	case SITECMD:
1545		if (cmdp[cpos] == ' ') {
1546			cpos++;
1547			return (SP);
1548		}
1549		cp = &cmdp[cpos];
1550		if ((cp2 = strpbrk(cp, " \n")))
1551			cpos = cp2 - cmdp;
1552		c = cmdp[cpos];
1553		cmdp[cpos] = '\0';
1554		p = lookup(sitetab, cp);
1555		cmdp[cpos] = c;
1556		if (p != NULL) {
1557			if (!CMD_IMPLEMENTED(p)) {
1558				reply(502, "SITE %s command not implemented.",
1559				    p->name);
1560				hasyyerrored = 1;
1561				break;
1562			}
1563			state = p->state;
1564			yylval.s = p->name;
1565			return (p->token);
1566		}
1567		break;
1568
1569	case OSTR:
1570		if (cmdp[cpos] == '\n') {
1571			state = EOLN;
1572			return (CRLF);
1573		}
1574		/* FALLTHROUGH */
1575
1576	case STR1:
1577	case ZSTR1:
1578	dostr1:
1579		if (cmdp[cpos] == ' ') {
1580			cpos++;
1581			state = state == OSTR ? STR2 : state+1;
1582			return (SP);
1583		}
1584		break;
1585
1586	case ZSTR2:
1587		if (cmdp[cpos] == '\n') {
1588			state = EOLN;
1589			return (CRLF);
1590		}
1591		/* FALLTHROUGH */
1592
1593	case STR2:
1594		cp = &cmdp[cpos];
1595		n = strlen(cp);
1596		cpos += n - 1;
1597		/*
1598		 * Make sure the string is nonempty and \n terminated.
1599		 */
1600		if (n > 1 && cmdp[cpos] == '\n') {
1601			cmdp[cpos] = '\0';
1602			yylval.s = ftpd_strdup(cp);
1603			cmdp[cpos] = '\n';
1604			state = ARGS;
1605			return (STRING);
1606		}
1607		break;
1608
1609	case NSTR:
1610		if (cmdp[cpos] == ' ') {
1611			cpos++;
1612			return (SP);
1613		}
1614		if (isdigit((unsigned char)cmdp[cpos])) {
1615			cp = &cmdp[cpos];
1616			while (isdigit((unsigned char)cmdp[++cpos]))
1617				;
1618			c = cmdp[cpos];
1619			cmdp[cpos] = '\0';
1620			yylval.u.i = atoi(cp);
1621			cmdp[cpos] = c;
1622			state = STR1;
1623			return (NUMBER);
1624		}
1625		state = STR1;
1626		goto dostr1;
1627
1628	case ARGS:
1629		if (isdigit((unsigned char)cmdp[cpos])) {
1630			cp = &cmdp[cpos];
1631			while (isdigit((unsigned char)cmdp[++cpos]))
1632				;
1633			c = cmdp[cpos];
1634			cmdp[cpos] = '\0';
1635			yylval.u.i = atoi(cp);
1636			yylval.u.ll = STRTOLL(cp, (char **)NULL, 10);
1637			cmdp[cpos] = c;
1638			return (NUMBER);
1639		}
1640		if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0
1641		    && !isalnum((unsigned char)cmdp[cpos + 3])) {
1642			cpos += 3;
1643			return (ALL);
1644		}
1645		switch (cmdp[cpos++]) {
1646
1647		case '\n':
1648			state = EOLN;
1649			return (CRLF);
1650
1651		case ' ':
1652			return (SP);
1653
1654		case ',':
1655			return (COMMA);
1656
1657		case 'A':
1658		case 'a':
1659			return (A);
1660
1661		case 'B':
1662		case 'b':
1663			return (B);
1664
1665		case 'C':
1666		case 'c':
1667			return (C);
1668
1669		case 'E':
1670		case 'e':
1671			return (E);
1672
1673		case 'F':
1674		case 'f':
1675			return (F);
1676
1677		case 'I':
1678		case 'i':
1679			return (I);
1680
1681		case 'L':
1682		case 'l':
1683			return (L);
1684
1685		case 'N':
1686		case 'n':
1687			return (N);
1688
1689		case 'P':
1690		case 'p':
1691			return (P);
1692
1693		case 'R':
1694		case 'r':
1695			return (R);
1696
1697		case 'S':
1698		case 's':
1699			return (S);
1700
1701		case 'T':
1702		case 't':
1703			return (T);
1704
1705		}
1706		break;
1707
1708	case NOARGS:
1709		if (cmdp[cpos] == '\n') {
1710			state = EOLN;
1711			return (CRLF);
1712		}
1713		c = cmdp[cpos];
1714		cmdp[cpos] = '\0';
1715		reply(501, "'%s' command does not take any arguments.", cmdp);
1716		hasyyerrored = 1;
1717		cmdp[cpos] = c;
1718		break;
1719
1720	case EOLN:
1721		state = CMD;
1722		return (0);
1723
1724	default:
1725		fatal("Unknown state in scanner.");
1726	}
1727	yyerror(NULL);
1728	state = CMD;
1729	return (0);
1730}
1731
1732/* ARGSUSED */
1733void
1734yyerror(char *s)
1735{
1736	char *cp;
1737
1738	if (hasyyerrored || is_oob)
1739		return;
1740	if ((cp = strchr(cmdp,'\n')) != NULL)
1741		*cp = '\0';
1742	reply(500, "'%s': command not understood.", cmdp);
1743	hasyyerrored = 1;
1744}
1745
1746static void
1747help(struct tab *ctab, const char *s)
1748{
1749	struct tab *c;
1750	int width, NCMDS;
1751	char *htype;
1752
1753	if (ctab == sitetab)
1754		htype = "SITE ";
1755	else
1756		htype = "";
1757	width = 0, NCMDS = 0;
1758	for (c = ctab; c->name != NULL; c++) {
1759		int len = strlen(c->name);
1760
1761		if (len > width)
1762			width = len;
1763		NCMDS++;
1764	}
1765	width = (width + 8) &~ 7;
1766	if (s == 0) {
1767		int i, j, w;
1768		int columns, lines;
1769
1770		reply(-214, "%s", "");
1771		reply(0, "The following %scommands are recognized.", htype);
1772		reply(0, "(`-' = not implemented, `+' = supports options)");
1773		columns = 76 / width;
1774		if (columns == 0)
1775			columns = 1;
1776		lines = (NCMDS + columns - 1) / columns;
1777		for (i = 0; i < lines; i++) {
1778			cprintf(stdout, "    ");
1779			for (j = 0; j < columns; j++) {
1780				c = ctab + j * lines + i;
1781				cprintf(stdout, "%s", c->name);
1782				w = strlen(c->name);
1783				if (! CMD_IMPLEMENTED(c)) {
1784					CPUTC('-', stdout);
1785					w++;
1786				}
1787				if (CMD_HAS_OPTIONS(c)) {
1788					CPUTC('+', stdout);
1789					w++;
1790				}
1791				if (c + lines >= &ctab[NCMDS])
1792					break;
1793				while (w < width) {
1794					CPUTC(' ', stdout);
1795					w++;
1796				}
1797			}
1798			cprintf(stdout, "\r\n");
1799		}
1800		(void) fflush(stdout);
1801		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1802		return;
1803	}
1804	c = lookup(ctab, s);
1805	if (c == (struct tab *)0) {
1806		reply(502, "Unknown command '%s'.", s);
1807		return;
1808	}
1809	if (CMD_IMPLEMENTED(c))
1810		reply(214, "Syntax: %s%s %s", htype, c->name, c->help);
1811	else
1812		reply(504, "%s%-*s\t%s; not implemented.", htype, width,
1813		    c->name, c->help);
1814}
1815
1816/*
1817 * Check that the structures used for a PORT, LPRT or EPRT command are
1818 * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks.
1819 * If family != -1 check that his_addr.su_family == family.
1820 */
1821static void
1822port_check(const char *cmd, int family)
1823{
1824	char h1[NI_MAXHOST], h2[NI_MAXHOST];
1825	char s1[NI_MAXHOST], s2[NI_MAXHOST];
1826#ifdef NI_WITHSCOPEID
1827	const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
1828#else
1829	const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
1830#endif
1831
1832	if (epsvall) {
1833		reply(501, "%s disallowed after EPSV ALL", cmd);
1834		return;
1835	}
1836
1837	if (family != -1 && his_addr.su_family != family) {
1838 port_check_fail:
1839		reply(500, "Illegal %s command rejected", cmd);
1840		return;
1841	}
1842
1843	if (data_dest.su_family != his_addr.su_family)
1844		goto port_check_fail;
1845
1846			/* be paranoid, if told so */
1847	if (CURCLASS_FLAGS_ISSET(checkportcmd)) {
1848#ifdef INET6
1849		/*
1850		 * be paranoid, there are getnameinfo implementation that does
1851		 * not present scopeid portion
1852		 */
1853		if (data_dest.su_family == AF_INET6 &&
1854		    data_dest.su_scope_id != his_addr.su_scope_id)
1855			goto port_check_fail;
1856#endif
1857
1858		if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len,
1859		    h1, sizeof(h1), s1, sizeof(s1), niflags))
1860			goto port_check_fail;
1861		if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
1862		    h2, sizeof(h2), s2, sizeof(s2), niflags))
1863			goto port_check_fail;
1864
1865		if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0)
1866			goto port_check_fail;
1867	}
1868
1869	usedefault = 0;
1870	if (pdata >= 0) {
1871		(void) close(pdata);
1872		pdata = -1;
1873	}
1874	reply(200, "%s command successful.", cmd);
1875}
1876