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