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