ftpcmd.y revision 133936
1/*	$NetBSD: ftpcmd.y,v 1.80 2004-08-09 12:56:47 lukem Exp $	*/
2
3/*-
4 * Copyright (c) 1997-2004 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Luke Mewburn.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 *    must display the following acknowledgement:
20 *        This product includes software developed by the NetBSD
21 *        Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 *    contributors may be used to endorse or promote products derived
24 *    from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39/*
40 * Copyright (c) 1985, 1988, 1993, 1994
41 *	The Regents of the University of California.  All rights reserved.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 *    notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 *    notice, this list of conditions and the following disclaimer in the
50 *    documentation and/or other materials provided with the distribution.
51 * 3. Neither the name of the University nor the names of its contributors
52 *    may be used to endorse or promote products derived from this software
53 *    without specific prior written permission.
54 *
55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65 * SUCH DAMAGE.
66 *
67 *	@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94
68 */
69
70/*
71 * Grammar for FTP commands.
72 * See RFC 959.
73 */
74
75%{
76#include <sys/cdefs.h>
77
78#ifndef lint
79#if 0
80static char sccsid[] = "@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94";
81#else
82__RCSID("$NetBSD: ftpcmd.y,v 1.80 2004-08-09 12:56:47 lukem Exp $");
83#endif
84#endif /* not lint */
85
86#include <sys/param.h>
87#include <sys/socket.h>
88#include <sys/stat.h>
89
90#include <netinet/in.h>
91#include <arpa/ftp.h>
92#include <arpa/inet.h>
93
94#include <ctype.h>
95#include <errno.h>
96#include <pwd.h>
97#include <stdio.h>
98#include <stdlib.h>
99#include <string.h>
100#include <syslog.h>
101#include <time.h>
102#include <tzfile.h>
103#include <unistd.h>
104#include <netdb.h>
105
106#ifdef KERBEROS5
107#include <krb5/krb5.h>
108#endif
109
110#include "extern.h"
111#include "version.h"
112
113static	int cmd_type;
114static	int cmd_form;
115static	int cmd_bytesz;
116
117char	cbuf[FTP_BUFLEN];
118char	*cmdp;
119char	*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
172cmd_sel
173	: cmd
174		{
175			REASSIGN(fromname, NULL);
176			restart_point = (off_t) 0;
177		}
178
179	| rcmd
180
181	;
182
183cmd
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
874rcmd
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
898username
899	: STRING
900	;
901
902password
903	: /* empty */
904		{
905			$$ = (char *)calloc(1, sizeof(char));
906		}
907
908	| STRING
909	;
910
911byte_size
912	: NUMBER
913		{
914			$$ = $1.i;
915		}
916	;
917
918host_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
934host_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
955host_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
989form_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
1006type_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
1056struct_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
1073mode_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
1090pathname
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
1133pathstring
1134	: STRING
1135	;
1136
1137octal_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
1163mechanism_name
1164	: STRING
1165	;
1166
1167base64data
1168	: STRING
1169	;
1170
1171prot_code
1172	: STRING
1173	;
1174
1175decimal_integer
1176	: NUMBER
1177		{
1178			$$ = $1.i;
1179		}
1180	;
1181
1182check_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
1209struct 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
1286struct 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
1296static	int	check_write(const char *, int);
1297static	void	help(struct tab *, const char *);
1298static	void	port_check(const char *, int);
1299	int	yylex(void);
1300
1301extern 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 */
1309static int
1310check_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(*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
1347struct tab *
1348lookup(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 */
1362char *
1363getline(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
1445void
1446ftp_handle_line(char *cp)
1447{
1448
1449	cmdp = cp;
1450	yyparse();
1451}
1452
1453void
1454ftp_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
1469int
1470yylex(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(cmdp[cpos])) {
1587			cp = &cmdp[cpos];
1588			while (isdigit(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(cmdp[cpos])) {
1602			cp = &cmdp[cpos];
1603			while (isdigit(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(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 */
1705void
1706yyerror(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
1718static void
1719help(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 */
1793static void
1794port_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