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