Home | History | Annotate | Line # | Download | only in ftpd
      1  1.96    jkoshy /*	$NetBSD: ftpcmd.y,v 1.96 2024/02/16 19:32:38 jkoshy Exp $	*/
      2  1.41     lukem 
      3  1.41     lukem /*-
      4  1.89     lukem  * Copyright (c) 1997-2009 The NetBSD Foundation, Inc.
      5  1.41     lukem  * All rights reserved.
      6  1.41     lukem  *
      7  1.41     lukem  * This code is derived from software contributed to The NetBSD Foundation
      8  1.41     lukem  * by Luke Mewburn.
      9  1.41     lukem  *
     10  1.41     lukem  * Redistribution and use in source and binary forms, with or without
     11  1.41     lukem  * modification, are permitted provided that the following conditions
     12  1.41     lukem  * are met:
     13  1.41     lukem  * 1. Redistributions of source code must retain the above copyright
     14  1.41     lukem  *    notice, this list of conditions and the following disclaimer.
     15  1.41     lukem  * 2. Redistributions in binary form must reproduce the above copyright
     16  1.41     lukem  *    notice, this list of conditions and the following disclaimer in the
     17  1.41     lukem  *    documentation and/or other materials provided with the distribution.
     18  1.41     lukem  *
     19  1.41     lukem  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  1.41     lukem  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  1.41     lukem  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  1.41     lukem  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  1.41     lukem  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  1.41     lukem  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  1.41     lukem  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  1.41     lukem  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  1.41     lukem  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  1.41     lukem  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  1.41     lukem  * POSSIBILITY OF SUCH DAMAGE.
     30  1.41     lukem  */
     31   1.5       cgd 
     32   1.1       cgd /*
     33   1.4   deraadt  * Copyright (c) 1985, 1988, 1993, 1994
     34   1.4   deraadt  *	The Regents of the University of California.  All rights reserved.
     35   1.1       cgd  *
     36   1.1       cgd  * Redistribution and use in source and binary forms, with or without
     37   1.1       cgd  * modification, are permitted provided that the following conditions
     38   1.1       cgd  * are met:
     39   1.1       cgd  * 1. Redistributions of source code must retain the above copyright
     40   1.1       cgd  *    notice, this list of conditions and the following disclaimer.
     41   1.1       cgd  * 2. Redistributions in binary form must reproduce the above copyright
     42   1.1       cgd  *    notice, this list of conditions and the following disclaimer in the
     43   1.1       cgd  *    documentation and/or other materials provided with the distribution.
     44  1.77       agc  * 3. Neither the name of the University nor the names of its contributors
     45   1.1       cgd  *    may be used to endorse or promote products derived from this software
     46   1.1       cgd  *    without specific prior written permission.
     47   1.1       cgd  *
     48   1.1       cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     49   1.1       cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     50   1.1       cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     51   1.1       cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     52   1.1       cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     53   1.1       cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     54   1.1       cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     55   1.1       cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     56   1.1       cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     57   1.1       cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     58   1.1       cgd  * SUCH DAMAGE.
     59   1.1       cgd  *
     60   1.4   deraadt  *	@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94
     61   1.1       cgd  */
     62   1.1       cgd 
     63   1.1       cgd /*
     64   1.1       cgd  * Grammar for FTP commands.
     65   1.1       cgd  * See RFC 959.
     66   1.1       cgd  */
     67   1.1       cgd 
     68   1.1       cgd %{
     69  1.13  christos #include <sys/cdefs.h>
     70   1.1       cgd 
     71   1.1       cgd #ifndef lint
     72   1.5       cgd #if 0
     73   1.4   deraadt static char sccsid[] = "@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94";
     74   1.5       cgd #else
     75  1.96    jkoshy __RCSID("$NetBSD: ftpcmd.y,v 1.96 2024/02/16 19:32:38 jkoshy Exp $");
     76   1.5       cgd #endif
     77   1.1       cgd #endif /* not lint */
     78   1.1       cgd 
     79   1.1       cgd #include <sys/param.h>
     80   1.1       cgd #include <sys/socket.h>
     81   1.1       cgd #include <sys/stat.h>
     82   1.4   deraadt 
     83   1.1       cgd #include <netinet/in.h>
     84   1.1       cgd #include <arpa/ftp.h>
     85  1.15       mrg #include <arpa/inet.h>
     86   1.4   deraadt 
     87   1.4   deraadt #include <ctype.h>
     88   1.4   deraadt #include <errno.h>
     89   1.4   deraadt #include <pwd.h>
     90   1.4   deraadt #include <stdio.h>
     91   1.4   deraadt #include <stdlib.h>
     92   1.4   deraadt #include <string.h>
     93   1.1       cgd #include <syslog.h>
     94   1.1       cgd #include <time.h>
     95  1.18     lukem #include <tzfile.h>
     96   1.1       cgd #include <unistd.h>
     97  1.32    itojun #include <netdb.h>
     98  1.26  explorer 
     99  1.26  explorer #ifdef KERBEROS5
    100  1.36  christos #include <krb5/krb5.h>
    101  1.26  explorer #endif
    102   1.4   deraadt 
    103   1.4   deraadt #include "extern.h"
    104  1.45     lukem #include "version.h"
    105   1.1       cgd 
    106   1.1       cgd static	int cmd_type;
    107   1.1       cgd static	int cmd_form;
    108   1.1       cgd static	int cmd_bytesz;
    109  1.44     lukem 
    110  1.60     aidan char	cbuf[FTP_BUFLEN];
    111  1.60     aidan char	*cmdp;
    112   1.1       cgd char	*fromname;
    113  1.24     lukem 
    114  1.82     lukem extern int	epsvall;
    115  1.82     lukem struct tab	sitetab[];
    116  1.82     lukem 
    117  1.82     lukem static	int	check_write(const char *, int);
    118  1.82     lukem static	void	help(struct tab *, const char *);
    119  1.82     lukem static	void	port_check(const char *, int);
    120  1.82     lukem 	int	yylex(void);
    121  1.82     lukem 
    122   1.1       cgd %}
    123   1.1       cgd 
    124   1.4   deraadt %union {
    125  1.70     lukem 	struct {
    126  1.74     lukem 		LLT	ll;
    127  1.70     lukem 		int	i;
    128  1.70     lukem 	} u;
    129  1.89     lukem 	char *s;
    130  1.89     lukem 	const char *cs;
    131   1.4   deraadt }
    132   1.4   deraadt 
    133   1.1       cgd %token
    134   1.1       cgd 	A	B	C	E	F	I
    135   1.1       cgd 	L	N	P	R	S	T
    136   1.1       cgd 
    137  1.80     lukem 	SP	CRLF	COMMA	ALL
    138   1.1       cgd 
    139  1.23     lukem 	USER	PASS	ACCT	CWD	CDUP	SMNT
    140  1.23     lukem 	QUIT	REIN	PORT	PASV	TYPE	STRU
    141  1.23     lukem 	MODE	RETR	STOR	STOU	APPE	ALLO
    142  1.23     lukem 	REST	RNFR	RNTO	ABOR	DELE	RMD
    143  1.23     lukem 	MKD	PWD	LIST	NLST	SITE	SYST
    144  1.23     lukem 	STAT	HELP	NOOP
    145  1.23     lukem 
    146  1.25     lukem 	AUTH	ADAT	PROT	PBSZ	CCC	MIC
    147  1.25     lukem 	CONF	ENC
    148  1.25     lukem 
    149  1.23     lukem 	FEAT	OPTS
    150  1.21     lukem 
    151  1.47     lukem 	SIZE	MDTM	MLST	MLSD
    152   1.1       cgd 
    153  1.32    itojun 	LPRT	LPSV	EPRT	EPSV
    154  1.32    itojun 
    155  1.23     lukem 	MAIL	MLFL	MRCP	MRSQ	MSAM	MSND
    156  1.23     lukem 	MSOM
    157  1.23     lukem 
    158  1.41     lukem 	CHMOD	IDLE	RATEGET	RATEPUT	UMASK
    159   1.1       cgd 
    160   1.1       cgd 	LEXERR
    161   1.1       cgd 
    162   1.4   deraadt %token	<s> STRING
    163  1.70     lukem %token	<u> NUMBER
    164   1.4   deraadt 
    165  1.70     lukem %type	<u.i> check_login octal_number byte_size
    166  1.70     lukem %type	<u.i> struct_code mode_code type_code form_code decimal_integer
    167   1.4   deraadt %type	<s> pathstring pathname password username
    168  1.25     lukem %type	<s> mechanism_name base64data prot_code
    169   1.4   deraadt 
    170  1.60     aidan %start	cmd_sel
    171   1.1       cgd 
    172   1.1       cgd %%
    173   1.1       cgd 
    174  1.60     aidan cmd_sel
    175  1.60     aidan 	: cmd
    176   1.4   deraadt 		{
    177  1.78     lukem 			REASSIGN(fromname, NULL);
    178   1.1       cgd 			restart_point = (off_t) 0;
    179   1.1       cgd 		}
    180  1.23     lukem 
    181  1.60     aidan 	| rcmd
    182  1.23     lukem 
    183   1.1       cgd 	;
    184   1.1       cgd 
    185   1.4   deraadt cmd
    186  1.23     lukem 						/* RFC 959 */
    187   1.4   deraadt 	: USER SP username CRLF
    188   1.4   deraadt 		{
    189   1.4   deraadt 			user($3);
    190   1.4   deraadt 			free($3);
    191   1.4   deraadt 		}
    192  1.23     lukem 
    193   1.4   deraadt 	| PASS SP password CRLF
    194   1.4   deraadt 		{
    195   1.4   deraadt 			pass($3);
    196  1.94       shm 			explicit_memset($3, 0, strlen($3));
    197   1.4   deraadt 			free($3);
    198   1.1       cgd 		}
    199  1.23     lukem 
    200  1.23     lukem 	| CWD check_login CRLF
    201  1.23     lukem 		{
    202  1.23     lukem 			if ($2)
    203  1.51     lukem 				cwd(homedir);
    204  1.23     lukem 		}
    205  1.23     lukem 
    206  1.23     lukem 	| CWD check_login SP pathname CRLF
    207  1.23     lukem 		{
    208  1.23     lukem 			if ($2 && $4 != NULL)
    209  1.23     lukem 				cwd($4);
    210  1.23     lukem 			if ($4 != NULL)
    211  1.23     lukem 				free($4);
    212  1.23     lukem 		}
    213  1.23     lukem 
    214  1.23     lukem 	| CDUP check_login CRLF
    215  1.23     lukem 		{
    216  1.23     lukem 			if ($2)
    217  1.23     lukem 				cwd("..");
    218  1.23     lukem 		}
    219  1.23     lukem 
    220  1.23     lukem 	| QUIT CRLF
    221  1.23     lukem 		{
    222  1.27     lukem 			if (logged_in) {
    223  1.49  sommerfe 				reply(-221, "%s", "");
    224  1.48     lukem 				reply(0,
    225  1.55     lukem  "Data traffic for this session was " LLF " byte%s in " LLF " file%s.",
    226  1.55     lukem 				    (LLT)total_data, PLURAL(total_data),
    227  1.55     lukem 				    (LLT)total_files, PLURAL(total_files));
    228  1.48     lukem 				reply(0,
    229  1.55     lukem  "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.",
    230  1.55     lukem 				    (LLT)total_bytes, PLURAL(total_bytes),
    231  1.55     lukem 				    (LLT)total_xfers, PLURAL(total_xfers));
    232  1.31     lukem 			}
    233  1.31     lukem 			reply(221,
    234  1.31     lukem 			    "Thank you for using the FTP service on %s.",
    235  1.31     lukem 			    hostname);
    236  1.59     lukem 			if (logged_in && logging) {
    237  1.27     lukem 				syslog(LOG_INFO,
    238  1.55     lukem 		"Data traffic: " LLF " byte%s in " LLF " file%s",
    239  1.55     lukem 				    (LLT)total_data, PLURAL(total_data),
    240  1.55     lukem 				    (LLT)total_files, PLURAL(total_files));
    241  1.27     lukem 				syslog(LOG_INFO,
    242  1.55     lukem 		"Total traffic: " LLF " byte%s in " LLF " transfer%s",
    243  1.55     lukem 				    (LLT)total_bytes, PLURAL(total_bytes),
    244  1.55     lukem 				    (LLT)total_xfers, PLURAL(total_xfers));
    245  1.27     lukem 			}
    246  1.31     lukem 
    247  1.23     lukem 			dologout(0);
    248  1.23     lukem 		}
    249  1.23     lukem 
    250  1.15       mrg 	| PORT check_login SP host_port CRLF
    251   1.4   deraadt 		{
    252  1.52     lukem 			if ($2)
    253  1.52     lukem 				port_check("PORT", AF_INET);
    254  1.32    itojun 		}
    255  1.32    itojun 
    256  1.34    itojun 	| LPRT check_login SP host_long_port4 CRLF
    257  1.32    itojun 		{
    258  1.52     lukem 			if ($2)
    259  1.52     lukem 				port_check("LPRT", AF_INET);
    260  1.34    itojun 		}
    261  1.34    itojun 
    262  1.34    itojun 	| LPRT check_login SP host_long_port6 CRLF
    263  1.34    itojun 		{
    264  1.57     lukem #ifdef INET6
    265  1.52     lukem 			if ($2)
    266  1.52     lukem 				port_check("LPRT", AF_INET6);
    267  1.57     lukem #else
    268  1.57     lukem 			reply(500, "IPv6 support not available.");
    269  1.57     lukem #endif
    270   1.1       cgd 		}
    271  1.23     lukem 
    272  1.32    itojun 	| EPRT check_login SP STRING CRLF
    273  1.32    itojun 		{
    274  1.42     lukem 			if ($2) {
    275  1.54    itojun 				if (extended_port($4) == 0)
    276  1.54    itojun 					port_check("EPRT", -1);
    277  1.42     lukem 			}
    278  1.42     lukem 			free($4);
    279  1.32    itojun 		}
    280  1.32    itojun 
    281  1.20        tv 	| PASV check_login CRLF
    282   1.4   deraadt 		{
    283  1.42     lukem 			if ($2) {
    284  1.56     lukem 				if (CURCLASS_FLAGS_ISSET(passive))
    285  1.42     lukem 					passive();
    286  1.42     lukem 				else
    287  1.42     lukem 					reply(500, "PASV mode not available.");
    288  1.20        tv 			}
    289   1.1       cgd 		}
    290  1.23     lukem 
    291  1.42     lukem 	| LPSV check_login CRLF
    292  1.32    itojun 		{
    293  1.42     lukem 			if ($2) {
    294  1.69        tv 				if (CURCLASS_FLAGS_ISSET(passive)) {
    295  1.69        tv 					if (epsvall)
    296  1.69        tv 						reply(501,
    297  1.69        tv 						    "LPSV disallowed after EPSV ALL");
    298  1.69        tv 					else
    299  1.69        tv 						long_passive("LPSV", PF_UNSPEC);
    300  1.69        tv 				} else
    301  1.69        tv 					reply(500, "LPSV mode not available.");
    302  1.42     lukem 			}
    303  1.32    itojun 		}
    304  1.32    itojun 
    305  1.42     lukem 	| EPSV check_login SP NUMBER CRLF
    306  1.32    itojun 		{
    307  1.69        tv 			if ($2) {
    308  1.69        tv 				if (CURCLASS_FLAGS_ISSET(passive))
    309  1.70     lukem 					long_passive("EPSV",
    310  1.70     lukem 					    epsvproto2af($4.i));
    311  1.69        tv 				else
    312  1.69        tv 					reply(500, "EPSV mode not available.");
    313  1.69        tv 			}
    314  1.32    itojun 		}
    315  1.32    itojun 
    316  1.42     lukem 	| EPSV check_login SP ALL CRLF
    317  1.32    itojun 		{
    318  1.42     lukem 			if ($2) {
    319  1.69        tv 				if (CURCLASS_FLAGS_ISSET(passive)) {
    320  1.70     lukem 					reply(200,
    321  1.70     lukem 					    "EPSV ALL command successful.");
    322  1.69        tv 					epsvall++;
    323  1.69        tv 				} else
    324  1.69        tv 					reply(500, "EPSV mode not available.");
    325  1.32    itojun 			}
    326  1.32    itojun 		}
    327  1.32    itojun 
    328  1.42     lukem 	| EPSV check_login CRLF
    329  1.32    itojun 		{
    330  1.69        tv 			if ($2) {
    331  1.69        tv 				if (CURCLASS_FLAGS_ISSET(passive))
    332  1.69        tv 					long_passive("EPSV", PF_UNSPEC);
    333  1.69        tv 				else
    334  1.69        tv 					reply(500, "EPSV mode not available.");
    335  1.69        tv 			}
    336  1.32    itojun 		}
    337  1.32    itojun 
    338  1.42     lukem 	| TYPE check_login SP type_code CRLF
    339   1.4   deraadt 		{
    340  1.42     lukem 			if ($2) {
    341  1.42     lukem 
    342   1.1       cgd 			switch (cmd_type) {
    343   1.1       cgd 
    344   1.1       cgd 			case TYPE_A:
    345   1.1       cgd 				if (cmd_form == FORM_N) {
    346   1.1       cgd 					reply(200, "Type set to A.");
    347   1.1       cgd 					type = cmd_type;
    348   1.1       cgd 					form = cmd_form;
    349   1.1       cgd 				} else
    350   1.1       cgd 					reply(504, "Form must be N.");
    351   1.1       cgd 				break;
    352   1.1       cgd 
    353   1.1       cgd 			case TYPE_E:
    354   1.1       cgd 				reply(504, "Type E not implemented.");
    355   1.1       cgd 				break;
    356   1.1       cgd 
    357   1.1       cgd 			case TYPE_I:
    358   1.1       cgd 				reply(200, "Type set to I.");
    359   1.1       cgd 				type = cmd_type;
    360   1.1       cgd 				break;
    361   1.1       cgd 
    362   1.1       cgd 			case TYPE_L:
    363   1.1       cgd #if NBBY == 8
    364   1.1       cgd 				if (cmd_bytesz == 8) {
    365   1.1       cgd 					reply(200,
    366   1.1       cgd 					    "Type set to L (byte size 8).");
    367   1.1       cgd 					type = cmd_type;
    368   1.1       cgd 				} else
    369   1.1       cgd 					reply(504, "Byte size must be 8.");
    370   1.1       cgd #else /* NBBY == 8 */
    371   1.1       cgd 				UNIMPLEMENTED for NBBY != 8
    372   1.1       cgd #endif /* NBBY == 8 */
    373   1.1       cgd 			}
    374  1.42     lukem 
    375  1.42     lukem 			}
    376   1.1       cgd 		}
    377  1.23     lukem 
    378  1.42     lukem 	| STRU check_login SP struct_code CRLF
    379   1.4   deraadt 		{
    380  1.42     lukem 			if ($2) {
    381  1.42     lukem 				switch ($4) {
    382   1.1       cgd 
    383  1.42     lukem 				case STRU_F:
    384  1.42     lukem 					reply(200, "STRU F ok.");
    385  1.42     lukem 					break;
    386   1.1       cgd 
    387  1.42     lukem 				default:
    388  1.42     lukem 					reply(504, "Unimplemented STRU type.");
    389  1.42     lukem 				}
    390   1.1       cgd 			}
    391   1.1       cgd 		}
    392  1.23     lukem 
    393  1.42     lukem 	| MODE check_login SP mode_code CRLF
    394   1.4   deraadt 		{
    395  1.42     lukem 			if ($2) {
    396  1.42     lukem 				switch ($4) {
    397   1.1       cgd 
    398  1.42     lukem 				case MODE_S:
    399  1.42     lukem 					reply(200, "MODE S ok.");
    400  1.42     lukem 					break;
    401   1.1       cgd 
    402  1.42     lukem 				default:
    403  1.42     lukem 					reply(502, "Unimplemented MODE type.");
    404  1.42     lukem 				}
    405   1.1       cgd 			}
    406   1.1       cgd 		}
    407  1.23     lukem 
    408   1.4   deraadt 	| RETR check_login SP pathname CRLF
    409   1.4   deraadt 		{
    410   1.1       cgd 			if ($2 && $4 != NULL)
    411  1.22     lukem 				retrieve(NULL, $4);
    412   1.1       cgd 			if ($4 != NULL)
    413   1.4   deraadt 				free($4);
    414   1.1       cgd 		}
    415  1.23     lukem 
    416  1.56     lukem 	| STOR SP pathname CRLF
    417   1.4   deraadt 		{
    418  1.56     lukem 			if (check_write($3, 1))
    419  1.56     lukem 				store($3, "w", 0);
    420  1.56     lukem 			if ($3 != NULL)
    421  1.56     lukem 				free($3);
    422   1.1       cgd 		}
    423  1.23     lukem 
    424  1.56     lukem 	| STOU SP pathname CRLF
    425   1.4   deraadt 		{
    426  1.56     lukem 			if (check_write($3, 1))
    427  1.56     lukem 				store($3, "w", 1);
    428  1.56     lukem 			if ($3 != NULL)
    429  1.56     lukem 				free($3);
    430   1.1       cgd 		}
    431  1.23     lukem 
    432  1.56     lukem 	| APPE SP pathname CRLF
    433   1.4   deraadt 		{
    434  1.56     lukem 			if (check_write($3, 1))
    435  1.56     lukem 				store($3, "a", 0);
    436  1.56     lukem 			if ($3 != NULL)
    437  1.56     lukem 				free($3);
    438   1.1       cgd 		}
    439  1.23     lukem 
    440  1.42     lukem 	| ALLO check_login SP NUMBER CRLF
    441   1.4   deraadt 		{
    442  1.42     lukem 			if ($2)
    443  1.42     lukem 				reply(202, "ALLO command ignored.");
    444   1.1       cgd 		}
    445  1.23     lukem 
    446  1.42     lukem 	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
    447   1.4   deraadt 		{
    448  1.42     lukem 			if ($2)
    449  1.42     lukem 				reply(202, "ALLO command ignored.");
    450   1.1       cgd 		}
    451  1.23     lukem 
    452  1.56     lukem 	| RNTO SP pathname CRLF
    453   1.4   deraadt 		{
    454  1.56     lukem 			if (check_write($3, 0)) {
    455  1.42     lukem 				if (fromname) {
    456  1.56     lukem 					renamecmd(fromname, $3);
    457  1.78     lukem 					REASSIGN(fromname, NULL);
    458  1.42     lukem 				} else {
    459  1.42     lukem 					reply(503, "Bad sequence of commands.");
    460  1.42     lukem 				}
    461   1.1       cgd 			}
    462  1.56     lukem 			if ($3 != NULL)
    463  1.56     lukem 				free($3);
    464   1.1       cgd 		}
    465  1.23     lukem 
    466  1.42     lukem 	| ABOR check_login CRLF
    467   1.4   deraadt 		{
    468  1.60     aidan 			if (is_oob)
    469  1.60     aidan 				abor();
    470  1.60     aidan 			else if ($2)
    471  1.42     lukem 				reply(225, "ABOR command successful.");
    472   1.1       cgd 		}
    473  1.23     lukem 
    474  1.56     lukem 	| DELE SP pathname CRLF
    475   1.4   deraadt 		{
    476  1.56     lukem 			if (check_write($3, 0))
    477  1.56     lukem 				delete($3);
    478  1.56     lukem 			if ($3 != NULL)
    479  1.56     lukem 				free($3);
    480   1.1       cgd 		}
    481  1.23     lukem 
    482  1.56     lukem 	| RMD SP pathname CRLF
    483   1.4   deraadt 		{
    484  1.56     lukem 			if (check_write($3, 0))
    485  1.56     lukem 				removedir($3);
    486  1.56     lukem 			if ($3 != NULL)
    487  1.56     lukem 				free($3);
    488   1.1       cgd 		}
    489   1.1       cgd 
    490  1.56     lukem 	| MKD SP pathname CRLF
    491   1.4   deraadt 		{
    492  1.56     lukem 			if (check_write($3, 0))
    493  1.56     lukem 				makedir($3);
    494  1.56     lukem 			if ($3 != NULL)
    495  1.56     lukem 				free($3);
    496   1.1       cgd 		}
    497  1.23     lukem 
    498  1.23     lukem 	| PWD check_login CRLF
    499  1.23     lukem 		{
    500  1.23     lukem 			if ($2)
    501  1.23     lukem 				pwd();
    502  1.23     lukem 		}
    503  1.23     lukem 
    504  1.23     lukem 	| LIST check_login CRLF
    505  1.23     lukem 		{
    506  1.89     lukem 			const char *argv[] = { INTERNAL_LS, "-lgA", NULL };
    507  1.40     lukem 
    508  1.83  ginsbach 			if (CURCLASS_FLAGS_ISSET(hidesymlinks))
    509  1.83  ginsbach 				argv[1] = "-LlgA";
    510  1.23     lukem 			if ($2)
    511  1.40     lukem 				retrieve(argv, "");
    512  1.23     lukem 		}
    513  1.23     lukem 
    514  1.23     lukem 	| LIST check_login SP pathname CRLF
    515   1.4   deraadt 		{
    516  1.89     lukem 			const char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL };
    517  1.40     lukem 
    518  1.83  ginsbach 			if (CURCLASS_FLAGS_ISSET(hidesymlinks))
    519  1.83  ginsbach 				argv[1] = "-LlgA";
    520  1.40     lukem 			if ($2 && $4 != NULL) {
    521  1.40     lukem 				argv[2] = $4;
    522  1.40     lukem 				retrieve(argv, $4);
    523  1.40     lukem 			}
    524   1.1       cgd 			if ($4 != NULL)
    525   1.4   deraadt 				free($4);
    526   1.1       cgd 		}
    527  1.23     lukem 
    528  1.23     lukem 	| NLST check_login CRLF
    529   1.4   deraadt 		{
    530   1.1       cgd 			if ($2)
    531  1.23     lukem 				send_file_list(".");
    532   1.1       cgd 		}
    533  1.23     lukem 
    534  1.51     lukem 	| NLST check_login SP pathname CRLF
    535   1.4   deraadt 		{
    536  1.42     lukem 			if ($2)
    537  1.23     lukem 				send_file_list($4);
    538  1.42     lukem 			free($4);
    539   1.1       cgd 		}
    540  1.23     lukem 
    541   1.4   deraadt 	| SITE SP HELP CRLF
    542   1.4   deraadt 		{
    543  1.22     lukem 			help(sitetab, NULL);
    544   1.1       cgd 		}
    545  1.23     lukem 
    546  1.56     lukem 	| SITE SP CHMOD SP octal_number SP pathname CRLF
    547   1.4   deraadt 		{
    548  1.56     lukem 			if (check_write($7, 0)) {
    549  1.79     lukem 				if (($5 == -1) || ($5 > 0777))
    550   1.1       cgd 					reply(501,
    551   1.1       cgd 				"CHMOD: Mode value must be between 0 and 0777");
    552  1.56     lukem 				else if (chmod($7, $5) < 0)
    553  1.56     lukem 					perror_reply(550, $7);
    554   1.1       cgd 				else
    555   1.1       cgd 					reply(200, "CHMOD command successful.");
    556   1.1       cgd 			}
    557  1.56     lukem 			if ($7 != NULL)
    558  1.56     lukem 				free($7);
    559   1.1       cgd 		}
    560  1.23     lukem 
    561  1.41     lukem 	| SITE SP HELP SP STRING CRLF
    562  1.41     lukem 		{
    563  1.41     lukem 			help(sitetab, $5);
    564  1.42     lukem 			free($5);
    565  1.41     lukem 		}
    566  1.41     lukem 
    567  1.42     lukem 	| SITE SP IDLE check_login CRLF
    568   1.4   deraadt 		{
    569  1.42     lukem 			if ($4) {
    570  1.42     lukem 				reply(200,
    571  1.72     lukem 				    "Current IDLE time limit is " LLF
    572  1.72     lukem 				    " seconds; max " LLF,
    573  1.72     lukem 				    (LLT)curclass.timeout,
    574  1.72     lukem 				    (LLT)curclass.maxtimeout);
    575  1.42     lukem 			}
    576   1.1       cgd 		}
    577  1.23     lukem 
    578  1.42     lukem 	| SITE SP IDLE check_login SP NUMBER CRLF
    579   1.4   deraadt 		{
    580  1.42     lukem 			if ($4) {
    581  1.70     lukem 				if ($6.i < 30 || $6.i > curclass.maxtimeout) {
    582  1.42     lukem 					reply(501,
    583  1.72     lukem 				"IDLE time limit must be between 30 and "
    584  1.72     lukem 					    LLF " seconds",
    585  1.72     lukem 					    (LLT)curclass.maxtimeout);
    586  1.42     lukem 				} else {
    587  1.70     lukem 					curclass.timeout = $6.i;
    588  1.42     lukem 					(void) alarm(curclass.timeout);
    589  1.42     lukem 					reply(200,
    590  1.72     lukem 					    "IDLE time limit set to "
    591  1.72     lukem 					    LLF " seconds",
    592  1.72     lukem 					    (LLT)curclass.timeout);
    593  1.42     lukem 				}
    594   1.1       cgd 			}
    595   1.1       cgd 		}
    596  1.23     lukem 
    597  1.42     lukem 	| SITE SP RATEGET check_login CRLF
    598  1.41     lukem 		{
    599  1.42     lukem 			if ($4) {
    600  1.56     lukem 				reply(200,
    601  1.56     lukem 				    "Current RATEGET is " LLF " bytes/sec",
    602  1.56     lukem 				    (LLT)curclass.rateget);
    603  1.42     lukem 			}
    604  1.41     lukem 		}
    605  1.41     lukem 
    606  1.42     lukem 	| SITE SP RATEGET check_login SP STRING CRLF
    607  1.41     lukem 		{
    608  1.72     lukem 			char errbuf[100];
    609  1.42     lukem 			char *p = $6;
    610  1.56     lukem 			LLT rate;
    611  1.41     lukem 
    612  1.42     lukem 			if ($4) {
    613  1.72     lukem 				rate = strsuftollx("RATEGET", p, 0,
    614  1.72     lukem 				    curclass.maxrateget
    615  1.72     lukem 				    ? curclass.maxrateget
    616  1.72     lukem 				    : LLTMAX, errbuf, sizeof(errbuf));
    617  1.72     lukem 				if (errbuf[0])
    618  1.72     lukem 					reply(501, "%s", errbuf);
    619  1.42     lukem 				else {
    620  1.42     lukem 					curclass.rateget = rate;
    621  1.42     lukem 					reply(200,
    622  1.56     lukem 					    "RATEGET set to " LLF " bytes/sec",
    623  1.56     lukem 					    (LLT)curclass.rateget);
    624  1.42     lukem 				}
    625  1.41     lukem 			}
    626  1.42     lukem 			free($6);
    627  1.41     lukem 		}
    628  1.41     lukem 
    629  1.42     lukem 	| SITE SP RATEPUT check_login CRLF
    630  1.41     lukem 		{
    631  1.42     lukem 			if ($4) {
    632  1.56     lukem 				reply(200,
    633  1.56     lukem 				    "Current RATEPUT is " LLF " bytes/sec",
    634  1.56     lukem 				    (LLT)curclass.rateput);
    635  1.42     lukem 			}
    636  1.41     lukem 		}
    637  1.41     lukem 
    638  1.42     lukem 	| SITE SP RATEPUT check_login SP STRING CRLF
    639  1.41     lukem 		{
    640  1.72     lukem 			char errbuf[100];
    641  1.42     lukem 			char *p = $6;
    642  1.56     lukem 			LLT rate;
    643  1.41     lukem 
    644  1.42     lukem 			if ($4) {
    645  1.72     lukem 				rate = strsuftollx("RATEPUT", p, 0,
    646  1.72     lukem 				    curclass.maxrateput
    647  1.72     lukem 				    ? curclass.maxrateput
    648  1.72     lukem 				    : LLTMAX, errbuf, sizeof(errbuf));
    649  1.72     lukem 				if (errbuf[0])
    650  1.72     lukem 					reply(501, "%s", errbuf);
    651  1.42     lukem 				else {
    652  1.42     lukem 					curclass.rateput = rate;
    653  1.42     lukem 					reply(200,
    654  1.56     lukem 					    "RATEPUT set to " LLF " bytes/sec",
    655  1.56     lukem 					    (LLT)curclass.rateput);
    656  1.42     lukem 				}
    657  1.41     lukem 			}
    658  1.42     lukem 			free($6);
    659  1.41     lukem 		}
    660  1.41     lukem 
    661  1.41     lukem 	| SITE SP UMASK check_login CRLF
    662  1.41     lukem 		{
    663  1.41     lukem 			int oldmask;
    664  1.41     lukem 
    665  1.41     lukem 			if ($4) {
    666  1.41     lukem 				oldmask = umask(0);
    667  1.41     lukem 				(void) umask(oldmask);
    668  1.41     lukem 				reply(200, "Current UMASK is %03o", oldmask);
    669  1.41     lukem 			}
    670  1.41     lukem 		}
    671  1.41     lukem 
    672  1.56     lukem 	| SITE SP UMASK check_login SP octal_number CRLF
    673  1.41     lukem 		{
    674  1.41     lukem 			int oldmask;
    675  1.41     lukem 
    676  1.67     lukem 			if ($4 && check_write("", 0)) {
    677  1.41     lukem 				if (($6 == -1) || ($6 > 0777)) {
    678  1.41     lukem 					reply(501, "Bad UMASK value");
    679  1.41     lukem 				} else {
    680  1.41     lukem 					oldmask = umask($6);
    681  1.41     lukem 					reply(200,
    682  1.41     lukem 					    "UMASK set to %03o (was %03o)",
    683  1.41     lukem 					    $6, oldmask);
    684  1.41     lukem 				}
    685  1.41     lukem 			}
    686  1.41     lukem 		}
    687  1.41     lukem 
    688  1.23     lukem 	| SYST CRLF
    689  1.23     lukem 		{
    690  1.52     lukem 			if (EMPTYSTR(version))
    691  1.52     lukem 				reply(215, "UNIX Type: L%d", NBBY);
    692  1.52     lukem 			else
    693  1.52     lukem 				reply(215, "UNIX Type: L%d Version: %s", NBBY,
    694  1.52     lukem 				    version);
    695  1.23     lukem 		}
    696  1.23     lukem 
    697  1.23     lukem 	| STAT check_login SP pathname CRLF
    698   1.4   deraadt 		{
    699   1.1       cgd 			if ($2 && $4 != NULL)
    700  1.23     lukem 				statfilecmd($4);
    701   1.1       cgd 			if ($4 != NULL)
    702   1.4   deraadt 				free($4);
    703   1.1       cgd 		}
    704  1.23     lukem 
    705  1.23     lukem 	| STAT CRLF
    706  1.23     lukem 		{
    707  1.60     aidan 			if (is_oob)
    708  1.60     aidan 				statxfer();
    709  1.60     aidan 			else
    710  1.60     aidan 				statcmd();
    711  1.23     lukem 		}
    712  1.23     lukem 
    713  1.23     lukem 	| HELP CRLF
    714  1.23     lukem 		{
    715  1.23     lukem 			help(cmdtab, NULL);
    716  1.23     lukem 		}
    717  1.23     lukem 
    718  1.23     lukem 	| HELP SP STRING CRLF
    719  1.23     lukem 		{
    720  1.23     lukem 			char *cp = $3;
    721  1.23     lukem 
    722  1.23     lukem 			if (strncasecmp(cp, "SITE", 4) == 0) {
    723  1.23     lukem 				cp = $3 + 4;
    724  1.23     lukem 				if (*cp == ' ')
    725  1.23     lukem 					cp++;
    726  1.23     lukem 				if (*cp)
    727  1.23     lukem 					help(sitetab, cp);
    728  1.23     lukem 				else
    729  1.23     lukem 					help(sitetab, NULL);
    730  1.23     lukem 			} else
    731  1.23     lukem 				help(cmdtab, $3);
    732  1.42     lukem 			free($3);
    733  1.23     lukem 		}
    734  1.23     lukem 
    735  1.23     lukem 	| NOOP CRLF
    736  1.23     lukem 		{
    737  1.23     lukem 			reply(200, "NOOP command successful.");
    738  1.23     lukem 		}
    739  1.23     lukem 
    740  1.25     lukem 						/* RFC 2228 */
    741  1.25     lukem 	| AUTH SP mechanism_name CRLF
    742  1.25     lukem 		{
    743  1.25     lukem 			reply(502, "RFC 2228 authentication not implemented.");
    744  1.42     lukem 			free($3);
    745  1.25     lukem 		}
    746  1.25     lukem 
    747  1.25     lukem 	| ADAT SP base64data CRLF
    748  1.25     lukem 		{
    749  1.25     lukem 			reply(503,
    750  1.25     lukem 			    "Please set authentication state with AUTH.");
    751  1.42     lukem 			free($3);
    752  1.25     lukem 		}
    753  1.25     lukem 
    754  1.25     lukem 	| PROT SP prot_code CRLF
    755  1.25     lukem 		{
    756  1.25     lukem 			reply(503,
    757  1.25     lukem 			    "Please set protection buffer size with PBSZ.");
    758  1.42     lukem 			free($3);
    759  1.25     lukem 		}
    760  1.25     lukem 
    761  1.25     lukem 	| PBSZ SP decimal_integer CRLF
    762  1.25     lukem 		{
    763  1.25     lukem 			reply(503,
    764  1.25     lukem 			    "Please set authentication state with AUTH.");
    765  1.25     lukem 		}
    766  1.25     lukem 
    767  1.25     lukem 	| CCC CRLF
    768  1.25     lukem 		{
    769  1.25     lukem 			reply(533, "No protection enabled.");
    770  1.25     lukem 		}
    771  1.25     lukem 
    772  1.25     lukem 	| MIC SP base64data CRLF
    773  1.25     lukem 		{
    774  1.25     lukem 			reply(502, "RFC 2228 authentication not implemented.");
    775  1.42     lukem 			free($3);
    776  1.25     lukem 		}
    777  1.25     lukem 
    778  1.25     lukem 	| CONF SP base64data CRLF
    779  1.25     lukem 		{
    780  1.25     lukem 			reply(502, "RFC 2228 authentication not implemented.");
    781  1.42     lukem 			free($3);
    782  1.25     lukem 		}
    783  1.25     lukem 
    784  1.25     lukem 	| ENC SP base64data CRLF
    785  1.25     lukem 		{
    786  1.25     lukem 			reply(502, "RFC 2228 authentication not implemented.");
    787  1.42     lukem 			free($3);
    788  1.25     lukem 		}
    789  1.25     lukem 
    790  1.23     lukem 						/* RFC 2389 */
    791  1.23     lukem 	| FEAT CRLF
    792  1.23     lukem 		{
    793  1.47     lukem 
    794  1.47     lukem 			feat();
    795  1.23     lukem 		}
    796  1.23     lukem 
    797  1.23     lukem 	| OPTS SP STRING CRLF
    798   1.4   deraadt 		{
    799  1.23     lukem 
    800  1.23     lukem 			opts($3);
    801  1.42     lukem 			free($3);
    802   1.1       cgd 		}
    803   1.1       cgd 
    804  1.23     lukem 
    805  1.85     lukem 						/* RFC 3659 */
    806  1.23     lukem 
    807   1.1       cgd 		/*
    808   1.1       cgd 		 * Return size of file in a format suitable for
    809   1.1       cgd 		 * using with RESTART (we just count bytes).
    810   1.1       cgd 		 */
    811   1.4   deraadt 	| SIZE check_login SP pathname CRLF
    812   1.4   deraadt 		{
    813   1.1       cgd 			if ($2 && $4 != NULL)
    814   1.4   deraadt 				sizecmd($4);
    815   1.1       cgd 			if ($4 != NULL)
    816   1.4   deraadt 				free($4);
    817   1.1       cgd 		}
    818   1.1       cgd 
    819   1.1       cgd 		/*
    820   1.1       cgd 		 * Return modification time of file as an ISO 3307
    821   1.1       cgd 		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
    822   1.1       cgd 		 * where xxx is the fractional second (of any precision,
    823   1.1       cgd 		 * not necessarily 3 digits)
    824   1.1       cgd 		 */
    825   1.4   deraadt 	| MDTM check_login SP pathname CRLF
    826   1.4   deraadt 		{
    827   1.1       cgd 			if ($2 && $4 != NULL) {
    828   1.1       cgd 				struct stat stbuf;
    829   1.4   deraadt 				if (stat($4, &stbuf) < 0)
    830  1.22     lukem 					perror_reply(550, $4);
    831   1.4   deraadt 				else if (!S_ISREG(stbuf.st_mode)) {
    832   1.4   deraadt 					reply(550, "%s: not a plain file.", $4);
    833   1.1       cgd 				} else {
    834   1.4   deraadt 					struct tm *t;
    835  1.47     lukem 
    836   1.1       cgd 					t = gmtime(&stbuf.st_mtime);
    837   1.1       cgd 					reply(213,
    838   1.7       jtc 					    "%04d%02d%02d%02d%02d%02d",
    839  1.18     lukem 					    TM_YEAR_BASE + t->tm_year,
    840   1.7       jtc 					    t->tm_mon+1, t->tm_mday,
    841   1.1       cgd 					    t->tm_hour, t->tm_min, t->tm_sec);
    842   1.1       cgd 				}
    843   1.1       cgd 			}
    844   1.1       cgd 			if ($4 != NULL)
    845   1.4   deraadt 				free($4);
    846   1.1       cgd 		}
    847  1.23     lukem 
    848  1.47     lukem 	| MLST check_login SP pathname CRLF
    849  1.47     lukem 		{
    850  1.47     lukem 			if ($2 && $4 != NULL)
    851  1.47     lukem 				mlst($4);
    852  1.47     lukem 			if ($4 != NULL)
    853  1.47     lukem 				free($4);
    854  1.47     lukem 		}
    855  1.47     lukem 
    856  1.48     lukem 	| MLST check_login CRLF
    857  1.47     lukem 		{
    858  1.95       shm 			if ($2)
    859  1.95       shm 				mlst(NULL);
    860  1.47     lukem 		}
    861  1.47     lukem 
    862  1.47     lukem 	| MLSD check_login SP pathname CRLF
    863  1.47     lukem 		{
    864  1.47     lukem 			if ($2 && $4 != NULL)
    865  1.47     lukem 				mlsd($4);
    866  1.47     lukem 			if ($4 != NULL)
    867  1.47     lukem 				free($4);
    868  1.47     lukem 		}
    869  1.47     lukem 
    870  1.48     lukem 	| MLSD check_login CRLF
    871  1.47     lukem 		{
    872  1.95       shm 			if ($2)
    873  1.95       shm 				mlsd(NULL);
    874  1.47     lukem 		}
    875  1.47     lukem 
    876   1.4   deraadt 	| error CRLF
    877   1.4   deraadt 		{
    878   1.1       cgd 			yyerrok;
    879   1.1       cgd 		}
    880   1.1       cgd 	;
    881  1.21     lukem 
    882   1.4   deraadt rcmd
    883  1.70     lukem 	: REST check_login SP NUMBER CRLF
    884  1.23     lukem 		{
    885  1.42     lukem 			if ($2) {
    886  1.78     lukem 				REASSIGN(fromname, NULL);
    887  1.74     lukem 				restart_point = (off_t)$4.ll;
    888  1.55     lukem 				reply(350,
    889  1.55     lukem     "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.",
    890  1.55     lukem 				    (LLT)restart_point);
    891  1.42     lukem 			}
    892  1.23     lukem 		}
    893  1.42     lukem 
    894  1.56     lukem 	| RNFR SP pathname CRLF
    895   1.4   deraadt 		{
    896   1.1       cgd 			restart_point = (off_t) 0;
    897  1.78     lukem 			if (check_write($3, 0)) {
    898  1.78     lukem 				REASSIGN(fromname, NULL);
    899  1.56     lukem 				fromname = renamefrom($3);
    900  1.78     lukem 			}
    901  1.56     lukem 			if ($3 != NULL)
    902  1.56     lukem 				free($3);
    903   1.1       cgd 		}
    904   1.1       cgd 	;
    905   1.4   deraadt 
    906   1.4   deraadt username
    907   1.4   deraadt 	: STRING
    908   1.1       cgd 	;
    909   1.1       cgd 
    910   1.4   deraadt password
    911   1.4   deraadt 	: /* empty */
    912   1.4   deraadt 		{
    913   1.4   deraadt 			$$ = (char *)calloc(1, sizeof(char));
    914   1.1       cgd 		}
    915  1.23     lukem 
    916   1.4   deraadt 	| STRING
    917   1.1       cgd 	;
    918   1.1       cgd 
    919   1.4   deraadt byte_size
    920   1.4   deraadt 	: NUMBER
    921  1.70     lukem 		{
    922  1.70     lukem 			$$ = $1.i;
    923  1.70     lukem 		}
    924   1.1       cgd 	;
    925   1.1       cgd 
    926   1.4   deraadt host_port
    927   1.4   deraadt 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
    928   1.1       cgd 		NUMBER COMMA NUMBER
    929   1.4   deraadt 		{
    930   1.4   deraadt 			char *a, *p;
    931   1.1       cgd 
    932  1.55     lukem 			memset(&data_dest, 0, sizeof(data_dest));
    933  1.32    itojun 			data_dest.su_len = sizeof(struct sockaddr_in);
    934  1.32    itojun 			data_dest.su_family = AF_INET;
    935  1.55     lukem 			p = (char *)&data_dest.su_port;
    936  1.70     lukem 			p[0] = $9.i; p[1] = $11.i;
    937  1.55     lukem 			a = (char *)&data_dest.su_addr;
    938  1.70     lukem 			a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
    939   1.1       cgd 		}
    940   1.1       cgd 	;
    941   1.1       cgd 
    942  1.34    itojun host_long_port4
    943  1.34    itojun 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
    944  1.34    itojun 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
    945  1.34    itojun 		NUMBER
    946  1.34    itojun 		{
    947  1.34    itojun 			char *a, *p;
    948  1.34    itojun 
    949  1.55     lukem 			memset(&data_dest, 0, sizeof(data_dest));
    950  1.55     lukem 			data_dest.su_len = sizeof(struct sockaddr_in);
    951  1.34    itojun 			data_dest.su_family = AF_INET;
    952  1.34    itojun 			p = (char *)&data_dest.su_port;
    953  1.70     lukem 			p[0] = $15.i; p[1] = $17.i;
    954  1.55     lukem 			a = (char *)&data_dest.su_addr;
    955  1.70     lukem 			a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
    956  1.35    itojun 
    957  1.35    itojun 			/* reject invalid LPRT command */
    958  1.70     lukem 			if ($1.i != 4 || $3.i != 4 || $13.i != 2)
    959  1.35    itojun 				memset(&data_dest, 0, sizeof(data_dest));
    960  1.34    itojun 		}
    961  1.34    itojun 	;
    962  1.34    itojun 
    963  1.34    itojun host_long_port6
    964  1.32    itojun 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
    965  1.32    itojun 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
    966  1.32    itojun 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
    967  1.32    itojun 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
    968  1.32    itojun 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
    969  1.32    itojun 		NUMBER
    970  1.32    itojun 		{
    971  1.53  christos #ifdef INET6
    972  1.91  christos 			unsigned char buf[16];
    973  1.32    itojun 
    974  1.91  christos 			(void)memset(&data_dest, 0, sizeof(data_dest));
    975  1.55     lukem 			data_dest.su_len = sizeof(struct sockaddr_in6);
    976  1.32    itojun 			data_dest.su_family = AF_INET6;
    977  1.91  christos 			buf[0] = $39.i; buf[1] = $41.i;
    978  1.91  christos 			(void)memcpy(&data_dest.su_port, buf,
    979  1.92     joerg 			    sizeof(data_dest.su_port));
    980  1.91  christos 			buf[0] = $5.i; buf[1] = $7.i;
    981  1.91  christos 			buf[2] = $9.i; buf[3] = $11.i;
    982  1.91  christos 			buf[4] = $13.i; buf[5] = $15.i;
    983  1.91  christos 			buf[6] = $17.i; buf[7] = $19.i;
    984  1.91  christos 			buf[8] = $21.i; buf[9] = $23.i;
    985  1.91  christos 			buf[10] = $25.i; buf[11] = $27.i;
    986  1.91  christos 			buf[12] = $29.i; buf[13] = $31.i;
    987  1.91  christos 			buf[14] = $33.i; buf[15] = $35.i;
    988  1.91  christos 			(void)memcpy(&data_dest.si_su.su_sin6.sin6_addr,
    989  1.91  christos 			    buf, sizeof(data_dest.si_su.su_sin6.sin6_addr));
    990  1.37    itojun 			if (his_addr.su_family == AF_INET6) {
    991  1.59     lukem 				/* XXX: more sanity checks! */
    992  1.55     lukem 				data_dest.su_scope_id = his_addr.su_scope_id;
    993  1.37    itojun 			}
    994  1.53  christos #else
    995  1.53  christos 			memset(&data_dest, 0, sizeof(data_dest));
    996  1.55     lukem #endif /* INET6 */
    997  1.35    itojun 			/* reject invalid LPRT command */
    998  1.75     lukem 			if ($1.i != 6 || $3.i != 16 || $37.i != 2)
    999  1.35    itojun 				memset(&data_dest, 0, sizeof(data_dest));
   1000  1.32    itojun 		}
   1001  1.32    itojun 	;
   1002  1.32    itojun 
   1003   1.4   deraadt form_code
   1004   1.4   deraadt 	: N
   1005   1.4   deraadt 		{
   1006   1.4   deraadt 			$$ = FORM_N;
   1007   1.4   deraadt 		}
   1008  1.23     lukem 
   1009   1.4   deraadt 	| T
   1010   1.4   deraadt 		{
   1011   1.4   deraadt 			$$ = FORM_T;
   1012   1.4   deraadt 		}
   1013  1.23     lukem 
   1014   1.4   deraadt 	| C
   1015   1.4   deraadt 		{
   1016   1.4   deraadt 			$$ = FORM_C;
   1017   1.4   deraadt 		}
   1018   1.1       cgd 	;
   1019   1.1       cgd 
   1020   1.4   deraadt type_code
   1021   1.4   deraadt 	: A
   1022   1.4   deraadt 		{
   1023   1.4   deraadt 			cmd_type = TYPE_A;
   1024   1.4   deraadt 			cmd_form = FORM_N;
   1025   1.4   deraadt 		}
   1026  1.23     lukem 
   1027   1.4   deraadt 	| A SP form_code
   1028   1.4   deraadt 		{
   1029   1.4   deraadt 			cmd_type = TYPE_A;
   1030   1.4   deraadt 			cmd_form = $3;
   1031   1.4   deraadt 		}
   1032  1.23     lukem 
   1033   1.4   deraadt 	| E
   1034   1.4   deraadt 		{
   1035   1.4   deraadt 			cmd_type = TYPE_E;
   1036   1.4   deraadt 			cmd_form = FORM_N;
   1037   1.4   deraadt 		}
   1038  1.23     lukem 
   1039   1.4   deraadt 	| E SP form_code
   1040   1.4   deraadt 		{
   1041   1.4   deraadt 			cmd_type = TYPE_E;
   1042   1.4   deraadt 			cmd_form = $3;
   1043   1.4   deraadt 		}
   1044  1.23     lukem 
   1045   1.4   deraadt 	| I
   1046   1.4   deraadt 		{
   1047   1.4   deraadt 			cmd_type = TYPE_I;
   1048   1.4   deraadt 		}
   1049  1.23     lukem 
   1050   1.4   deraadt 	| L
   1051   1.4   deraadt 		{
   1052   1.4   deraadt 			cmd_type = TYPE_L;
   1053   1.4   deraadt 			cmd_bytesz = NBBY;
   1054   1.4   deraadt 		}
   1055  1.23     lukem 
   1056   1.4   deraadt 	| L SP byte_size
   1057   1.4   deraadt 		{
   1058   1.4   deraadt 			cmd_type = TYPE_L;
   1059   1.4   deraadt 			cmd_bytesz = $3;
   1060   1.4   deraadt 		}
   1061  1.23     lukem 
   1062   1.4   deraadt 		/* this is for a bug in the BBN ftp */
   1063   1.4   deraadt 	| L byte_size
   1064   1.4   deraadt 		{
   1065   1.4   deraadt 			cmd_type = TYPE_L;
   1066   1.4   deraadt 			cmd_bytesz = $2;
   1067   1.4   deraadt 		}
   1068   1.1       cgd 	;
   1069   1.1       cgd 
   1070   1.4   deraadt struct_code
   1071   1.4   deraadt 	: F
   1072   1.4   deraadt 		{
   1073   1.4   deraadt 			$$ = STRU_F;
   1074   1.4   deraadt 		}
   1075  1.23     lukem 
   1076   1.4   deraadt 	| R
   1077   1.4   deraadt 		{
   1078   1.4   deraadt 			$$ = STRU_R;
   1079   1.4   deraadt 		}
   1080  1.23     lukem 
   1081   1.4   deraadt 	| P
   1082   1.4   deraadt 		{
   1083   1.4   deraadt 			$$ = STRU_P;
   1084   1.4   deraadt 		}
   1085   1.1       cgd 	;
   1086   1.1       cgd 
   1087   1.4   deraadt mode_code
   1088   1.4   deraadt 	: S
   1089   1.4   deraadt 		{
   1090   1.4   deraadt 			$$ = MODE_S;
   1091   1.4   deraadt 		}
   1092  1.23     lukem 
   1093   1.4   deraadt 	| B
   1094   1.4   deraadt 		{
   1095   1.4   deraadt 			$$ = MODE_B;
   1096   1.4   deraadt 		}
   1097  1.23     lukem 
   1098   1.4   deraadt 	| C
   1099   1.4   deraadt 		{
   1100   1.4   deraadt 			$$ = MODE_C;
   1101   1.4   deraadt 		}
   1102   1.1       cgd 	;
   1103   1.1       cgd 
   1104   1.4   deraadt pathname
   1105   1.4   deraadt 	: pathstring
   1106   1.4   deraadt 		{
   1107   1.4   deraadt 			/*
   1108   1.4   deraadt 			 * Problem: this production is used for all pathname
   1109   1.4   deraadt 			 * processing, but only gives a 550 error reply.
   1110   1.9     lukem 			 * This is a valid reply in some cases but not in
   1111   1.9     lukem 			 * others.
   1112   1.4   deraadt 			 */
   1113   1.4   deraadt 			if (logged_in && $1 && *$1 == '~') {
   1114  1.64     lukem 				char	*path, *home, *result;
   1115  1.64     lukem 				size_t	len;
   1116   1.4   deraadt 
   1117  1.64     lukem 				path = strchr($1 + 1, '/');
   1118  1.64     lukem 				if (path != NULL)
   1119  1.64     lukem 					*path++ = '\0';
   1120   1.9     lukem 				if ($1[1] == '\0')
   1121  1.64     lukem 					home = homedir;
   1122   1.9     lukem 				else {
   1123  1.66     lukem 					struct passwd	*hpw;
   1124  1.64     lukem 
   1125  1.66     lukem 					if ((hpw = getpwnam($1 + 1)) != NULL)
   1126  1.66     lukem 						home = hpw->pw_dir;
   1127  1.64     lukem 					else
   1128  1.64     lukem 						home = $1;
   1129  1.64     lukem 				}
   1130  1.64     lukem 				len = strlen(home) + 1;
   1131  1.64     lukem 				if (path != NULL)
   1132  1.64     lukem 					len += strlen(path) + 1;
   1133  1.64     lukem 				if ((result = malloc(len)) == NULL)
   1134  1.64     lukem 					fatal("Local resource failure: malloc");
   1135  1.64     lukem 				strlcpy(result, home, len);
   1136  1.64     lukem 				if (path != NULL) {
   1137  1.64     lukem 					strlcat(result, "/", len);
   1138  1.64     lukem 					strlcat(result, path, len);
   1139   1.4   deraadt 				}
   1140  1.64     lukem 				$$ = result;
   1141   1.4   deraadt 				free($1);
   1142   1.4   deraadt 			} else
   1143   1.4   deraadt 				$$ = $1;
   1144   1.4   deraadt 		}
   1145   1.1       cgd 	;
   1146   1.1       cgd 
   1147   1.4   deraadt pathstring
   1148   1.4   deraadt 	: STRING
   1149   1.1       cgd 	;
   1150   1.1       cgd 
   1151   1.4   deraadt octal_number
   1152   1.4   deraadt 	: NUMBER
   1153   1.4   deraadt 		{
   1154   1.4   deraadt 			int ret, dec, multby, digit;
   1155   1.1       cgd 
   1156   1.4   deraadt 			/*
   1157   1.4   deraadt 			 * Convert a number that was read as decimal number
   1158   1.4   deraadt 			 * to what it would be if it had been read as octal.
   1159   1.4   deraadt 			 */
   1160  1.70     lukem 			dec = $1.i;
   1161   1.4   deraadt 			multby = 1;
   1162   1.4   deraadt 			ret = 0;
   1163   1.4   deraadt 			while (dec) {
   1164   1.4   deraadt 				digit = dec%10;
   1165   1.4   deraadt 				if (digit > 7) {
   1166   1.4   deraadt 					ret = -1;
   1167   1.4   deraadt 					break;
   1168   1.4   deraadt 				}
   1169   1.4   deraadt 				ret += digit * multby;
   1170   1.4   deraadt 				multby *= 8;
   1171   1.4   deraadt 				dec /= 10;
   1172   1.1       cgd 			}
   1173   1.4   deraadt 			$$ = ret;
   1174   1.1       cgd 		}
   1175   1.1       cgd 	;
   1176   1.1       cgd 
   1177  1.25     lukem mechanism_name
   1178  1.25     lukem 	: STRING
   1179  1.25     lukem 	;
   1180  1.25     lukem 
   1181  1.25     lukem base64data
   1182  1.25     lukem 	: STRING
   1183  1.25     lukem 	;
   1184  1.25     lukem 
   1185  1.25     lukem prot_code
   1186  1.25     lukem 	: STRING
   1187  1.25     lukem 	;
   1188  1.25     lukem 
   1189  1.25     lukem decimal_integer
   1190  1.25     lukem 	: NUMBER
   1191  1.70     lukem 		{
   1192  1.70     lukem 			$$ = $1.i;
   1193  1.70     lukem 		}
   1194  1.25     lukem 	;
   1195  1.25     lukem 
   1196   1.4   deraadt check_login
   1197   1.4   deraadt 	: /* empty */
   1198   1.4   deraadt 		{
   1199   1.4   deraadt 			if (logged_in)
   1200   1.4   deraadt 				$$ = 1;
   1201   1.4   deraadt 			else {
   1202   1.4   deraadt 				reply(530, "Please login with USER and PASS.");
   1203   1.4   deraadt 				$$ = 0;
   1204  1.22     lukem 				hasyyerrored = 1;
   1205   1.4   deraadt 			}
   1206   1.1       cgd 		}
   1207   1.1       cgd 	;
   1208  1.21     lukem 
   1209   1.1       cgd %%
   1210   1.1       cgd 
   1211   1.1       cgd #define	CMD	0	/* beginning of command */
   1212   1.1       cgd #define	ARGS	1	/* expect miscellaneous arguments */
   1213   1.1       cgd #define	STR1	2	/* expect SP followed by STRING */
   1214   1.1       cgd #define	STR2	3	/* expect STRING */
   1215   1.1       cgd #define	OSTR	4	/* optional SP then STRING */
   1216   1.1       cgd #define	ZSTR1	5	/* SP then optional STRING */
   1217   1.1       cgd #define	ZSTR2	6	/* optional STRING after SP */
   1218   1.1       cgd #define	SITECMD	7	/* SITE command */
   1219   1.1       cgd #define	NSTR	8	/* Number followed by a string */
   1220  1.21     lukem #define NOARGS	9	/* No arguments allowed */
   1221  1.60     aidan #define EOLN	10	/* End of line */
   1222   1.1       cgd 
   1223  1.21     lukem struct tab cmdtab[] = {
   1224  1.21     lukem 				/* From RFC 959, in order defined (5.3.1) */
   1225  1.89     lukem 	{ "USER", USER, STR1,	1,	"<sp> username", 0, },
   1226  1.89     lukem 	{ "PASS", PASS, ZSTR1,	1,	"<sp> password", 0, },
   1227  1.89     lukem 	{ "ACCT", ACCT, STR1,	0,	"(specify account)", 0, },
   1228  1.89     lukem 	{ "CWD",  CWD,  OSTR,	1,	"[ <sp> directory-name ]", 0, },
   1229  1.89     lukem 	{ "CDUP", CDUP, NOARGS,	1,	"(change to parent directory)", 0, },
   1230  1.89     lukem 	{ "SMNT", SMNT, ARGS,	0,	"(structure mount)", 0, },
   1231  1.89     lukem 	{ "QUIT", QUIT, NOARGS,	1,	"(terminate service)", 0, },
   1232  1.89     lukem 	{ "REIN", REIN, NOARGS,	0,	"(reinitialize server state)", 0, },
   1233  1.89     lukem 	{ "PORT", PORT, ARGS,	1,	"<sp> b0, b1, b2, b3, b4, b5", 0, },
   1234  1.89     lukem 	{ "LPRT", LPRT, ARGS,	1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2...", 0, },
   1235  1.89     lukem 	{ "EPRT", EPRT, STR1,	1,	"<sp> |af|addr|port|", 0, },
   1236  1.89     lukem 	{ "PASV", PASV, NOARGS,	1,	"(set server in passive mode)", 0, },
   1237  1.89     lukem 	{ "LPSV", LPSV, ARGS,	1,	"(set server in passive mode)", 0, },
   1238  1.89     lukem 	{ "EPSV", EPSV, ARGS,	1,	"[<sp> af|ALL]", 0, },
   1239  1.89     lukem 	{ "TYPE", TYPE, ARGS,	1,	"<sp> [ A | E | I | L ]", 0, },
   1240  1.89     lukem 	{ "STRU", STRU, ARGS,	1,	"(specify file structure)", 0, },
   1241  1.89     lukem 	{ "MODE", MODE, ARGS,	1,	"(specify transfer mode)", 0, },
   1242  1.89     lukem 	{ "RETR", RETR, STR1,	1,	"<sp> file-name", 0, },
   1243  1.89     lukem 	{ "STOR", STOR, STR1,	1,	"<sp> file-name", 0, },
   1244  1.89     lukem 	{ "STOU", STOU, STR1,	1,	"<sp> file-name", 0, },
   1245  1.89     lukem 	{ "APPE", APPE, STR1,	1,	"<sp> file-name", 0, },
   1246  1.89     lukem 	{ "ALLO", ALLO, ARGS,	1,	"allocate storage (vacuously)", 0, },
   1247  1.89     lukem 	{ "REST", REST, ARGS,	1,	"<sp> offset (restart command)", 0, },
   1248  1.89     lukem 	{ "RNFR", RNFR, STR1,	1,	"<sp> file-name", 0, },
   1249  1.89     lukem 	{ "RNTO", RNTO, STR1,	1,	"<sp> file-name", 0, },
   1250  1.89     lukem 	{ "ABOR", ABOR, NOARGS,	4,	"(abort operation)", 0, },
   1251  1.89     lukem 	{ "DELE", DELE, STR1,	1,	"<sp> file-name", 0, },
   1252  1.89     lukem 	{ "RMD",  RMD,  STR1,	1,	"<sp> path-name", 0, },
   1253  1.89     lukem 	{ "MKD",  MKD,  STR1,	1,	"<sp> path-name", 0, },
   1254  1.89     lukem 	{ "PWD",  PWD,  NOARGS,	1,	"(return current directory)", 0, },
   1255  1.89     lukem 	{ "LIST", LIST, OSTR,	1,	"[ <sp> path-name ]", 0, },
   1256  1.89     lukem 	{ "NLST", NLST, OSTR,	1,	"[ <sp> path-name ]", 0, },
   1257  1.89     lukem 	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]", 0, },
   1258  1.89     lukem 	{ "SYST", SYST, NOARGS,	1,	"(get type of operating system)", 0, },
   1259  1.89     lukem 	{ "STAT", STAT, OSTR,	4,	"[ <sp> path-name ]", 0, },
   1260  1.89     lukem 	{ "HELP", HELP, OSTR,	1,	"[ <sp> <string> ]", 0, },
   1261  1.89     lukem 	{ "NOOP", NOOP, NOARGS,	2,	"", 0, },
   1262  1.23     lukem 
   1263  1.25     lukem 				/* From RFC 2228, in order defined */
   1264  1.89     lukem 	{ "AUTH", AUTH, STR1,	1,	"<sp> mechanism-name", 0, },
   1265  1.89     lukem 	{ "ADAT", ADAT, STR1,	1,	"<sp> base-64-data", 0, },
   1266  1.89     lukem 	{ "PROT", PROT, STR1,	1,	"<sp> prot-code", 0, },
   1267  1.89     lukem 	{ "PBSZ", PBSZ, ARGS,	1,	"<sp> decimal-integer", 0, },
   1268  1.89     lukem 	{ "CCC",  CCC,  NOARGS,	1,	"(Disable data protection)", 0, },
   1269  1.89     lukem 	{ "MIC",  MIC,  STR1,	4,	"<sp> base64data", 0, },
   1270  1.89     lukem 	{ "CONF", CONF, STR1,	4,	"<sp> base64data", 0, },
   1271  1.89     lukem 	{ "ENC",  ENC,  STR1,	4,	"<sp> base64data", 0, },
   1272  1.25     lukem 
   1273  1.25     lukem 				/* From RFC 2389, in order defined */
   1274  1.89     lukem 	{ "FEAT", FEAT, NOARGS,	1,	"(display extended features)", 0, },
   1275  1.89     lukem 	{ "OPTS", OPTS, STR1,	1,	"<sp> command [ <sp> options ]", 0, },
   1276  1.23     lukem 
   1277  1.85     lukem 				/* From RFC 3659, in order defined */
   1278  1.89     lukem 	{ "MDTM", MDTM, OSTR,	1,	"<sp> path-name", 0, },
   1279  1.89     lukem 	{ "SIZE", SIZE, OSTR,	1,	"<sp> path-name", 0, },
   1280  1.89     lukem 	{ "MLST", MLST, OSTR,	2,	"[ <sp> path-name ]", 0, },
   1281  1.89     lukem 	{ "MLSD", MLSD, OSTR,	1,	"[ <sp> directory-name ]", 0, },
   1282  1.21     lukem 
   1283  1.21     lukem 				/* obsolete commands */
   1284  1.89     lukem 	{ "MAIL", MAIL, OSTR,	0,	"(mail to user)", 0, },
   1285  1.89     lukem 	{ "MLFL", MLFL, OSTR,	0,	"(mail file)", 0, },
   1286  1.89     lukem 	{ "MRCP", MRCP, STR1,	0,	"(mail recipient)", 0, },
   1287  1.89     lukem 	{ "MRSQ", MRSQ, OSTR,	0,	"(mail recipient scheme question)", 0, },
   1288  1.89     lukem 	{ "MSAM", MSAM, OSTR,	0,	"(mail send to terminal and mailbox)", 0, },
   1289  1.89     lukem 	{ "MSND", MSND, OSTR,	0,	"(mail send to terminal)", 0, },
   1290  1.89     lukem 	{ "MSOM", MSOM, OSTR,	0,	"(mail send to terminal or mailbox)", 0, },
   1291  1.89     lukem 	{ "XCUP", CDUP, NOARGS,	1,	"(change to parent directory)", 0, },
   1292  1.89     lukem 	{ "XCWD", CWD,  OSTR,	1,	"[ <sp> directory-name ]", 0, },
   1293  1.89     lukem 	{ "XMKD", MKD,  STR1,	1,	"<sp> path-name", 0, },
   1294  1.89     lukem 	{ "XPWD", PWD,  NOARGS,	1,	"(return current directory)", 0, },
   1295  1.89     lukem 	{ "XRMD", RMD,  STR1,	1,	"<sp> path-name", 0, },
   1296  1.21     lukem 
   1297  1.89     lukem 	{  NULL,  0,	0,	0,	0, 0, }
   1298   1.1       cgd };
   1299   1.1       cgd 
   1300   1.1       cgd struct tab sitetab[] = {
   1301  1.89     lukem 	{ "CHMOD",	CHMOD,	NSTR,	1,	"<sp> mode <sp> file-name", 0, },
   1302  1.89     lukem 	{ "HELP",	HELP,	OSTR,	1,	"[ <sp> <string> ]", 0, },
   1303  1.89     lukem 	{ "IDLE",	IDLE,	ARGS,	1,	"[ <sp> maximum-idle-time ]", 0, },
   1304  1.89     lukem 	{ "RATEGET",	RATEGET,OSTR,	1,	"[ <sp> get-throttle-rate ]", 0, },
   1305  1.89     lukem 	{ "RATEPUT",	RATEPUT,OSTR,	1,	"[ <sp> put-throttle-rate ]", 0, },
   1306  1.89     lukem 	{ "UMASK",	UMASK,	ARGS,	1,	"[ <sp> umask ]", 0, },
   1307  1.89     lukem 	{ NULL,		0,	0,	0,	0, 0, }
   1308   1.1       cgd };
   1309   1.1       cgd 
   1310  1.56     lukem /*
   1311  1.56     lukem  * Check if a filename is allowed to be modified (isupload == 0) or
   1312  1.56     lukem  * uploaded (isupload == 1), and if necessary, check the filename is `sane'.
   1313  1.67     lukem  * If the filename is NULL, fail.
   1314  1.67     lukem  * If the filename is "", don't do the sane name check.
   1315  1.56     lukem  */
   1316  1.56     lukem static int
   1317  1.56     lukem check_write(const char *file, int isupload)
   1318  1.56     lukem {
   1319  1.56     lukem 	if (file == NULL)
   1320  1.56     lukem 		return (0);
   1321  1.56     lukem 	if (! logged_in) {
   1322  1.56     lukem 		reply(530, "Please login with USER and PASS.");
   1323  1.56     lukem 		return (0);
   1324  1.56     lukem 	}
   1325  1.56     lukem 		/* checking modify */
   1326  1.56     lukem 	if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) {
   1327  1.56     lukem 		reply(502, "No permission to use this command.");
   1328  1.56     lukem 		return (0);
   1329  1.56     lukem 	}
   1330  1.56     lukem 		/* checking upload */
   1331  1.56     lukem 	if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) {
   1332  1.56     lukem 		reply(502, "No permission to use this command.");
   1333  1.56     lukem 		return (0);
   1334  1.56     lukem 	}
   1335  1.67     lukem 
   1336  1.56     lukem 		/* checking sanenames */
   1337  1.67     lukem 	if (file[0] != '\0' && CURCLASS_FLAGS_ISSET(sanenames)) {
   1338  1.56     lukem 		const char *p;
   1339  1.56     lukem 
   1340  1.56     lukem 		if (file[0] == '.')
   1341  1.56     lukem 			goto insane_name;
   1342  1.56     lukem 		for (p = file; *p; p++) {
   1343  1.81       dsl 			if (isalnum((unsigned char)*p) || *p == '-' || *p == '+' ||
   1344  1.56     lukem 			    *p == ',' || *p == '.' || *p == '_')
   1345  1.56     lukem 				continue;
   1346  1.56     lukem  insane_name:
   1347  1.56     lukem 			reply(553, "File name `%s' not allowed.", file);
   1348  1.56     lukem 			return (0);
   1349  1.56     lukem 		}
   1350  1.56     lukem 	}
   1351  1.56     lukem 	return (1);
   1352  1.56     lukem }
   1353  1.56     lukem 
   1354  1.47     lukem struct tab *
   1355  1.46     lukem lookup(struct tab *p, const char *cmd)
   1356   1.1       cgd {
   1357   1.1       cgd 
   1358   1.1       cgd 	for (; p->name != NULL; p++)
   1359  1.23     lukem 		if (strcasecmp(cmd, p->name) == 0)
   1360   1.1       cgd 			return (p);
   1361   1.1       cgd 	return (0);
   1362   1.1       cgd }
   1363   1.1       cgd 
   1364   1.1       cgd #include <arpa/telnet.h>
   1365   1.1       cgd 
   1366   1.1       cgd /*
   1367  1.90       roy  * get_line - a hacked up version of fgets to ignore TELNET escape codes.
   1368  1.88     lukem  *	`s' is the buffer to read into.
   1369  1.88     lukem  *	`n' is the 1 less than the size of the buffer, to allow trailing NUL
   1370  1.88     lukem  *	`iop' is the FILE to read from.
   1371  1.88     lukem  *	Returns 0 on success, -1 on EOF, -2 if the command was too long.
   1372   1.1       cgd  */
   1373  1.88     lukem int
   1374  1.90       roy get_line(char *s, int n, FILE *iop)
   1375   1.1       cgd {
   1376   1.4   deraadt 	int c;
   1377  1.21     lukem 	char *cs;
   1378   1.1       cgd 
   1379   1.1       cgd 	cs = s;
   1380   1.1       cgd /* tmpline may contain saved command from urgent mode interruption */
   1381   1.1       cgd 	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
   1382   1.1       cgd 		*cs++ = tmpline[c];
   1383   1.1       cgd 		if (tmpline[c] == '\n') {
   1384   1.1       cgd 			*cs++ = '\0';
   1385  1.84  christos 			if (ftpd_debug)
   1386   1.1       cgd 				syslog(LOG_DEBUG, "command: %s", s);
   1387   1.1       cgd 			tmpline[0] = '\0';
   1388  1.88     lukem 			return(0);
   1389   1.1       cgd 		}
   1390   1.1       cgd 		if (c == 0)
   1391   1.1       cgd 			tmpline[0] = '\0';
   1392   1.1       cgd 	}
   1393   1.1       cgd 	while ((c = getc(iop)) != EOF) {
   1394  1.27     lukem 		total_bytes++;
   1395  1.27     lukem 		total_bytes_in++;
   1396   1.1       cgd 		c &= 0377;
   1397   1.1       cgd 		if (c == IAC) {
   1398   1.1       cgd 		    if ((c = getc(iop)) != EOF) {
   1399  1.27     lukem 			total_bytes++;
   1400  1.27     lukem 			total_bytes_in++;
   1401   1.1       cgd 			c &= 0377;
   1402   1.1       cgd 			switch (c) {
   1403   1.1       cgd 			case WILL:
   1404   1.1       cgd 			case WONT:
   1405   1.1       cgd 				c = getc(iop);
   1406  1.27     lukem 				total_bytes++;
   1407  1.27     lukem 				total_bytes_in++;
   1408  1.48     lukem 				cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c);
   1409   1.1       cgd 				(void) fflush(stdout);
   1410   1.1       cgd 				continue;
   1411   1.1       cgd 			case DO:
   1412   1.1       cgd 			case DONT:
   1413   1.1       cgd 				c = getc(iop);
   1414  1.27     lukem 				total_bytes++;
   1415  1.27     lukem 				total_bytes_in++;
   1416  1.48     lukem 				cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c);
   1417   1.1       cgd 				(void) fflush(stdout);
   1418   1.1       cgd 				continue;
   1419   1.1       cgd 			case IAC:
   1420   1.1       cgd 				break;
   1421   1.1       cgd 			default:
   1422   1.1       cgd 				continue;	/* ignore command */
   1423   1.1       cgd 			}
   1424   1.1       cgd 		    }
   1425   1.1       cgd 		}
   1426   1.1       cgd 		*cs++ = c;
   1427  1.88     lukem 		if (--n <= 0) {
   1428  1.88     lukem 			/*
   1429  1.88     lukem 			 * If command doesn't fit into buffer, discard the
   1430  1.88     lukem 			 * rest of the command and indicate truncation.
   1431  1.88     lukem 			 * This prevents the command to be split up into
   1432  1.88     lukem 			 * multiple commands.
   1433  1.88     lukem 			 */
   1434  1.88     lukem 			if (ftpd_debug)
   1435  1.88     lukem 				syslog(LOG_DEBUG,
   1436  1.88     lukem 				    "command too long, last char: %d", c);
   1437  1.88     lukem 			while (c != '\n' && (c = getc(iop)) != EOF)
   1438  1.88     lukem 				continue;
   1439  1.88     lukem 			return (-2);
   1440  1.88     lukem 		}
   1441  1.88     lukem 		if (c == '\n')
   1442   1.1       cgd 			break;
   1443   1.1       cgd 	}
   1444   1.1       cgd 	if (c == EOF && cs == s)
   1445  1.88     lukem 		return (-1);
   1446   1.1       cgd 	*cs++ = '\0';
   1447  1.84  christos 	if (ftpd_debug) {
   1448  1.58     lukem 		if ((curclass.type != CLASS_GUEST &&
   1449  1.58     lukem 		    strncasecmp(s, "PASS ", 5) == 0) ||
   1450  1.58     lukem 		    strncasecmp(s, "ACCT ", 5) == 0) {
   1451   1.4   deraadt 			/* Don't syslog passwords */
   1452  1.58     lukem 			syslog(LOG_DEBUG, "command: %.4s ???", s);
   1453   1.4   deraadt 		} else {
   1454  1.21     lukem 			char *cp;
   1455  1.21     lukem 			int len;
   1456   1.4   deraadt 
   1457   1.4   deraadt 			/* Don't syslog trailing CR-LF */
   1458   1.4   deraadt 			len = strlen(s);
   1459   1.4   deraadt 			cp = s + len - 1;
   1460   1.4   deraadt 			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
   1461   1.4   deraadt 				--cp;
   1462   1.4   deraadt 				--len;
   1463   1.4   deraadt 			}
   1464   1.4   deraadt 			syslog(LOG_DEBUG, "command: %.*s", len, s);
   1465   1.4   deraadt 		}
   1466   1.4   deraadt 	}
   1467  1.88     lukem 	return (0);
   1468   1.1       cgd }
   1469   1.1       cgd 
   1470  1.60     aidan void
   1471  1.60     aidan ftp_handle_line(char *cp)
   1472  1.60     aidan {
   1473  1.62     lukem 
   1474  1.60     aidan 	cmdp = cp;
   1475  1.60     aidan 	yyparse();
   1476  1.60     aidan }
   1477  1.60     aidan 
   1478  1.60     aidan void
   1479  1.60     aidan ftp_loop(void)
   1480  1.60     aidan {
   1481  1.88     lukem 	int ret;
   1482  1.62     lukem 
   1483  1.60     aidan 	while (1) {
   1484  1.60     aidan 		(void) alarm(curclass.timeout);
   1485  1.90       roy 		ret = get_line(cbuf, sizeof(cbuf)-1, stdin);
   1486  1.88     lukem 		(void) alarm(0);
   1487  1.88     lukem 		if (ret == -1) {
   1488  1.60     aidan 			reply(221, "You could at least say goodbye.");
   1489  1.60     aidan 			dologout(0);
   1490  1.88     lukem 		} else if (ret == -2) {
   1491  1.88     lukem 			reply(500, "Command too long.");
   1492  1.88     lukem 		} else {
   1493  1.88     lukem 			ftp_handle_line(cbuf);
   1494  1.60     aidan 		}
   1495  1.60     aidan 	}
   1496  1.60     aidan 	/*NOTREACHED*/
   1497  1.60     aidan }
   1498  1.60     aidan 
   1499  1.76     lukem int
   1500  1.46     lukem yylex(void)
   1501   1.1       cgd {
   1502   1.1       cgd 	static int cpos, state;
   1503   1.4   deraadt 	char *cp, *cp2;
   1504   1.4   deraadt 	struct tab *p;
   1505  1.22     lukem 	int n;
   1506   1.4   deraadt 	char c;
   1507   1.1       cgd 
   1508  1.23     lukem 	switch (state) {
   1509   1.1       cgd 
   1510  1.23     lukem 	case CMD:
   1511  1.23     lukem 		hasyyerrored = 0;
   1512  1.60     aidan 		if ((cp = strchr(cmdp, '\r'))) {
   1513  1.42     lukem 			*cp = '\0';
   1514  1.86     lukem #if defined(HAVE_SETPROCTITLE)
   1515  1.60     aidan 			if (strncasecmp(cmdp, "PASS", 4) != 0 &&
   1516  1.60     aidan 			    strncasecmp(cmdp, "ACCT", 4) != 0)
   1517  1.60     aidan 				setproctitle("%s: %s", proctitle, cmdp);
   1518  1.86     lukem #endif /* defined(HAVE_SETPROCTITLE) */
   1519  1.23     lukem 			*cp++ = '\n';
   1520  1.23     lukem 			*cp = '\0';
   1521  1.23     lukem 		}
   1522  1.60     aidan 		if ((cp = strpbrk(cmdp, " \n")))
   1523  1.60     aidan 			cpos = cp - cmdp;
   1524  1.23     lukem 		if (cpos == 0)
   1525  1.23     lukem 			cpos = 4;
   1526  1.60     aidan 		c = cmdp[cpos];
   1527  1.60     aidan 		cmdp[cpos] = '\0';
   1528  1.60     aidan 		p = lookup(cmdtab, cmdp);
   1529  1.60     aidan 		cmdp[cpos] = c;
   1530  1.23     lukem 		if (p != NULL) {
   1531  1.60     aidan 			if (is_oob && ! CMD_OOB(p)) {
   1532  1.60     aidan 				/* command will be handled in-band */
   1533  1.60     aidan 				return (0);
   1534  1.60     aidan 			} else if (! CMD_IMPLEMENTED(p)) {
   1535  1.23     lukem 				reply(502, "%s command not implemented.",
   1536  1.23     lukem 				    p->name);
   1537  1.23     lukem 				hasyyerrored = 1;
   1538  1.23     lukem 				break;
   1539  1.23     lukem 			}
   1540  1.23     lukem 			state = p->state;
   1541  1.89     lukem 			yylval.cs = p->name;
   1542  1.23     lukem 			return (p->token);
   1543  1.23     lukem 		}
   1544  1.23     lukem 		break;
   1545  1.23     lukem 
   1546  1.23     lukem 	case SITECMD:
   1547  1.60     aidan 		if (cmdp[cpos] == ' ') {
   1548  1.23     lukem 			cpos++;
   1549  1.23     lukem 			return (SP);
   1550  1.23     lukem 		}
   1551  1.60     aidan 		cp = &cmdp[cpos];
   1552  1.23     lukem 		if ((cp2 = strpbrk(cp, " \n")))
   1553  1.60     aidan 			cpos = cp2 - cmdp;
   1554  1.60     aidan 		c = cmdp[cpos];
   1555  1.60     aidan 		cmdp[cpos] = '\0';
   1556  1.23     lukem 		p = lookup(sitetab, cp);
   1557  1.60     aidan 		cmdp[cpos] = c;
   1558  1.23     lukem 		if (p != NULL) {
   1559  1.47     lukem 			if (!CMD_IMPLEMENTED(p)) {
   1560  1.23     lukem 				reply(502, "SITE %s command not implemented.",
   1561  1.23     lukem 				    p->name);
   1562  1.23     lukem 				hasyyerrored = 1;
   1563  1.23     lukem 				break;
   1564  1.23     lukem 			}
   1565  1.23     lukem 			state = p->state;
   1566  1.89     lukem 			yylval.cs = p->name;
   1567  1.23     lukem 			return (p->token);
   1568  1.23     lukem 		}
   1569  1.23     lukem 		break;
   1570  1.23     lukem 
   1571  1.23     lukem 	case OSTR:
   1572  1.60     aidan 		if (cmdp[cpos] == '\n') {
   1573  1.60     aidan 			state = EOLN;
   1574  1.23     lukem 			return (CRLF);
   1575  1.23     lukem 		}
   1576  1.23     lukem 		/* FALLTHROUGH */
   1577  1.23     lukem 
   1578  1.23     lukem 	case STR1:
   1579  1.23     lukem 	case ZSTR1:
   1580  1.23     lukem 	dostr1:
   1581  1.60     aidan 		if (cmdp[cpos] == ' ') {
   1582  1.23     lukem 			cpos++;
   1583  1.39      tron 			state = state == OSTR ? STR2 : state+1;
   1584  1.23     lukem 			return (SP);
   1585  1.23     lukem 		}
   1586  1.23     lukem 		break;
   1587  1.23     lukem 
   1588  1.23     lukem 	case ZSTR2:
   1589  1.60     aidan 		if (cmdp[cpos] == '\n') {
   1590  1.60     aidan 			state = EOLN;
   1591  1.23     lukem 			return (CRLF);
   1592  1.23     lukem 		}
   1593  1.23     lukem 		/* FALLTHROUGH */
   1594  1.23     lukem 
   1595  1.23     lukem 	case STR2:
   1596  1.60     aidan 		cp = &cmdp[cpos];
   1597  1.23     lukem 		n = strlen(cp);
   1598  1.23     lukem 		cpos += n - 1;
   1599  1.23     lukem 		/*
   1600  1.23     lukem 		 * Make sure the string is nonempty and \n terminated.
   1601  1.23     lukem 		 */
   1602  1.60     aidan 		if (n > 1 && cmdp[cpos] == '\n') {
   1603  1.60     aidan 			cmdp[cpos] = '\0';
   1604  1.84  christos 			yylval.s = ftpd_strdup(cp);
   1605  1.60     aidan 			cmdp[cpos] = '\n';
   1606  1.23     lukem 			state = ARGS;
   1607  1.23     lukem 			return (STRING);
   1608  1.23     lukem 		}
   1609  1.23     lukem 		break;
   1610  1.23     lukem 
   1611  1.23     lukem 	case NSTR:
   1612  1.60     aidan 		if (cmdp[cpos] == ' ') {
   1613  1.23     lukem 			cpos++;
   1614  1.23     lukem 			return (SP);
   1615  1.23     lukem 		}
   1616  1.81       dsl 		if (isdigit((unsigned char)cmdp[cpos])) {
   1617  1.60     aidan 			cp = &cmdp[cpos];
   1618  1.81       dsl 			while (isdigit((unsigned char)cmdp[++cpos]))
   1619  1.23     lukem 				;
   1620  1.60     aidan 			c = cmdp[cpos];
   1621  1.60     aidan 			cmdp[cpos] = '\0';
   1622  1.70     lukem 			yylval.u.i = atoi(cp);
   1623  1.60     aidan 			cmdp[cpos] = c;
   1624  1.23     lukem 			state = STR1;
   1625  1.23     lukem 			return (NUMBER);
   1626  1.23     lukem 		}
   1627  1.23     lukem 		state = STR1;
   1628  1.23     lukem 		goto dostr1;
   1629   1.1       cgd 
   1630  1.23     lukem 	case ARGS:
   1631  1.81       dsl 		if (isdigit((unsigned char)cmdp[cpos])) {
   1632  1.60     aidan 			cp = &cmdp[cpos];
   1633  1.81       dsl 			while (isdigit((unsigned char)cmdp[++cpos]))
   1634  1.23     lukem 				;
   1635  1.60     aidan 			c = cmdp[cpos];
   1636  1.60     aidan 			cmdp[cpos] = '\0';
   1637  1.70     lukem 			yylval.u.i = atoi(cp);
   1638  1.93    plunky 			yylval.u.ll = STRTOLL(cp, NULL, 10);
   1639  1.60     aidan 			cmdp[cpos] = c;
   1640  1.23     lukem 			return (NUMBER);
   1641  1.32    itojun 		}
   1642  1.60     aidan 		if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0
   1643  1.81       dsl 		    && !isalnum((unsigned char)cmdp[cpos + 3])) {
   1644  1.32    itojun 			cpos += 3;
   1645  1.80     lukem 			return (ALL);
   1646  1.23     lukem 		}
   1647  1.60     aidan 		switch (cmdp[cpos++]) {
   1648  1.23     lukem 
   1649  1.23     lukem 		case '\n':
   1650  1.60     aidan 			state = EOLN;
   1651  1.23     lukem 			return (CRLF);
   1652  1.23     lukem 
   1653  1.23     lukem 		case ' ':
   1654  1.23     lukem 			return (SP);
   1655  1.23     lukem 
   1656  1.23     lukem 		case ',':
   1657  1.23     lukem 			return (COMMA);
   1658  1.23     lukem 
   1659  1.23     lukem 		case 'A':
   1660  1.23     lukem 		case 'a':
   1661  1.23     lukem 			return (A);
   1662  1.23     lukem 
   1663  1.23     lukem 		case 'B':
   1664  1.23     lukem 		case 'b':
   1665  1.23     lukem 			return (B);
   1666  1.23     lukem 
   1667  1.23     lukem 		case 'C':
   1668  1.23     lukem 		case 'c':
   1669  1.23     lukem 			return (C);
   1670  1.23     lukem 
   1671  1.23     lukem 		case 'E':
   1672  1.23     lukem 		case 'e':
   1673  1.23     lukem 			return (E);
   1674  1.23     lukem 
   1675  1.23     lukem 		case 'F':
   1676  1.23     lukem 		case 'f':
   1677  1.23     lukem 			return (F);
   1678  1.23     lukem 
   1679  1.23     lukem 		case 'I':
   1680  1.23     lukem 		case 'i':
   1681  1.23     lukem 			return (I);
   1682   1.1       cgd 
   1683  1.23     lukem 		case 'L':
   1684  1.23     lukem 		case 'l':
   1685  1.23     lukem 			return (L);
   1686   1.1       cgd 
   1687  1.23     lukem 		case 'N':
   1688  1.23     lukem 		case 'n':
   1689  1.23     lukem 			return (N);
   1690   1.1       cgd 
   1691  1.23     lukem 		case 'P':
   1692  1.23     lukem 		case 'p':
   1693  1.23     lukem 			return (P);
   1694   1.1       cgd 
   1695  1.23     lukem 		case 'R':
   1696  1.23     lukem 		case 'r':
   1697  1.23     lukem 			return (R);
   1698   1.1       cgd 
   1699  1.23     lukem 		case 'S':
   1700  1.23     lukem 		case 's':
   1701  1.23     lukem 			return (S);
   1702   1.1       cgd 
   1703  1.23     lukem 		case 'T':
   1704  1.23     lukem 		case 't':
   1705  1.23     lukem 			return (T);
   1706   1.1       cgd 
   1707  1.23     lukem 		}
   1708  1.23     lukem 		break;
   1709  1.21     lukem 
   1710  1.23     lukem 	case NOARGS:
   1711  1.60     aidan 		if (cmdp[cpos] == '\n') {
   1712  1.60     aidan 			state = EOLN;
   1713  1.23     lukem 			return (CRLF);
   1714   1.1       cgd 		}
   1715  1.60     aidan 		c = cmdp[cpos];
   1716  1.60     aidan 		cmdp[cpos] = '\0';
   1717  1.60     aidan 		reply(501, "'%s' command does not take any arguments.", cmdp);
   1718  1.23     lukem 		hasyyerrored = 1;
   1719  1.60     aidan 		cmdp[cpos] = c;
   1720  1.23     lukem 		break;
   1721  1.23     lukem 
   1722  1.60     aidan 	case EOLN:
   1723  1.60     aidan 		state = CMD;
   1724  1.60     aidan 		return (0);
   1725  1.60     aidan 
   1726  1.23     lukem 	default:
   1727  1.23     lukem 		fatal("Unknown state in scanner.");
   1728   1.1       cgd 	}
   1729  1.23     lukem 	yyerror(NULL);
   1730  1.23     lukem 	state = CMD;
   1731  1.80     lukem 	return (0);
   1732   1.1       cgd }
   1733   1.1       cgd 
   1734  1.22     lukem /* ARGSUSED */
   1735  1.22     lukem void
   1736  1.89     lukem yyerror(const char *s)
   1737  1.22     lukem {
   1738  1.22     lukem 	char *cp;
   1739  1.22     lukem 
   1740  1.60     aidan 	if (hasyyerrored || is_oob)
   1741  1.22     lukem 		return;
   1742  1.60     aidan 	if ((cp = strchr(cmdp,'\n')) != NULL)
   1743  1.22     lukem 		*cp = '\0';
   1744  1.60     aidan 	reply(500, "'%s': command not understood.", cmdp);
   1745  1.22     lukem 	hasyyerrored = 1;
   1746  1.22     lukem }
   1747  1.22     lukem 
   1748   1.4   deraadt static void
   1749  1.46     lukem help(struct tab *ctab, const char *s)
   1750   1.1       cgd {
   1751   1.4   deraadt 	struct tab *c;
   1752   1.4   deraadt 	int width, NCMDS;
   1753  1.89     lukem 	const char *htype;
   1754   1.1       cgd 
   1755   1.1       cgd 	if (ctab == sitetab)
   1756  1.66     lukem 		htype = "SITE ";
   1757   1.1       cgd 	else
   1758  1.66     lukem 		htype = "";
   1759   1.1       cgd 	width = 0, NCMDS = 0;
   1760   1.1       cgd 	for (c = ctab; c->name != NULL; c++) {
   1761   1.1       cgd 		int len = strlen(c->name);
   1762   1.1       cgd 
   1763   1.1       cgd 		if (len > width)
   1764   1.1       cgd 			width = len;
   1765   1.1       cgd 		NCMDS++;
   1766   1.1       cgd 	}
   1767   1.1       cgd 	width = (width + 8) &~ 7;
   1768   1.1       cgd 	if (s == 0) {
   1769   1.4   deraadt 		int i, j, w;
   1770   1.1       cgd 		int columns, lines;
   1771   1.1       cgd 
   1772  1.49  sommerfe 		reply(-214, "%s", "");
   1773  1.66     lukem 		reply(0, "The following %scommands are recognized.", htype);
   1774  1.48     lukem 		reply(0, "(`-' = not implemented, `+' = supports options)");
   1775   1.1       cgd 		columns = 76 / width;
   1776   1.1       cgd 		if (columns == 0)
   1777   1.1       cgd 			columns = 1;
   1778   1.1       cgd 		lines = (NCMDS + columns - 1) / columns;
   1779   1.1       cgd 		for (i = 0; i < lines; i++) {
   1780  1.48     lukem 			cprintf(stdout, "    ");
   1781   1.1       cgd 			for (j = 0; j < columns; j++) {
   1782   1.1       cgd 				c = ctab + j * lines + i;
   1783  1.48     lukem 				cprintf(stdout, "%s", c->name);
   1784  1.23     lukem 				w = strlen(c->name);
   1785  1.47     lukem 				if (! CMD_IMPLEMENTED(c)) {
   1786  1.48     lukem 					CPUTC('-', stdout);
   1787  1.23     lukem 					w++;
   1788  1.23     lukem 				}
   1789  1.47     lukem 				if (CMD_HAS_OPTIONS(c)) {
   1790  1.48     lukem 					CPUTC('+', stdout);
   1791  1.23     lukem 					w++;
   1792  1.23     lukem 				}
   1793   1.1       cgd 				if (c + lines >= &ctab[NCMDS])
   1794   1.1       cgd 					break;
   1795   1.1       cgd 				while (w < width) {
   1796  1.48     lukem 					CPUTC(' ', stdout);
   1797   1.1       cgd 					w++;
   1798   1.1       cgd 				}
   1799   1.1       cgd 			}
   1800  1.48     lukem 			cprintf(stdout, "\r\n");
   1801   1.1       cgd 		}
   1802   1.1       cgd 		(void) fflush(stdout);
   1803   1.1       cgd 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
   1804   1.1       cgd 		return;
   1805   1.1       cgd 	}
   1806   1.1       cgd 	c = lookup(ctab, s);
   1807   1.1       cgd 	if (c == (struct tab *)0) {
   1808  1.71   darrenr 		reply(502, "Unknown command '%s'.", s);
   1809   1.1       cgd 		return;
   1810   1.1       cgd 	}
   1811  1.47     lukem 	if (CMD_IMPLEMENTED(c))
   1812  1.66     lukem 		reply(214, "Syntax: %s%s %s", htype, c->name, c->help);
   1813   1.1       cgd 	else
   1814  1.71   darrenr 		reply(504, "%s%-*s\t%s; not implemented.", htype, width,
   1815   1.1       cgd 		    c->name, c->help);
   1816  1.52     lukem }
   1817  1.52     lukem 
   1818  1.52     lukem /*
   1819  1.52     lukem  * Check that the structures used for a PORT, LPRT or EPRT command are
   1820  1.52     lukem  * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks.
   1821  1.52     lukem  * If family != -1 check that his_addr.su_family == family.
   1822  1.52     lukem  */
   1823  1.52     lukem static void
   1824  1.52     lukem port_check(const char *cmd, int family)
   1825  1.52     lukem {
   1826  1.61    itojun 	char h1[NI_MAXHOST], h2[NI_MAXHOST];
   1827  1.61    itojun 	char s1[NI_MAXHOST], s2[NI_MAXHOST];
   1828  1.61    itojun 	const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
   1829  1.52     lukem 
   1830  1.52     lukem 	if (epsvall) {
   1831  1.52     lukem 		reply(501, "%s disallowed after EPSV ALL", cmd);
   1832  1.52     lukem 		return;
   1833  1.52     lukem 	}
   1834  1.52     lukem 
   1835  1.52     lukem 	if (family != -1 && his_addr.su_family != family) {
   1836  1.52     lukem  port_check_fail:
   1837  1.52     lukem 		reply(500, "Illegal %s command rejected", cmd);
   1838  1.52     lukem 		return;
   1839  1.52     lukem 	}
   1840  1.52     lukem 
   1841  1.52     lukem 	if (data_dest.su_family != his_addr.su_family)
   1842  1.52     lukem 		goto port_check_fail;
   1843  1.52     lukem 
   1844  1.52     lukem 			/* be paranoid, if told so */
   1845  1.56     lukem 	if (CURCLASS_FLAGS_ISSET(checkportcmd)) {
   1846  1.61    itojun #ifdef INET6
   1847  1.61    itojun 		/*
   1848  1.61    itojun 		 * be paranoid, there are getnameinfo implementation that does
   1849  1.61    itojun 		 * not present scopeid portion
   1850  1.61    itojun 		 */
   1851  1.61    itojun 		if (data_dest.su_family == AF_INET6 &&
   1852  1.61    itojun 		    data_dest.su_scope_id != his_addr.su_scope_id)
   1853  1.52     lukem 			goto port_check_fail;
   1854  1.53  christos #endif
   1855  1.61    itojun 
   1856  1.61    itojun 		if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len,
   1857  1.61    itojun 		    h1, sizeof(h1), s1, sizeof(s1), niflags))
   1858  1.61    itojun 			goto port_check_fail;
   1859  1.61    itojun 		if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
   1860  1.61    itojun 		    h2, sizeof(h2), s2, sizeof(s2), niflags))
   1861  1.61    itojun 			goto port_check_fail;
   1862  1.61    itojun 
   1863  1.61    itojun 		if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0)
   1864  1.52     lukem 			goto port_check_fail;
   1865  1.52     lukem 	}
   1866  1.52     lukem 
   1867  1.52     lukem 	usedefault = 0;
   1868  1.52     lukem 	if (pdata >= 0) {
   1869  1.52     lukem 		(void) close(pdata);
   1870  1.52     lukem 		pdata = -1;
   1871  1.52     lukem 	}
   1872  1.52     lukem 	reply(200, "%s command successful.", cmd);
   1873  1.23     lukem }
   1874