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