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