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