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