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