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