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