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