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