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