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