Home | History | Annotate | Line # | Download | only in ftpd
ftpcmd.y revision 1.75
      1 /*	$NetBSD: ftpcmd.y,v 1.75 2003/03/03 01:52:13 lukem Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1997-2002 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. All advertising materials mentioning features or use of this software
     52  *    must display the following acknowledgement:
     53  *	This product includes software developed by the University of
     54  *	California, Berkeley and its contributors.
     55  * 4. Neither the name of the University nor the names of its contributors
     56  *    may be used to endorse or promote products derived from this software
     57  *    without specific prior written permission.
     58  *
     59  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     60  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     61  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     62  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     63  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     64  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     65  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     66  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     67  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     68  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     69  * SUCH DAMAGE.
     70  *
     71  *	@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94
     72  */
     73 
     74 /*
     75  * Grammar for FTP commands.
     76  * See RFC 959.
     77  */
     78 
     79 %{
     80 #include <sys/cdefs.h>
     81 
     82 #ifndef lint
     83 #if 0
     84 static char sccsid[] = "@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94";
     85 #else
     86 __RCSID("$NetBSD: ftpcmd.y,v 1.75 2003/03/03 01:52:13 lukem Exp $");
     87 #endif
     88 #endif /* not lint */
     89 
     90 #include <sys/param.h>
     91 #include <sys/socket.h>
     92 #include <sys/stat.h>
     93 
     94 #include <netinet/in.h>
     95 #include <arpa/ftp.h>
     96 #include <arpa/inet.h>
     97 
     98 #include <ctype.h>
     99 #include <errno.h>
    100 #include <pwd.h>
    101 #include <setjmp.h>
    102 #include <signal.h>
    103 #include <stdio.h>
    104 #include <stdlib.h>
    105 #include <string.h>
    106 #include <syslog.h>
    107 #include <time.h>
    108 #include <tzfile.h>
    109 #include <unistd.h>
    110 #include <netdb.h>
    111 
    112 #ifdef KERBEROS5
    113 #include <krb5/krb5.h>
    114 #endif
    115 
    116 #include "extern.h"
    117 #include "version.h"
    118 
    119 static	int cmd_type;
    120 static	int cmd_form;
    121 static	int cmd_bytesz;
    122 
    123 char	cbuf[FTP_BUFLEN];
    124 char	*cmdp;
    125 char	*fromname;
    126 
    127 %}
    128 
    129 %union {
    130 	struct {
    131 		LLT	ll;
    132 		int	i;
    133 	} u;
    134 	char   *s;
    135 }
    136 
    137 %token
    138 	A	B	C	E	F	I
    139 	L	N	P	R	S	T
    140 
    141 	SP	CRLF	COMMA
    142 
    143 	USER	PASS	ACCT	CWD	CDUP	SMNT
    144 	QUIT	REIN	PORT	PASV	TYPE	STRU
    145 	MODE	RETR	STOR	STOU	APPE	ALLO
    146 	REST	RNFR	RNTO	ABOR	DELE	RMD
    147 	MKD	PWD	LIST	NLST	SITE	SYST
    148 	STAT	HELP	NOOP
    149 
    150 	AUTH	ADAT	PROT	PBSZ	CCC	MIC
    151 	CONF	ENC
    152 
    153 	FEAT	OPTS
    154 
    155 	SIZE	MDTM	MLST	MLSD
    156 
    157 	LPRT	LPSV	EPRT	EPSV
    158 
    159 	MAIL	MLFL	MRCP	MRSQ	MSAM	MSND
    160 	MSOM
    161 
    162 	CHMOD	IDLE	RATEGET	RATEPUT	UMASK
    163 
    164 	LEXERR
    165 
    166 %token	<s> STRING
    167 %token	<s> ALL
    168 %token	<u> NUMBER
    169 
    170 %type	<u.i> check_login octal_number byte_size
    171 %type	<u.i> struct_code mode_code type_code form_code decimal_integer
    172 %type	<s> pathstring pathname password username
    173 %type	<s> mechanism_name base64data prot_code
    174 
    175 %start	cmd_sel
    176 
    177 %%
    178 
    179 cmd_sel
    180 	: cmd
    181 		{
    182 			fromname = NULL;
    183 			restart_point = (off_t) 0;
    184 		}
    185 
    186 	| rcmd
    187 
    188 	;
    189 
    190 cmd
    191 						/* RFC 959 */
    192 	: USER SP username CRLF
    193 		{
    194 			user($3);
    195 			free($3);
    196 		}
    197 
    198 	| PASS SP password CRLF
    199 		{
    200 			pass($3);
    201 			memset($3, 0, strlen($3));
    202 			free($3);
    203 		}
    204 
    205 	| CWD check_login CRLF
    206 		{
    207 			if ($2)
    208 				cwd(homedir);
    209 		}
    210 
    211 	| CWD check_login SP pathname CRLF
    212 		{
    213 			if ($2 && $4 != NULL)
    214 				cwd($4);
    215 			if ($4 != NULL)
    216 				free($4);
    217 		}
    218 
    219 	| CDUP check_login CRLF
    220 		{
    221 			if ($2)
    222 				cwd("..");
    223 		}
    224 
    225 	| QUIT CRLF
    226 		{
    227 			if (logged_in) {
    228 				reply(-221, "%s", "");
    229 				reply(0,
    230  "Data traffic for this session was " LLF " byte%s in " LLF " file%s.",
    231 				    (LLT)total_data, PLURAL(total_data),
    232 				    (LLT)total_files, PLURAL(total_files));
    233 				reply(0,
    234  "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.",
    235 				    (LLT)total_bytes, PLURAL(total_bytes),
    236 				    (LLT)total_xfers, PLURAL(total_xfers));
    237 			}
    238 			reply(221,
    239 			    "Thank you for using the FTP service on %s.",
    240 			    hostname);
    241 			if (logged_in && logging) {
    242 				syslog(LOG_INFO,
    243 		"Data traffic: " LLF " byte%s in " LLF " file%s",
    244 				    (LLT)total_data, PLURAL(total_data),
    245 				    (LLT)total_files, PLURAL(total_files));
    246 				syslog(LOG_INFO,
    247 		"Total traffic: " LLF " byte%s in " LLF " transfer%s",
    248 				    (LLT)total_bytes, PLURAL(total_bytes),
    249 				    (LLT)total_xfers, PLURAL(total_xfers));
    250 			}
    251 
    252 			dologout(0);
    253 		}
    254 
    255 	| PORT check_login SP host_port CRLF
    256 		{
    257 			if ($2)
    258 				port_check("PORT", AF_INET);
    259 		}
    260 
    261 	| LPRT check_login SP host_long_port4 CRLF
    262 		{
    263 			if ($2)
    264 				port_check("LPRT", AF_INET);
    265 		}
    266 
    267 	| LPRT check_login SP host_long_port6 CRLF
    268 		{
    269 #ifdef INET6
    270 			if ($2)
    271 				port_check("LPRT", AF_INET6);
    272 #else
    273 			reply(500, "IPv6 support not available.");
    274 #endif
    275 		}
    276 
    277 	| EPRT check_login SP STRING CRLF
    278 		{
    279 			if ($2) {
    280 				if (extended_port($4) == 0)
    281 					port_check("EPRT", -1);
    282 			}
    283 			free($4);
    284 		}
    285 
    286 	| PASV check_login CRLF
    287 		{
    288 			if ($2) {
    289 				if (CURCLASS_FLAGS_ISSET(passive))
    290 					passive();
    291 				else
    292 					reply(500, "PASV mode not available.");
    293 			}
    294 		}
    295 
    296 	| LPSV check_login CRLF
    297 		{
    298 			if ($2) {
    299 				if (CURCLASS_FLAGS_ISSET(passive)) {
    300 					if (epsvall)
    301 						reply(501,
    302 						    "LPSV disallowed after EPSV ALL");
    303 					else
    304 						long_passive("LPSV", PF_UNSPEC);
    305 				} else
    306 					reply(500, "LPSV mode not available.");
    307 			}
    308 		}
    309 
    310 	| EPSV check_login SP NUMBER CRLF
    311 		{
    312 			if ($2) {
    313 				if (CURCLASS_FLAGS_ISSET(passive))
    314 					long_passive("EPSV",
    315 					    epsvproto2af($4.i));
    316 				else
    317 					reply(500, "EPSV mode not available.");
    318 			}
    319 		}
    320 
    321 	| EPSV check_login SP ALL CRLF
    322 		{
    323 			if ($2) {
    324 				if (CURCLASS_FLAGS_ISSET(passive)) {
    325 					reply(200,
    326 					    "EPSV ALL command successful.");
    327 					epsvall++;
    328 				} else
    329 					reply(500, "EPSV mode not available.");
    330 			}
    331 		}
    332 
    333 	| EPSV check_login CRLF
    334 		{
    335 			if ($2) {
    336 				if (CURCLASS_FLAGS_ISSET(passive))
    337 					long_passive("EPSV", PF_UNSPEC);
    338 				else
    339 					reply(500, "EPSV mode not available.");
    340 			}
    341 		}
    342 
    343 	| TYPE check_login SP type_code CRLF
    344 		{
    345 			if ($2) {
    346 
    347 			switch (cmd_type) {
    348 
    349 			case TYPE_A:
    350 				if (cmd_form == FORM_N) {
    351 					reply(200, "Type set to A.");
    352 					type = cmd_type;
    353 					form = cmd_form;
    354 				} else
    355 					reply(504, "Form must be N.");
    356 				break;
    357 
    358 			case TYPE_E:
    359 				reply(504, "Type E not implemented.");
    360 				break;
    361 
    362 			case TYPE_I:
    363 				reply(200, "Type set to I.");
    364 				type = cmd_type;
    365 				break;
    366 
    367 			case TYPE_L:
    368 #if NBBY == 8
    369 				if (cmd_bytesz == 8) {
    370 					reply(200,
    371 					    "Type set to L (byte size 8).");
    372 					type = cmd_type;
    373 				} else
    374 					reply(504, "Byte size must be 8.");
    375 #else /* NBBY == 8 */
    376 				UNIMPLEMENTED for NBBY != 8
    377 #endif /* NBBY == 8 */
    378 			}
    379 
    380 			}
    381 		}
    382 
    383 	| STRU check_login SP struct_code CRLF
    384 		{
    385 			if ($2) {
    386 				switch ($4) {
    387 
    388 				case STRU_F:
    389 					reply(200, "STRU F ok.");
    390 					break;
    391 
    392 				default:
    393 					reply(504, "Unimplemented STRU type.");
    394 				}
    395 			}
    396 		}
    397 
    398 	| MODE check_login SP mode_code CRLF
    399 		{
    400 			if ($2) {
    401 				switch ($4) {
    402 
    403 				case MODE_S:
    404 					reply(200, "MODE S ok.");
    405 					break;
    406 
    407 				default:
    408 					reply(502, "Unimplemented MODE type.");
    409 				}
    410 			}
    411 		}
    412 
    413 	| RETR check_login SP pathname CRLF
    414 		{
    415 			if ($2 && $4 != NULL)
    416 				retrieve(NULL, $4);
    417 			if ($4 != NULL)
    418 				free($4);
    419 		}
    420 
    421 	| STOR SP pathname CRLF
    422 		{
    423 			if (check_write($3, 1))
    424 				store($3, "w", 0);
    425 			if ($3 != NULL)
    426 				free($3);
    427 		}
    428 
    429 	| STOU SP pathname CRLF
    430 		{
    431 			if (check_write($3, 1))
    432 				store($3, "w", 1);
    433 			if ($3 != NULL)
    434 				free($3);
    435 		}
    436 
    437 	| APPE SP pathname CRLF
    438 		{
    439 			if (check_write($3, 1))
    440 				store($3, "a", 0);
    441 			if ($3 != NULL)
    442 				free($3);
    443 		}
    444 
    445 	| ALLO check_login SP NUMBER CRLF
    446 		{
    447 			if ($2)
    448 				reply(202, "ALLO command ignored.");
    449 		}
    450 
    451 	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
    452 		{
    453 			if ($2)
    454 				reply(202, "ALLO command ignored.");
    455 		}
    456 
    457 	| RNTO SP pathname CRLF
    458 		{
    459 			if (check_write($3, 0)) {
    460 				if (fromname) {
    461 					renamecmd(fromname, $3);
    462 					free(fromname);
    463 					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 > 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 				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 				fromname = renamefrom($3);
    899 			if ($3 != NULL)
    900 				free($3);
    901 		}
    902 	;
    903 
    904 username
    905 	: STRING
    906 	;
    907 
    908 password
    909 	: /* empty */
    910 		{
    911 			$$ = (char *)calloc(1, sizeof(char));
    912 		}
    913 
    914 	| STRING
    915 	;
    916 
    917 byte_size
    918 	: NUMBER
    919 		{
    920 			$$ = $1.i;
    921 		}
    922 	;
    923 
    924 host_port
    925 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
    926 		NUMBER COMMA NUMBER
    927 		{
    928 			char *a, *p;
    929 
    930 			memset(&data_dest, 0, sizeof(data_dest));
    931 			data_dest.su_len = sizeof(struct sockaddr_in);
    932 			data_dest.su_family = AF_INET;
    933 			p = (char *)&data_dest.su_port;
    934 			p[0] = $9.i; p[1] = $11.i;
    935 			a = (char *)&data_dest.su_addr;
    936 			a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
    937 		}
    938 	;
    939 
    940 host_long_port4
    941 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
    942 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
    943 		NUMBER
    944 		{
    945 			char *a, *p;
    946 
    947 			memset(&data_dest, 0, sizeof(data_dest));
    948 			data_dest.su_len = sizeof(struct sockaddr_in);
    949 			data_dest.su_family = AF_INET;
    950 			p = (char *)&data_dest.su_port;
    951 			p[0] = $15.i; p[1] = $17.i;
    952 			a = (char *)&data_dest.su_addr;
    953 			a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
    954 
    955 			/* reject invalid LPRT command */
    956 			if ($1.i != 4 || $3.i != 4 || $13.i != 2)
    957 				memset(&data_dest, 0, sizeof(data_dest));
    958 		}
    959 	;
    960 
    961 host_long_port6
    962 	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
    963 		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
    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
    968 		{
    969 #ifdef INET6
    970 			char *a, *p;
    971 
    972 			memset(&data_dest, 0, sizeof(data_dest));
    973 			data_dest.su_len = sizeof(struct sockaddr_in6);
    974 			data_dest.su_family = AF_INET6;
    975 			p = (char *)&data_dest.su_port;
    976 			p[0] = $39.i; p[1] = $41.i;
    977 			a = (char *)&data_dest.si_su.su_sin6.sin6_addr;
    978 			a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
    979 			a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
    980 			a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
    981 			a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
    982 			if (his_addr.su_family == AF_INET6) {
    983 				/* XXX: more sanity checks! */
    984 				data_dest.su_scope_id = his_addr.su_scope_id;
    985 			}
    986 #else
    987 			memset(&data_dest, 0, sizeof(data_dest));
    988 #endif /* INET6 */
    989 			/* reject invalid LPRT command */
    990 			if ($1.i != 6 || $3.i != 16 || $37.i != 2)
    991 				memset(&data_dest, 0, sizeof(data_dest));
    992 		}
    993 	;
    994 
    995 form_code
    996 	: N
    997 		{
    998 			$$ = FORM_N;
    999 		}
   1000 
   1001 	| T
   1002 		{
   1003 			$$ = FORM_T;
   1004 		}
   1005 
   1006 	| C
   1007 		{
   1008 			$$ = FORM_C;
   1009 		}
   1010 	;
   1011 
   1012 type_code
   1013 	: A
   1014 		{
   1015 			cmd_type = TYPE_A;
   1016 			cmd_form = FORM_N;
   1017 		}
   1018 
   1019 	| A SP form_code
   1020 		{
   1021 			cmd_type = TYPE_A;
   1022 			cmd_form = $3;
   1023 		}
   1024 
   1025 	| E
   1026 		{
   1027 			cmd_type = TYPE_E;
   1028 			cmd_form = FORM_N;
   1029 		}
   1030 
   1031 	| E SP form_code
   1032 		{
   1033 			cmd_type = TYPE_E;
   1034 			cmd_form = $3;
   1035 		}
   1036 
   1037 	| I
   1038 		{
   1039 			cmd_type = TYPE_I;
   1040 		}
   1041 
   1042 	| L
   1043 		{
   1044 			cmd_type = TYPE_L;
   1045 			cmd_bytesz = NBBY;
   1046 		}
   1047 
   1048 	| L SP byte_size
   1049 		{
   1050 			cmd_type = TYPE_L;
   1051 			cmd_bytesz = $3;
   1052 		}
   1053 
   1054 		/* this is for a bug in the BBN ftp */
   1055 	| L byte_size
   1056 		{
   1057 			cmd_type = TYPE_L;
   1058 			cmd_bytesz = $2;
   1059 		}
   1060 	;
   1061 
   1062 struct_code
   1063 	: F
   1064 		{
   1065 			$$ = STRU_F;
   1066 		}
   1067 
   1068 	| R
   1069 		{
   1070 			$$ = STRU_R;
   1071 		}
   1072 
   1073 	| P
   1074 		{
   1075 			$$ = STRU_P;
   1076 		}
   1077 	;
   1078 
   1079 mode_code
   1080 	: S
   1081 		{
   1082 			$$ = MODE_S;
   1083 		}
   1084 
   1085 	| B
   1086 		{
   1087 			$$ = MODE_B;
   1088 		}
   1089 
   1090 	| C
   1091 		{
   1092 			$$ = MODE_C;
   1093 		}
   1094 	;
   1095 
   1096 pathname
   1097 	: pathstring
   1098 		{
   1099 			/*
   1100 			 * Problem: this production is used for all pathname
   1101 			 * processing, but only gives a 550 error reply.
   1102 			 * This is a valid reply in some cases but not in
   1103 			 * others.
   1104 			 */
   1105 			if (logged_in && $1 && *$1 == '~') {
   1106 				char	*path, *home, *result;
   1107 				size_t	len;
   1108 
   1109 				path = strchr($1 + 1, '/');
   1110 				if (path != NULL)
   1111 					*path++ = '\0';
   1112 				if ($1[1] == '\0')
   1113 					home = homedir;
   1114 				else {
   1115 					struct passwd	*hpw;
   1116 
   1117 					if ((hpw = getpwnam($1 + 1)) != NULL)
   1118 						home = hpw->pw_dir;
   1119 					else
   1120 						home = $1;
   1121 				}
   1122 				len = strlen(home) + 1;
   1123 				if (path != NULL)
   1124 					len += strlen(path) + 1;
   1125 				if ((result = malloc(len)) == NULL)
   1126 					fatal("Local resource failure: malloc");
   1127 				strlcpy(result, home, len);
   1128 				if (path != NULL) {
   1129 					strlcat(result, "/", len);
   1130 					strlcat(result, path, len);
   1131 				}
   1132 				$$ = result;
   1133 				free($1);
   1134 			} else
   1135 				$$ = $1;
   1136 		}
   1137 	;
   1138 
   1139 pathstring
   1140 	: STRING
   1141 	;
   1142 
   1143 octal_number
   1144 	: NUMBER
   1145 		{
   1146 			int ret, dec, multby, digit;
   1147 
   1148 			/*
   1149 			 * Convert a number that was read as decimal number
   1150 			 * to what it would be if it had been read as octal.
   1151 			 */
   1152 			dec = $1.i;
   1153 			multby = 1;
   1154 			ret = 0;
   1155 			while (dec) {
   1156 				digit = dec%10;
   1157 				if (digit > 7) {
   1158 					ret = -1;
   1159 					break;
   1160 				}
   1161 				ret += digit * multby;
   1162 				multby *= 8;
   1163 				dec /= 10;
   1164 			}
   1165 			$$ = ret;
   1166 		}
   1167 	;
   1168 
   1169 mechanism_name
   1170 	: STRING
   1171 	;
   1172 
   1173 base64data
   1174 	: STRING
   1175 	;
   1176 
   1177 prot_code
   1178 	: STRING
   1179 	;
   1180 
   1181 decimal_integer
   1182 	: NUMBER
   1183 		{
   1184 			$$ = $1.i;
   1185 		}
   1186 	;
   1187 
   1188 check_login
   1189 	: /* empty */
   1190 		{
   1191 			if (logged_in)
   1192 				$$ = 1;
   1193 			else {
   1194 				reply(530, "Please login with USER and PASS.");
   1195 				$$ = 0;
   1196 				hasyyerrored = 1;
   1197 			}
   1198 		}
   1199 	;
   1200 
   1201 %%
   1202 
   1203 #define	CMD	0	/* beginning of command */
   1204 #define	ARGS	1	/* expect miscellaneous arguments */
   1205 #define	STR1	2	/* expect SP followed by STRING */
   1206 #define	STR2	3	/* expect STRING */
   1207 #define	OSTR	4	/* optional SP then STRING */
   1208 #define	ZSTR1	5	/* SP then optional STRING */
   1209 #define	ZSTR2	6	/* optional STRING after SP */
   1210 #define	SITECMD	7	/* SITE command */
   1211 #define	NSTR	8	/* Number followed by a string */
   1212 #define NOARGS	9	/* No arguments allowed */
   1213 #define EOLN	10	/* End of line */
   1214 
   1215 struct tab cmdtab[] = {
   1216 				/* From RFC 959, in order defined (5.3.1) */
   1217 	{ "USER", USER, STR1,	1,	"<sp> username" },
   1218 	{ "PASS", PASS, ZSTR1,	1,	"<sp> password" },
   1219 	{ "ACCT", ACCT, STR1,	0,	"(specify account)" },
   1220 	{ "CWD",  CWD,  OSTR,	1,	"[ <sp> directory-name ]" },
   1221 	{ "CDUP", CDUP, NOARGS,	1,	"(change to parent directory)" },
   1222 	{ "SMNT", SMNT, ARGS,	0,	"(structure mount)" },
   1223 	{ "QUIT", QUIT, NOARGS,	1,	"(terminate service)" },
   1224 	{ "REIN", REIN, NOARGS,	0,	"(reinitialize server state)" },
   1225 	{ "PORT", PORT, ARGS,	1,	"<sp> b0, b1, b2, b3, b4, b5" },
   1226 	{ "LPRT", LPRT, ARGS,	1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
   1227 	{ "EPRT", EPRT, STR1,	1,	"<sp> |af|addr|port|" },
   1228 	{ "PASV", PASV, NOARGS,	1,	"(set server in passive mode)" },
   1229 	{ "LPSV", LPSV, ARGS,	1,	"(set server in passive mode)" },
   1230 	{ "EPSV", EPSV, ARGS,	1,	"[<sp> af|ALL]" },
   1231 	{ "TYPE", TYPE, ARGS,	1,	"<sp> [ A | E | I | L ]" },
   1232 	{ "STRU", STRU, ARGS,	1,	"(specify file structure)" },
   1233 	{ "MODE", MODE, ARGS,	1,	"(specify transfer mode)" },
   1234 	{ "RETR", RETR, STR1,	1,	"<sp> file-name" },
   1235 	{ "STOR", STOR, STR1,	1,	"<sp> file-name" },
   1236 	{ "STOU", STOU, STR1,	1,	"<sp> file-name" },
   1237 	{ "APPE", APPE, STR1,	1,	"<sp> file-name" },
   1238 	{ "ALLO", ALLO, ARGS,	1,	"allocate storage (vacuously)" },
   1239 	{ "REST", REST, ARGS,	1,	"<sp> offset (restart command)" },
   1240 	{ "RNFR", RNFR, STR1,	1,	"<sp> file-name" },
   1241 	{ "RNTO", RNTO, STR1,	1,	"<sp> file-name" },
   1242 	{ "ABOR", ABOR, NOARGS,	4,	"(abort operation)" },
   1243 	{ "DELE", DELE, STR1,	1,	"<sp> file-name" },
   1244 	{ "RMD",  RMD,  STR1,	1,	"<sp> path-name" },
   1245 	{ "MKD",  MKD,  STR1,	1,	"<sp> path-name" },
   1246 	{ "PWD",  PWD,  NOARGS,	1,	"(return current directory)" },
   1247 	{ "LIST", LIST, OSTR,	1,	"[ <sp> path-name ]" },
   1248 	{ "NLST", NLST, OSTR,	1,	"[ <sp> path-name ]" },
   1249 	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
   1250 	{ "SYST", SYST, NOARGS,	1,	"(get type of operating system)" },
   1251 	{ "STAT", STAT, OSTR,	4,	"[ <sp> path-name ]" },
   1252 	{ "HELP", HELP, OSTR,	1,	"[ <sp> <string> ]" },
   1253 	{ "NOOP", NOOP, NOARGS,	2,	"" },
   1254 
   1255 				/* From RFC 2228, in order defined */
   1256 	{ "AUTH", AUTH, STR1,	1,	"<sp> mechanism-name" },
   1257 	{ "ADAT", ADAT, STR1,	1,	"<sp> base-64-data" },
   1258 	{ "PROT", PROT, STR1,	1,	"<sp> prot-code" },
   1259 	{ "PBSZ", PBSZ, ARGS,	1,	"<sp> decimal-integer" },
   1260 	{ "CCC",  CCC,  NOARGS,	1,	"(Disable data protection)" },
   1261 	{ "MIC",  MIC,  STR1,	4,	"<sp> base64data" },
   1262 	{ "CONF", CONF, STR1,	4,	"<sp> base64data" },
   1263 	{ "ENC",  ENC,  STR1,	4,	"<sp> base64data" },
   1264 
   1265 				/* From RFC 2389, in order defined */
   1266 	{ "FEAT", FEAT, NOARGS,	1,	"(display extended features)" },
   1267 	{ "OPTS", OPTS, STR1,	1,	"<sp> command [ <sp> options ]" },
   1268 
   1269 				/* from draft-ietf-ftpext-mlst-11 */
   1270 	{ "MDTM", MDTM, OSTR,	1,	"<sp> path-name" },
   1271 	{ "SIZE", SIZE, OSTR,	1,	"<sp> path-name" },
   1272 	{ "MLST", MLST, OSTR,	2,	"[ <sp> path-name ]" },
   1273 	{ "MLSD", MLSD, OSTR,	1,	"[ <sp> directory-name ]" },
   1274 
   1275 				/* obsolete commands */
   1276 	{ "MAIL", MAIL, OSTR,	0,	"(mail to user)" },
   1277 	{ "MLFL", MLFL, OSTR,	0,	"(mail file)" },
   1278 	{ "MRCP", MRCP, STR1,	0,	"(mail recipient)" },
   1279 	{ "MRSQ", MRSQ, OSTR,	0,	"(mail recipient scheme question)" },
   1280 	{ "MSAM", MSAM, OSTR,	0,	"(mail send to terminal and mailbox)" },
   1281 	{ "MSND", MSND, OSTR,	0,	"(mail send to terminal)" },
   1282 	{ "MSOM", MSOM, OSTR,	0,	"(mail send to terminal or mailbox)" },
   1283 	{ "XCUP", CDUP, NOARGS,	1,	"(change to parent directory)" },
   1284 	{ "XCWD", CWD,  OSTR,	1,	"[ <sp> directory-name ]" },
   1285 	{ "XMKD", MKD,  STR1,	1,	"<sp> path-name" },
   1286 	{ "XPWD", PWD,  NOARGS,	1,	"(return current directory)" },
   1287 	{ "XRMD", RMD,  STR1,	1,	"<sp> path-name" },
   1288 
   1289 	{  NULL,  0,	0,	0,	0 }
   1290 };
   1291 
   1292 struct tab sitetab[] = {
   1293 	{ "CHMOD",	CHMOD,	NSTR,	1,	"<sp> mode <sp> file-name" },
   1294 	{ "HELP",	HELP,	OSTR,	1,	"[ <sp> <string> ]" },
   1295 	{ "IDLE",	IDLE,	ARGS,	1,	"[ <sp> maximum-idle-time ]" },
   1296 	{ "RATEGET",	RATEGET,OSTR,	1,	"[ <sp> get-throttle-rate ]" },
   1297 	{ "RATEPUT",	RATEPUT,OSTR,	1,	"[ <sp> put-throttle-rate ]" },
   1298 	{ "UMASK",	UMASK,	ARGS,	1,	"[ <sp> umask ]" },
   1299 	{ NULL,		0,	0,	0,	NULL }
   1300 };
   1301 
   1302 static	int	check_write(const char *, int);
   1303 static	void	help(struct tab *, const char *);
   1304 static	void	port_check(const char *, int);
   1305 static	void	toolong(int);
   1306 static	int	yylex(void);
   1307 
   1308 extern int epsvall;
   1309 
   1310 /*
   1311  * Check if a filename is allowed to be modified (isupload == 0) or
   1312  * uploaded (isupload == 1), and if necessary, check the filename is `sane'.
   1313  * If the filename is NULL, fail.
   1314  * If the filename is "", don't do the sane name check.
   1315  */
   1316 static int
   1317 check_write(const char *file, int isupload)
   1318 {
   1319 	if (file == NULL)
   1320 		return (0);
   1321 	if (! logged_in) {
   1322 		reply(530, "Please login with USER and PASS.");
   1323 		return (0);
   1324 	}
   1325 		/* checking modify */
   1326 	if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) {
   1327 		reply(502, "No permission to use this command.");
   1328 		return (0);
   1329 	}
   1330 		/* checking upload */
   1331 	if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) {
   1332 		reply(502, "No permission to use this command.");
   1333 		return (0);
   1334 	}
   1335 
   1336 		/* checking sanenames */
   1337 	if (file[0] != '\0' && CURCLASS_FLAGS_ISSET(sanenames)) {
   1338 		const char *p;
   1339 
   1340 		if (file[0] == '.')
   1341 			goto insane_name;
   1342 		for (p = file; *p; p++) {
   1343 			if (isalnum(*p) || *p == '-' || *p == '+' ||
   1344 			    *p == ',' || *p == '.' || *p == '_')
   1345 				continue;
   1346  insane_name:
   1347 			reply(553, "File name `%s' not allowed.", file);
   1348 			return (0);
   1349 		}
   1350 	}
   1351 	return (1);
   1352 }
   1353 
   1354 struct tab *
   1355 lookup(struct tab *p, const char *cmd)
   1356 {
   1357 
   1358 	for (; p->name != NULL; p++)
   1359 		if (strcasecmp(cmd, p->name) == 0)
   1360 			return (p);
   1361 	return (0);
   1362 }
   1363 
   1364 #include <arpa/telnet.h>
   1365 
   1366 /*
   1367  * getline - a hacked up version of fgets to ignore TELNET escape codes.
   1368  */
   1369 char *
   1370 getline(char *s, int n, FILE *iop)
   1371 {
   1372 	int c;
   1373 	char *cs;
   1374 
   1375 	cs = s;
   1376 /* tmpline may contain saved command from urgent mode interruption */
   1377 	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
   1378 		*cs++ = tmpline[c];
   1379 		if (tmpline[c] == '\n') {
   1380 			*cs++ = '\0';
   1381 			if (debug)
   1382 				syslog(LOG_DEBUG, "command: %s", s);
   1383 			tmpline[0] = '\0';
   1384 			return(s);
   1385 		}
   1386 		if (c == 0)
   1387 			tmpline[0] = '\0';
   1388 	}
   1389 	while ((c = getc(iop)) != EOF) {
   1390 		total_bytes++;
   1391 		total_bytes_in++;
   1392 		c &= 0377;
   1393 		if (c == IAC) {
   1394 		    if ((c = getc(iop)) != EOF) {
   1395 			total_bytes++;
   1396 			total_bytes_in++;
   1397 			c &= 0377;
   1398 			switch (c) {
   1399 			case WILL:
   1400 			case WONT:
   1401 				c = getc(iop);
   1402 				total_bytes++;
   1403 				total_bytes_in++;
   1404 				cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c);
   1405 				(void) fflush(stdout);
   1406 				continue;
   1407 			case DO:
   1408 			case DONT:
   1409 				c = getc(iop);
   1410 				total_bytes++;
   1411 				total_bytes_in++;
   1412 				cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c);
   1413 				(void) fflush(stdout);
   1414 				continue;
   1415 			case IAC:
   1416 				break;
   1417 			default:
   1418 				continue;	/* ignore command */
   1419 			}
   1420 		    }
   1421 		}
   1422 		*cs++ = c;
   1423 		if (--n <= 0 || c == '\n')
   1424 			break;
   1425 	}
   1426 	if (c == EOF && cs == s)
   1427 		return (NULL);
   1428 	*cs++ = '\0';
   1429 	if (debug) {
   1430 		if ((curclass.type != CLASS_GUEST &&
   1431 		    strncasecmp(s, "PASS ", 5) == 0) ||
   1432 		    strncasecmp(s, "ACCT ", 5) == 0) {
   1433 			/* Don't syslog passwords */
   1434 			syslog(LOG_DEBUG, "command: %.4s ???", s);
   1435 		} else {
   1436 			char *cp;
   1437 			int len;
   1438 
   1439 			/* Don't syslog trailing CR-LF */
   1440 			len = strlen(s);
   1441 			cp = s + len - 1;
   1442 			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
   1443 				--cp;
   1444 				--len;
   1445 			}
   1446 			syslog(LOG_DEBUG, "command: %.*s", len, s);
   1447 		}
   1448 	}
   1449 	return (s);
   1450 }
   1451 
   1452 static void
   1453 toolong(int signo)
   1454 {
   1455 
   1456 	reply(421,
   1457 	    "Timeout (" LLF " seconds): closing control connection.",
   1458 	    (LLT)curclass.timeout);
   1459 	if (logging)
   1460 		syslog(LOG_INFO, "User %s timed out after " LLF " seconds",
   1461 		    (pw ? pw->pw_name : "unknown"), (LLT)curclass.timeout);
   1462 	dologout(1);
   1463 }
   1464 
   1465 void
   1466 ftp_handle_line(char *cp)
   1467 {
   1468 
   1469 	cmdp = cp;
   1470 	yyparse();
   1471 }
   1472 
   1473 void
   1474 ftp_loop(void)
   1475 {
   1476 
   1477 	while (1) {
   1478 		(void) signal(SIGALRM, toolong);
   1479 		(void) alarm(curclass.timeout);
   1480 		if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
   1481 			reply(221, "You could at least say goodbye.");
   1482 			dologout(0);
   1483 		}
   1484 		(void) alarm(0);
   1485 		ftp_handle_line(cbuf);
   1486 	}
   1487 	/*NOTREACHED*/
   1488 }
   1489 
   1490 static int
   1491 yylex(void)
   1492 {
   1493 	static int cpos, state;
   1494 	char *cp, *cp2;
   1495 	struct tab *p;
   1496 	int n;
   1497 	char c;
   1498 
   1499 	switch (state) {
   1500 
   1501 	case CMD:
   1502 		hasyyerrored = 0;
   1503 		if ((cp = strchr(cmdp, '\r'))) {
   1504 			*cp = '\0';
   1505 #if HAVE_SETPROCTITLE
   1506 			if (strncasecmp(cmdp, "PASS", 4) != 0 &&
   1507 			    strncasecmp(cmdp, "ACCT", 4) != 0)
   1508 				setproctitle("%s: %s", proctitle, cmdp);
   1509 #endif /* HAVE_SETPROCTITLE */
   1510 			*cp++ = '\n';
   1511 			*cp = '\0';
   1512 		}
   1513 		if ((cp = strpbrk(cmdp, " \n")))
   1514 			cpos = cp - cmdp;
   1515 		if (cpos == 0)
   1516 			cpos = 4;
   1517 		c = cmdp[cpos];
   1518 		cmdp[cpos] = '\0';
   1519 		p = lookup(cmdtab, cmdp);
   1520 		cmdp[cpos] = c;
   1521 		if (p != NULL) {
   1522 			if (is_oob && ! CMD_OOB(p)) {
   1523 				/* command will be handled in-band */
   1524 				return (0);
   1525 			} else if (! CMD_IMPLEMENTED(p)) {
   1526 				reply(502, "%s command not implemented.",
   1527 				    p->name);
   1528 				hasyyerrored = 1;
   1529 				break;
   1530 			}
   1531 			state = p->state;
   1532 			yylval.s = p->name;
   1533 			return (p->token);
   1534 		}
   1535 		break;
   1536 
   1537 	case SITECMD:
   1538 		if (cmdp[cpos] == ' ') {
   1539 			cpos++;
   1540 			return (SP);
   1541 		}
   1542 		cp = &cmdp[cpos];
   1543 		if ((cp2 = strpbrk(cp, " \n")))
   1544 			cpos = cp2 - cmdp;
   1545 		c = cmdp[cpos];
   1546 		cmdp[cpos] = '\0';
   1547 		p = lookup(sitetab, cp);
   1548 		cmdp[cpos] = c;
   1549 		if (p != NULL) {
   1550 			if (!CMD_IMPLEMENTED(p)) {
   1551 				reply(502, "SITE %s command not implemented.",
   1552 				    p->name);
   1553 				hasyyerrored = 1;
   1554 				break;
   1555 			}
   1556 			state = p->state;
   1557 			yylval.s = p->name;
   1558 			return (p->token);
   1559 		}
   1560 		break;
   1561 
   1562 	case OSTR:
   1563 		if (cmdp[cpos] == '\n') {
   1564 			state = EOLN;
   1565 			return (CRLF);
   1566 		}
   1567 		/* FALLTHROUGH */
   1568 
   1569 	case STR1:
   1570 	case ZSTR1:
   1571 	dostr1:
   1572 		if (cmdp[cpos] == ' ') {
   1573 			cpos++;
   1574 			state = state == OSTR ? STR2 : state+1;
   1575 			return (SP);
   1576 		}
   1577 		break;
   1578 
   1579 	case ZSTR2:
   1580 		if (cmdp[cpos] == '\n') {
   1581 			state = EOLN;
   1582 			return (CRLF);
   1583 		}
   1584 		/* FALLTHROUGH */
   1585 
   1586 	case STR2:
   1587 		cp = &cmdp[cpos];
   1588 		n = strlen(cp);
   1589 		cpos += n - 1;
   1590 		/*
   1591 		 * Make sure the string is nonempty and \n terminated.
   1592 		 */
   1593 		if (n > 1 && cmdp[cpos] == '\n') {
   1594 			cmdp[cpos] = '\0';
   1595 			yylval.s = xstrdup(cp);
   1596 			cmdp[cpos] = '\n';
   1597 			state = ARGS;
   1598 			return (STRING);
   1599 		}
   1600 		break;
   1601 
   1602 	case NSTR:
   1603 		if (cmdp[cpos] == ' ') {
   1604 			cpos++;
   1605 			return (SP);
   1606 		}
   1607 		if (isdigit(cmdp[cpos])) {
   1608 			cp = &cmdp[cpos];
   1609 			while (isdigit(cmdp[++cpos]))
   1610 				;
   1611 			c = cmdp[cpos];
   1612 			cmdp[cpos] = '\0';
   1613 			yylval.u.i = atoi(cp);
   1614 			cmdp[cpos] = c;
   1615 			state = STR1;
   1616 			return (NUMBER);
   1617 		}
   1618 		state = STR1;
   1619 		goto dostr1;
   1620 
   1621 	case ARGS:
   1622 		if (isdigit(cmdp[cpos])) {
   1623 			cp = &cmdp[cpos];
   1624 			while (isdigit(cmdp[++cpos]))
   1625 				;
   1626 			c = cmdp[cpos];
   1627 			cmdp[cpos] = '\0';
   1628 			yylval.u.i = atoi(cp);
   1629 			yylval.u.ll = STRTOLL(cp, (char **)NULL, 10);
   1630 			cmdp[cpos] = c;
   1631 			return (NUMBER);
   1632 		}
   1633 		if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0
   1634 		 && !isalnum(cmdp[cpos + 3])) {
   1635 			yylval.s = xstrdup("ALL");
   1636 			cpos += 3;
   1637 			return ALL;
   1638 		}
   1639 		switch (cmdp[cpos++]) {
   1640 
   1641 		case '\n':
   1642 			state = EOLN;
   1643 			return (CRLF);
   1644 
   1645 		case ' ':
   1646 			return (SP);
   1647 
   1648 		case ',':
   1649 			return (COMMA);
   1650 
   1651 		case 'A':
   1652 		case 'a':
   1653 			return (A);
   1654 
   1655 		case 'B':
   1656 		case 'b':
   1657 			return (B);
   1658 
   1659 		case 'C':
   1660 		case 'c':
   1661 			return (C);
   1662 
   1663 		case 'E':
   1664 		case 'e':
   1665 			return (E);
   1666 
   1667 		case 'F':
   1668 		case 'f':
   1669 			return (F);
   1670 
   1671 		case 'I':
   1672 		case 'i':
   1673 			return (I);
   1674 
   1675 		case 'L':
   1676 		case 'l':
   1677 			return (L);
   1678 
   1679 		case 'N':
   1680 		case 'n':
   1681 			return (N);
   1682 
   1683 		case 'P':
   1684 		case 'p':
   1685 			return (P);
   1686 
   1687 		case 'R':
   1688 		case 'r':
   1689 			return (R);
   1690 
   1691 		case 'S':
   1692 		case 's':
   1693 			return (S);
   1694 
   1695 		case 'T':
   1696 		case 't':
   1697 			return (T);
   1698 
   1699 		}
   1700 		break;
   1701 
   1702 	case NOARGS:
   1703 		if (cmdp[cpos] == '\n') {
   1704 			state = EOLN;
   1705 			return (CRLF);
   1706 		}
   1707 		c = cmdp[cpos];
   1708 		cmdp[cpos] = '\0';
   1709 		reply(501, "'%s' command does not take any arguments.", cmdp);
   1710 		hasyyerrored = 1;
   1711 		cmdp[cpos] = c;
   1712 		break;
   1713 
   1714 	case EOLN:
   1715 		state = CMD;
   1716 		return (0);
   1717 
   1718 	default:
   1719 		fatal("Unknown state in scanner.");
   1720 	}
   1721 	yyerror(NULL);
   1722 	state = CMD;
   1723 	is_oob = 0;
   1724 	longjmp(errcatch, 0);
   1725 	/* NOTREACHED */
   1726 }
   1727 
   1728 /* ARGSUSED */
   1729 void
   1730 yyerror(char *s)
   1731 {
   1732 	char *cp;
   1733 
   1734 	if (hasyyerrored || is_oob)
   1735 		return;
   1736 	if ((cp = strchr(cmdp,'\n')) != NULL)
   1737 		*cp = '\0';
   1738 	reply(500, "'%s': command not understood.", cmdp);
   1739 	hasyyerrored = 1;
   1740 }
   1741 
   1742 static void
   1743 help(struct tab *ctab, const char *s)
   1744 {
   1745 	struct tab *c;
   1746 	int width, NCMDS;
   1747 	char *htype;
   1748 
   1749 	if (ctab == sitetab)
   1750 		htype = "SITE ";
   1751 	else
   1752 		htype = "";
   1753 	width = 0, NCMDS = 0;
   1754 	for (c = ctab; c->name != NULL; c++) {
   1755 		int len = strlen(c->name);
   1756 
   1757 		if (len > width)
   1758 			width = len;
   1759 		NCMDS++;
   1760 	}
   1761 	width = (width + 8) &~ 7;
   1762 	if (s == 0) {
   1763 		int i, j, w;
   1764 		int columns, lines;
   1765 
   1766 		reply(-214, "%s", "");
   1767 		reply(0, "The following %scommands are recognized.", htype);
   1768 		reply(0, "(`-' = not implemented, `+' = supports options)");
   1769 		columns = 76 / width;
   1770 		if (columns == 0)
   1771 			columns = 1;
   1772 		lines = (NCMDS + columns - 1) / columns;
   1773 		for (i = 0; i < lines; i++) {
   1774 			cprintf(stdout, "    ");
   1775 			for (j = 0; j < columns; j++) {
   1776 				c = ctab + j * lines + i;
   1777 				cprintf(stdout, "%s", c->name);
   1778 				w = strlen(c->name);
   1779 				if (! CMD_IMPLEMENTED(c)) {
   1780 					CPUTC('-', stdout);
   1781 					w++;
   1782 				}
   1783 				if (CMD_HAS_OPTIONS(c)) {
   1784 					CPUTC('+', stdout);
   1785 					w++;
   1786 				}
   1787 				if (c + lines >= &ctab[NCMDS])
   1788 					break;
   1789 				while (w < width) {
   1790 					CPUTC(' ', stdout);
   1791 					w++;
   1792 				}
   1793 			}
   1794 			cprintf(stdout, "\r\n");
   1795 		}
   1796 		(void) fflush(stdout);
   1797 		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
   1798 		return;
   1799 	}
   1800 	c = lookup(ctab, s);
   1801 	if (c == (struct tab *)0) {
   1802 		reply(502, "Unknown command '%s'.", s);
   1803 		return;
   1804 	}
   1805 	if (CMD_IMPLEMENTED(c))
   1806 		reply(214, "Syntax: %s%s %s", htype, c->name, c->help);
   1807 	else
   1808 		reply(504, "%s%-*s\t%s; not implemented.", htype, width,
   1809 		    c->name, c->help);
   1810 }
   1811 
   1812 /*
   1813  * Check that the structures used for a PORT, LPRT or EPRT command are
   1814  * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks.
   1815  * If family != -1 check that his_addr.su_family == family.
   1816  */
   1817 static void
   1818 port_check(const char *cmd, int family)
   1819 {
   1820 	char h1[NI_MAXHOST], h2[NI_MAXHOST];
   1821 	char s1[NI_MAXHOST], s2[NI_MAXHOST];
   1822 #ifdef NI_WITHSCOPEID
   1823 	const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
   1824 #else
   1825 	const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
   1826 #endif
   1827 
   1828 	if (epsvall) {
   1829 		reply(501, "%s disallowed after EPSV ALL", cmd);
   1830 		return;
   1831 	}
   1832 
   1833 	if (family != -1 && his_addr.su_family != family) {
   1834  port_check_fail:
   1835 		reply(500, "Illegal %s command rejected", cmd);
   1836 		return;
   1837 	}
   1838 
   1839 	if (data_dest.su_family != his_addr.su_family)
   1840 		goto port_check_fail;
   1841 
   1842 			/* be paranoid, if told so */
   1843 	if (CURCLASS_FLAGS_ISSET(checkportcmd)) {
   1844 #ifdef INET6
   1845 		/*
   1846 		 * be paranoid, there are getnameinfo implementation that does
   1847 		 * not present scopeid portion
   1848 		 */
   1849 		if (data_dest.su_family == AF_INET6 &&
   1850 		    data_dest.su_scope_id != his_addr.su_scope_id)
   1851 			goto port_check_fail;
   1852 #endif
   1853 
   1854 		if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len,
   1855 		    h1, sizeof(h1), s1, sizeof(s1), niflags))
   1856 			goto port_check_fail;
   1857 		if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
   1858 		    h2, sizeof(h2), s2, sizeof(s2), niflags))
   1859 			goto port_check_fail;
   1860 
   1861 		if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0)
   1862 			goto port_check_fail;
   1863 	}
   1864 
   1865 	usedefault = 0;
   1866 	if (pdata >= 0) {
   1867 		(void) close(pdata);
   1868 		pdata = -1;
   1869 	}
   1870 	reply(200, "%s command successful.", cmd);
   1871 }
   1872