ftpcmd.y revision 116439
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 116439 2003-06-16 11:30:23Z 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 remotehost[];
93extern	char proctitle[];
94extern	int usedefault;
95extern  int transflag;
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 = (char *) 0;
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 = (char *) 0;
164			restart_point = (off_t) 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	| 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	| EPRT check_login SP STRING CRLF
228		{
229			char delim;
230			char *tmp = NULL;
231			char *p, *q;
232			char *result[3];
233			struct addrinfo hints;
234			struct addrinfo *res;
235			int i;
236
237			if (epsvall) {
238				reply(501, "no EPRT allowed after EPSV ALL");
239				goto eprt_done;
240			}
241			if (!$2)
242				goto eprt_done;
243
244			memset(&data_dest, 0, sizeof(data_dest));
245			tmp = strdup($4);
246			if (ftpdebug)
247				syslog(LOG_DEBUG, "%s", tmp);
248			if (!tmp) {
249				fatalerror("not enough core");
250				/*NOTREACHED*/
251			}
252			p = tmp;
253			delim = p[0];
254			p++;
255			memset(result, 0, sizeof(result));
256			for (i = 0; i < 3; i++) {
257				q = strchr(p, delim);
258				if (!q || *q != delim) {
259		parsefail:
260					reply(500,
261						"Invalid argument, rejected.");
262					if (tmp)
263						free(tmp);
264					usedefault = 1;
265					goto eprt_done;
266				}
267				*q++ = '\0';
268				result[i] = p;
269				if (ftpdebug)
270					syslog(LOG_DEBUG, "%d: %s", i, p);
271				p = q;
272			}
273
274			/* some more sanity check */
275			p = result[0];
276			while (*p) {
277				if (!isdigit(*p))
278					goto parsefail;
279				p++;
280			}
281			p = result[2];
282			while (*p) {
283				if (!isdigit(*p))
284					goto parsefail;
285				p++;
286			}
287
288			/* grab address */
289			memset(&hints, 0, sizeof(hints));
290			if (atoi(result[0]) == 1)
291				hints.ai_family = PF_INET;
292#ifdef INET6
293			else if (atoi(result[0]) == 2)
294				hints.ai_family = PF_INET6;
295#endif
296			else
297				hints.ai_family = PF_UNSPEC;	/*XXX*/
298			hints.ai_socktype = SOCK_STREAM;
299			i = getaddrinfo(result[1], result[2], &hints, &res);
300			if (i)
301				goto parsefail;
302			memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
303#ifdef INET6
304			if (his_addr.su_family == AF_INET6
305			    && data_dest.su_family == AF_INET6) {
306				/* XXX more sanity checks! */
307				data_dest.su_sin6.sin6_scope_id =
308					his_addr.su_sin6.sin6_scope_id;
309			}
310#endif
311			free(tmp);
312			tmp = NULL;
313
314			if (port_check("EPRT") == 1)
315				goto eprt_done;
316#ifdef INET6
317			if (his_addr.su_family != AF_INET6) {
318				usedefault = 1;
319				reply(500, "Invalid address rejected.");
320				goto eprt_done;
321			}
322			if (port_check_v6("EPRT") == 1)
323				goto eprt_done;
324#endif
325		eprt_done:
326			free($4);
327		}
328	| PASV check_login CRLF
329		{
330			if (epsvall)
331				reply(501, "no PASV allowed after EPSV ALL");
332			else if ($2)
333				passive();
334		}
335	| LPSV check_login CRLF
336		{
337			if (epsvall)
338				reply(501, "no LPSV allowed after EPSV ALL");
339			else if ($2)
340				long_passive("LPSV", PF_UNSPEC);
341		}
342	| EPSV check_login_epsv SP NUMBER CRLF
343		{
344			if ($2) {
345				int pf;
346				switch ($4.i) {
347				case 1:
348					pf = PF_INET;
349					break;
350#ifdef INET6
351				case 2:
352					pf = PF_INET6;
353					break;
354#endif
355				default:
356					pf = -1;	/*junk value*/
357					break;
358				}
359				long_passive("EPSV", pf);
360			}
361		}
362	| EPSV check_login_epsv SP ALL CRLF
363		{
364			if ($2) {
365				reply(200,
366				      "EPSV ALL command successful.");
367				epsvall++;
368			}
369		}
370	| EPSV check_login_epsv CRLF
371		{
372			if ($2)
373				long_passive("EPSV", PF_UNSPEC);
374		}
375	| TYPE check_login SP type_code CRLF
376		{
377			if ($2) {
378				switch (cmd_type) {
379
380				case TYPE_A:
381					if (cmd_form == FORM_N) {
382						reply(200, "Type set to A.");
383						type = cmd_type;
384						form = cmd_form;
385					} else
386						reply(504, "Form must be N.");
387					break;
388
389				case TYPE_E:
390					reply(504, "Type E not implemented.");
391					break;
392
393				case TYPE_I:
394					reply(200, "Type set to I.");
395					type = cmd_type;
396					break;
397
398				case TYPE_L:
399#if CHAR_BIT == 8
400					if (cmd_bytesz == 8) {
401						reply(200,
402						    "Type set to L (byte size 8).");
403						type = cmd_type;
404					} else
405						reply(504, "Byte size must be 8.");
406#else /* CHAR_BIT == 8 */
407					UNIMPLEMENTED for CHAR_BIT != 8
408#endif /* CHAR_BIT == 8 */
409				}
410			}
411		}
412	| STRU check_login SP struct_code CRLF
413		{
414			if ($2) {
415				switch ($4) {
416
417				case STRU_F:
418					reply(200, "STRU F ok.");
419					break;
420
421				default:
422					reply(504, "Unimplemented STRU type.");
423				}
424			}
425		}
426	| MODE check_login SP mode_code CRLF
427		{
428			if ($2) {
429				switch ($4) {
430
431				case MODE_S:
432					reply(200, "MODE S ok.");
433					break;
434
435				default:
436					reply(502, "Unimplemented MODE type.");
437				}
438			}
439		}
440	| ALLO check_login SP NUMBER CRLF
441		{
442			if ($2) {
443				reply(202, "ALLO command ignored.");
444			}
445		}
446	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
447		{
448			if ($2) {
449				reply(202, "ALLO command ignored.");
450			}
451		}
452	| RETR check_login SP pathname CRLF
453		{
454			if (noretr || (guest && noguestretr))
455				reply(500, "RETR command is disabled");
456			else if ($2 && $4 != NULL)
457				retrieve((char *) 0, $4);
458
459			if ($4 != NULL)
460				free($4);
461		}
462	| STOR check_login_ro SP pathname CRLF
463		{
464			if ($2 && $4 != NULL)
465				store($4, "w", 0);
466			if ($4 != NULL)
467				free($4);
468		}
469	| APPE check_login_ro SP pathname CRLF
470		{
471			if ($2 && $4 != NULL)
472				store($4, "a", 0);
473			if ($4 != NULL)
474				free($4);
475		}
476	| NLST check_login CRLF
477		{
478			if ($2)
479				send_file_list(".");
480		}
481	| NLST check_login SP pathstring CRLF
482		{
483			if ($2)
484				send_file_list($4);
485			free($4);
486		}
487	| LIST check_login CRLF
488		{
489			if ($2)
490				retrieve(_PATH_LS " -lgA", "");
491		}
492	| LIST check_login SP pathstring CRLF
493		{
494			if ($2)
495				retrieve(_PATH_LS " -lgA %s", $4);
496			free($4);
497		}
498	| STAT check_login SP pathname CRLF
499		{
500			if ($2 && $4 != NULL)
501				statfilecmd($4);
502			if ($4 != NULL)
503				free($4);
504		}
505	| STAT check_login CRLF
506		{
507			if ($2) {
508				statcmd();
509			}
510		}
511	| DELE check_login_ro SP pathname CRLF
512		{
513			if ($2 && $4 != NULL)
514				delete($4);
515			if ($4 != NULL)
516				free($4);
517		}
518	| RNTO check_login_ro SP pathname CRLF
519		{
520			if ($2 && $4 != NULL) {
521				if (fromname) {
522					renamecmd(fromname, $4);
523					free(fromname);
524					fromname = (char *) 0;
525				} else {
526					reply(503, "Bad sequence of commands.");
527				}
528			}
529			if ($4 != NULL)
530				free($4);
531		}
532	| ABOR check_login CRLF
533		{
534			if ($2)
535				reply(225, "ABOR command successful.");
536		}
537	| CWD check_login CRLF
538		{
539			if ($2) {
540				cwd(homedir);
541			}
542		}
543	| CWD check_login SP pathname CRLF
544		{
545			if ($2 && $4 != NULL)
546				cwd($4);
547			if ($4 != NULL)
548				free($4);
549		}
550	| HELP CRLF
551		{
552			help(cmdtab, (char *) 0);
553		}
554	| HELP SP STRING CRLF
555		{
556			char *cp = $3;
557
558			if (strncasecmp(cp, "SITE", 4) == 0) {
559				cp = $3 + 4;
560				if (*cp == ' ')
561					cp++;
562				if (*cp)
563					help(sitetab, cp);
564				else
565					help(sitetab, (char *) 0);
566			} else
567				help(cmdtab, $3);
568			free($3);
569		}
570	| NOOP CRLF
571		{
572			reply(200, "NOOP command successful.");
573		}
574	| MKD check_login_ro SP pathname CRLF
575		{
576			if ($2 && $4 != NULL)
577				makedir($4);
578			if ($4 != NULL)
579				free($4);
580		}
581	| RMD check_login_ro SP pathname CRLF
582		{
583			if ($2 && $4 != NULL)
584				removedir($4);
585			if ($4 != NULL)
586				free($4);
587		}
588	| PWD check_login CRLF
589		{
590			if ($2)
591				pwd();
592		}
593	| CDUP check_login CRLF
594		{
595			if ($2)
596				cwd("..");
597		}
598	| SITE SP HELP CRLF
599		{
600			help(sitetab, (char *) 0);
601		}
602	| SITE SP HELP SP STRING CRLF
603		{
604			help(sitetab, $5);
605			free($5);
606		}
607	| SITE SP MDFIVE check_login SP pathname CRLF
608		{
609			char p[64], *q;
610
611			if ($4 && $6) {
612				q = MD5File($6, p);
613				if (q != NULL)
614					reply(200, "MD5(%s) = %s", $6, p);
615				else
616					perror_reply(550, $6);
617			}
618			if ($6)
619				free($6);
620		}
621	| SITE SP UMASK check_login CRLF
622		{
623			int oldmask;
624
625			if ($4) {
626				oldmask = umask(0);
627				(void) umask(oldmask);
628				reply(200, "Current UMASK is %03o", oldmask);
629			}
630		}
631	| SITE SP UMASK check_login SP octal_number CRLF
632		{
633			int oldmask;
634
635			if ($4) {
636				if (($6 == -1) || ($6 > 0777)) {
637					reply(501, "Bad UMASK value");
638				} else {
639					oldmask = umask($6);
640					reply(200,
641					    "UMASK set to %03o (was %03o)",
642					    $6, oldmask);
643				}
644			}
645		}
646	| SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
647		{
648			if ($4 && ($8 != NULL)) {
649				if (($6 == -1 ) || ($6 > 0777))
650					reply(501, "Bad mode value");
651				else if (chmod($8, $6) < 0)
652					perror_reply(550, $8);
653				else
654					reply(200, "CHMOD command successful.");
655			}
656			if ($8 != NULL)
657				free($8);
658		}
659	| SITE SP check_login IDLE CRLF
660		{
661			if ($3)
662				reply(200,
663			    	    "Current IDLE time limit is %d seconds; max %d",
664				    timeout, maxtimeout);
665		}
666	| SITE SP check_login IDLE SP NUMBER CRLF
667		{
668			if ($3) {
669				if ($6.i < 30 || $6.i > maxtimeout) {
670					reply(501,
671					    "Maximum IDLE time must be between 30 and %d seconds",
672					    maxtimeout);
673				} else {
674					timeout = $6.i;
675					(void) alarm((unsigned) timeout);
676					reply(200,
677					    "Maximum IDLE time set to %d seconds",
678					    timeout);
679				}
680			}
681		}
682	| STOU check_login_ro SP pathname CRLF
683		{
684			if ($2 && $4 != NULL)
685				store($4, "w", 1);
686			if ($4 != NULL)
687				free($4);
688		}
689	| SYST check_login CRLF
690		{
691			if ($2) {
692				if (hostinfo)
693#ifdef BSD
694					reply(215, "UNIX Type: L%d Version: BSD-%d",
695					      CHAR_BIT, BSD);
696#else /* BSD */
697					reply(215, "UNIX Type: L%d", CHAR_BIT);
698#endif /* BSD */
699				else
700					reply(215, "UNKNOWN Type: L%d", CHAR_BIT);
701			}
702		}
703
704		/*
705		 * SIZE is not in RFC959, but Postel has blessed it and
706		 * it will be in the updated RFC.
707		 *
708		 * Return size of file in a format suitable for
709		 * using with RESTART (we just count bytes).
710		 */
711	| SIZE check_login SP pathname CRLF
712		{
713			if ($2 && $4 != NULL)
714				sizecmd($4);
715			if ($4 != NULL)
716				free($4);
717		}
718
719		/*
720		 * MDTM is not in RFC959, but Postel has blessed it and
721		 * it will be in the updated RFC.
722		 *
723		 * Return modification time of file as an ISO 3307
724		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
725		 * where xxx is the fractional second (of any precision,
726		 * not necessarily 3 digits)
727		 */
728	| MDTM check_login SP pathname CRLF
729		{
730			if ($2 && $4 != NULL) {
731				struct stat stbuf;
732				if (stat($4, &stbuf) < 0)
733					reply(550, "%s: %s",
734					    $4, strerror(errno));
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 = (off_t) 0;
770			if ($2 && $4) {
771				if (fromname)
772					free(fromname);
773				fromname = (char *) 0;
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 = (char *) 0;
788				restart_point = $4.o;
789				reply(350, "Restarting at %llu. %s",
790				    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 *);
1148static int	 port_check_v6(const char *);
1149static void	 sizecmd(char *);
1150static void	 toolong(int);
1151static void	 v4map_data_dest(void);
1152static int	 yylex(void);
1153
1154static struct tab *
1155lookup(struct tab *p, char *cmd)
1156{
1157
1158	for (; p->name != NULL; p++)
1159		if (strcmp(cmd, p->name) == 0)
1160			return (p);
1161	return (0);
1162}
1163
1164#include <arpa/telnet.h>
1165
1166/*
1167 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1168 */
1169char *
1170getline(char *s, int n, FILE *iop)
1171{
1172	int c;
1173	register char *cs;
1174
1175	cs = s;
1176/* tmpline may contain saved command from urgent mode interruption */
1177	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1178		*cs++ = tmpline[c];
1179		if (tmpline[c] == '\n') {
1180			*cs++ = '\0';
1181			if (ftpdebug)
1182				syslog(LOG_DEBUG, "command: %s", s);
1183			tmpline[0] = '\0';
1184			return(s);
1185		}
1186		if (c == 0)
1187			tmpline[0] = '\0';
1188	}
1189	while ((c = getc(iop)) != EOF) {
1190		c &= 0377;
1191		if (c == IAC) {
1192		    if ((c = getc(iop)) != EOF) {
1193			c &= 0377;
1194			switch (c) {
1195			case WILL:
1196			case WONT:
1197				c = getc(iop);
1198				printf("%c%c%c", IAC, DONT, 0377&c);
1199				(void) fflush(stdout);
1200				continue;
1201			case DO:
1202			case DONT:
1203				c = getc(iop);
1204				printf("%c%c%c", IAC, WONT, 0377&c);
1205				(void) fflush(stdout);
1206				continue;
1207			case IAC:
1208				break;
1209			default:
1210				continue;	/* ignore command */
1211			}
1212		    }
1213		}
1214		*cs++ = c;
1215		if (--n <= 0 || c == '\n')
1216			break;
1217	}
1218	if (c == EOF && cs == s)
1219		return (NULL);
1220	*cs++ = '\0';
1221	if (ftpdebug) {
1222		if (!guest && strncasecmp("pass ", s, 5) == 0) {
1223			/* Don't syslog passwords */
1224			syslog(LOG_DEBUG, "command: %.5s ???", s);
1225		} else {
1226			register char *cp;
1227			register int len;
1228
1229			/* Don't syslog trailing CR-LF */
1230			len = strlen(s);
1231			cp = s + len - 1;
1232			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1233				--cp;
1234				--len;
1235			}
1236			syslog(LOG_DEBUG, "command: %.*s", len, s);
1237		}
1238	}
1239	return (s);
1240}
1241
1242static void
1243toolong(int signo)
1244{
1245
1246	reply(421,
1247	    "Timeout (%d seconds): closing control connection.", timeout);
1248	if (logging)
1249		syslog(LOG_INFO, "User %s timed out after %d seconds",
1250		    (pw ? pw -> pw_name : "unknown"), timeout);
1251	dologout(1);
1252}
1253
1254static int
1255yylex(void)
1256{
1257	static int cpos;
1258	char *cp, *cp2;
1259	struct tab *p;
1260	int n;
1261	char c;
1262
1263	for (;;) {
1264		switch (state) {
1265
1266		case CMD:
1267			(void) signal(SIGALRM, toolong);
1268			(void) alarm((unsigned) timeout);
1269			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1270				reply(221, "You could at least say goodbye.");
1271				dologout(0);
1272			}
1273			(void) alarm(0);
1274#ifdef SETPROCTITLE
1275			if (strncasecmp(cbuf, "PASS", 4) != 0)
1276				setproctitle("%s: %s", proctitle, cbuf);
1277#endif /* SETPROCTITLE */
1278			if ((cp = strchr(cbuf, '\r'))) {
1279				*cp++ = '\n';
1280				*cp = '\0';
1281			}
1282			if ((cp = strpbrk(cbuf, " \n")))
1283				cpos = cp - cbuf;
1284			if (cpos == 0)
1285				cpos = 4;
1286			c = cbuf[cpos];
1287			cbuf[cpos] = '\0';
1288			upper(cbuf);
1289			p = lookup(cmdtab, cbuf);
1290			cbuf[cpos] = c;
1291			if (p != 0) {
1292				yylval.s = p->name;
1293				if (!p->implemented)
1294					return (NOTIMPL); /* state remains CMD */
1295				state = p->state;
1296				return (p->token);
1297			}
1298			break;
1299
1300		case SITECMD:
1301			if (cbuf[cpos] == ' ') {
1302				cpos++;
1303				return (SP);
1304			}
1305			cp = &cbuf[cpos];
1306			if ((cp2 = strpbrk(cp, " \n")))
1307				cpos = cp2 - cbuf;
1308			c = cbuf[cpos];
1309			cbuf[cpos] = '\0';
1310			upper(cp);
1311			p = lookup(sitetab, cp);
1312			cbuf[cpos] = c;
1313			if (guest == 0 && p != 0) {
1314				yylval.s = p->name;
1315				if (!p->implemented) {
1316					state = CMD;
1317					return (NOTIMPL);
1318				}
1319				state = p->state;
1320				return (p->token);
1321			}
1322			state = CMD;
1323			break;
1324
1325		case ZSTR1:
1326		case OSTR:
1327			if (cbuf[cpos] == '\n') {
1328				state = CMD;
1329				return (CRLF);
1330			}
1331			/* FALLTHROUGH */
1332
1333		case STR1:
1334		dostr1:
1335			if (cbuf[cpos] == ' ') {
1336				cpos++;
1337				state = state == OSTR ? STR2 : state+1;
1338				return (SP);
1339			}
1340			break;
1341
1342		case ZSTR2:
1343			if (cbuf[cpos] == '\n') {
1344				state = CMD;
1345				return (CRLF);
1346			}
1347			/* FALLTHROUGH */
1348
1349		case STR2:
1350			cp = &cbuf[cpos];
1351			n = strlen(cp);
1352			cpos += n - 1;
1353			/*
1354			 * Make sure the string is nonempty and \n terminated.
1355			 */
1356			if (n > 1 && cbuf[cpos] == '\n') {
1357				cbuf[cpos] = '\0';
1358				yylval.s = copy(cp);
1359				cbuf[cpos] = '\n';
1360				state = ARGS;
1361				return (STRING);
1362			}
1363			break;
1364
1365		case NSTR:
1366			if (cbuf[cpos] == ' ') {
1367				cpos++;
1368				return (SP);
1369			}
1370			if (isdigit(cbuf[cpos])) {
1371				cp = &cbuf[cpos];
1372				while (isdigit(cbuf[++cpos]))
1373					;
1374				c = cbuf[cpos];
1375				cbuf[cpos] = '\0';
1376				yylval.u.i = atoi(cp);
1377				cbuf[cpos] = c;
1378				state = STR1;
1379				return (NUMBER);
1380			}
1381			state = STR1;
1382			goto dostr1;
1383
1384		case ARGS:
1385			if (isdigit(cbuf[cpos])) {
1386				cp = &cbuf[cpos];
1387				while (isdigit(cbuf[++cpos]))
1388					;
1389				c = cbuf[cpos];
1390				cbuf[cpos] = '\0';
1391				yylval.u.i = atoi(cp);
1392				yylval.u.o = strtoull(cp, (char **)NULL, 10);
1393				cbuf[cpos] = c;
1394				return (NUMBER);
1395			}
1396			if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1397			 && !isalnum(cbuf[cpos + 3])) {
1398				cpos += 3;
1399				return ALL;
1400			}
1401			switch (cbuf[cpos++]) {
1402
1403			case '\n':
1404				state = CMD;
1405				return (CRLF);
1406
1407			case ' ':
1408				return (SP);
1409
1410			case ',':
1411				return (COMMA);
1412
1413			case 'A':
1414			case 'a':
1415				return (A);
1416
1417			case 'B':
1418			case 'b':
1419				return (B);
1420
1421			case 'C':
1422			case 'c':
1423				return (C);
1424
1425			case 'E':
1426			case 'e':
1427				return (E);
1428
1429			case 'F':
1430			case 'f':
1431				return (F);
1432
1433			case 'I':
1434			case 'i':
1435				return (I);
1436
1437			case 'L':
1438			case 'l':
1439				return (L);
1440
1441			case 'N':
1442			case 'n':
1443				return (N);
1444
1445			case 'P':
1446			case 'p':
1447				return (P);
1448
1449			case 'R':
1450			case 'r':
1451				return (R);
1452
1453			case 'S':
1454			case 's':
1455				return (S);
1456
1457			case 'T':
1458			case 't':
1459				return (T);
1460
1461			}
1462			break;
1463
1464		default:
1465			fatalerror("Unknown state in scanner.");
1466		}
1467		state = CMD;
1468		return (LEXERR);
1469	}
1470}
1471
1472void
1473upper(char *s)
1474{
1475	while (*s != '\0') {
1476		if (islower(*s))
1477			*s = toupper(*s);
1478		s++;
1479	}
1480}
1481
1482static char *
1483copy(char *s)
1484{
1485	char *p;
1486
1487	p = malloc((unsigned) strlen(s) + 1);
1488	if (p == NULL)
1489		fatalerror("Ran out of memory.");
1490	(void) strcpy(p, s);
1491	return (p);
1492}
1493
1494static void
1495help(struct tab *ctab, char *s)
1496{
1497	struct tab *c;
1498	int width, NCMDS;
1499	char *type;
1500
1501	if (ctab == sitetab)
1502		type = "SITE ";
1503	else
1504		type = "";
1505	width = 0, NCMDS = 0;
1506	for (c = ctab; c->name != NULL; c++) {
1507		int len = strlen(c->name);
1508
1509		if (len > width)
1510			width = len;
1511		NCMDS++;
1512	}
1513	width = (width + 8) &~ 7;
1514	if (s == 0) {
1515		int i, j, w;
1516		int columns, lines;
1517
1518		lreply(214, "The following %scommands are recognized %s.",
1519		    type, "(* =>'s unimplemented)");
1520		columns = 76 / width;
1521		if (columns == 0)
1522			columns = 1;
1523		lines = (NCMDS + columns - 1) / columns;
1524		for (i = 0; i < lines; i++) {
1525			printf("   ");
1526			for (j = 0; j < columns; j++) {
1527				c = ctab + j * lines + i;
1528				printf("%s%c", c->name,
1529					c->implemented ? ' ' : '*');
1530				if (c + lines >= &ctab[NCMDS])
1531					break;
1532				w = strlen(c->name) + 1;
1533				while (w < width) {
1534					putchar(' ');
1535					w++;
1536				}
1537			}
1538			printf("\r\n");
1539		}
1540		(void) fflush(stdout);
1541		if (hostinfo)
1542			reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1543		else
1544			reply(214, "End.");
1545		return;
1546	}
1547	upper(s);
1548	c = lookup(ctab, s);
1549	if (c == (struct tab *)0) {
1550		reply(502, "Unknown command %s.", s);
1551		return;
1552	}
1553	if (c->implemented)
1554		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1555	else
1556		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1557		    c->name, c->help);
1558}
1559
1560static void
1561sizecmd(char *filename)
1562{
1563	switch (type) {
1564	case TYPE_L:
1565	case TYPE_I: {
1566		struct stat stbuf;
1567		if (stat(filename, &stbuf) < 0)
1568			perror_reply(550, filename);
1569		else if (!S_ISREG(stbuf.st_mode))
1570			reply(550, "%s: not a plain file.", filename);
1571		else
1572			reply(213, "%qu", stbuf.st_size);
1573		break; }
1574	case TYPE_A: {
1575		FILE *fin;
1576		int c;
1577		off_t count;
1578		struct stat stbuf;
1579		fin = fopen(filename, "r");
1580		if (fin == NULL) {
1581			perror_reply(550, filename);
1582			return;
1583		}
1584		if (fstat(fileno(fin), &stbuf) < 0) {
1585			perror_reply(550, filename);
1586			(void) fclose(fin);
1587			return;
1588		} else if (!S_ISREG(stbuf.st_mode)) {
1589			reply(550, "%s: not a plain file.", filename);
1590			(void) fclose(fin);
1591			return;
1592		} else if (stbuf.st_size > MAXASIZE) {
1593			reply(550, "%s: too large for type A SIZE.", filename);
1594			(void) fclose(fin);
1595			return;
1596		}
1597
1598		count = 0;
1599		while((c=getc(fin)) != EOF) {
1600			if (c == '\n')	/* will get expanded to \r\n */
1601				count++;
1602			count++;
1603		}
1604		(void) fclose(fin);
1605
1606		reply(213, "%qd", count);
1607		break; }
1608	default:
1609		reply(504, "SIZE not implemented for type %s.",
1610		           typenames[type]);
1611	}
1612}
1613
1614/* Return 1, if port check is done. Return 0, if not yet. */
1615static int
1616port_check(const char *pcmd)
1617{
1618	if (his_addr.su_family == AF_INET) {
1619		if (data_dest.su_family != AF_INET) {
1620			usedefault = 1;
1621			reply(500, "Invalid address rejected.");
1622			return 1;
1623		}
1624		if (paranoid &&
1625		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1626		     memcmp(&data_dest.su_sin.sin_addr,
1627			    &his_addr.su_sin.sin_addr,
1628			    sizeof(data_dest.su_sin.sin_addr)))) {
1629			usedefault = 1;
1630			reply(500, "Illegal PORT range rejected.");
1631		} else {
1632			usedefault = 0;
1633			if (pdata >= 0) {
1634				(void) close(pdata);
1635				pdata = -1;
1636			}
1637			reply(200, "%s command successful.", pcmd);
1638		}
1639		return 1;
1640	}
1641	return 0;
1642}
1643
1644static int
1645check_login1(void)
1646{
1647	if (logged_in)
1648		return 1;
1649	else {
1650		reply(530, "Please login with USER and PASS.");
1651		return 0;
1652	}
1653}
1654
1655/*
1656 * Replace leading "~user" in a pathname by the user's login directory.
1657 * Returned string will be in a freshly malloced buffer unless it's NULL.
1658 */
1659static char *
1660exptilde(char *s)
1661{
1662	char *p, *q;
1663	char *path, *user;
1664	struct passwd *ppw;
1665
1666	if ((p = strdup(s)) == NULL)
1667		return (NULL);
1668	if (*p != '~')
1669		return (p);
1670
1671	user = p + 1;	/* skip tilde */
1672	if ((path = strchr(p, '/')) != NULL)
1673		*(path++) = '\0'; /* separate ~user from the rest of path */
1674	if (*user == '\0') /* no user specified, use the current user */
1675		user = pw->pw_name;
1676	/* read passwd even for the current user since we may be chrooted */
1677	if ((ppw = getpwnam(user)) != NULL) {
1678		/* user found, substitute login directory for ~user */
1679		if (path)
1680			asprintf(&q, "%s/%s", ppw->pw_dir, path);
1681		else
1682			q = strdup(ppw->pw_dir);
1683		free(p);
1684		p = q;
1685	} else {
1686		/* user not found, undo the damage */
1687		if (path)
1688			path[-1] = '/';
1689	}
1690	return (p);
1691}
1692
1693/*
1694 * Expand glob(3) patterns possibly present in a pathname.
1695 * Avoid expanding to a pathname including '\r' or '\n' in order to
1696 * not disrupt the FTP protocol.
1697 * The expansion found must be unique.
1698 * Return the result as a malloced string, or NULL if an error occured.
1699 *
1700 * Problem: this production is used for all pathname
1701 * processing, but only gives a 550 error reply.
1702 * This is a valid reply in some cases but not in others.
1703 */
1704static char *
1705expglob(char *s)
1706{
1707	char *p, **pp, *rval;
1708	int flags = GLOB_BRACE | GLOB_NOCHECK;
1709	int n;
1710	glob_t gl;
1711
1712	memset(&gl, 0, sizeof(gl));
1713	flags |= GLOB_LIMIT;
1714	gl.gl_matchc = MAXGLOBARGS;
1715	if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) {
1716		for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++)
1717			if (*(*pp + strcspn(*pp, "\r\n")) == '\0') {
1718				p = *pp;
1719				n++;
1720			}
1721		if (n == 0)
1722			rval = strdup(s);
1723		else if (n == 1)
1724			rval = strdup(p);
1725		else {
1726			reply(550, "ambiguous");
1727			rval = NULL;
1728		}
1729	} else {
1730		reply(550, "wildcard expansion error");
1731		rval = NULL;
1732	}
1733	globfree(&gl);
1734	return (rval);
1735}
1736
1737#ifdef INET6
1738/* Return 1, if port check is done. Return 0, if not yet. */
1739static int
1740port_check_v6(const char *pcmd)
1741{
1742	if (his_addr.su_family == AF_INET6) {
1743		if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1744			/* Convert data_dest into v4 mapped sockaddr.*/
1745			v4map_data_dest();
1746		if (data_dest.su_family != AF_INET6) {
1747			usedefault = 1;
1748			reply(500, "Invalid address rejected.");
1749			return 1;
1750		}
1751		if (paranoid &&
1752		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1753		     memcmp(&data_dest.su_sin6.sin6_addr,
1754			    &his_addr.su_sin6.sin6_addr,
1755			    sizeof(data_dest.su_sin6.sin6_addr)))) {
1756			usedefault = 1;
1757			reply(500, "Illegal PORT range rejected.");
1758		} else {
1759			usedefault = 0;
1760			if (pdata >= 0) {
1761				(void) close(pdata);
1762				pdata = -1;
1763			}
1764			reply(200, "%s command successful.", pcmd);
1765		}
1766		return 1;
1767	}
1768	return 0;
1769}
1770
1771static void
1772v4map_data_dest(void)
1773{
1774	struct in_addr savedaddr;
1775	int savedport;
1776
1777	if (data_dest.su_family != AF_INET) {
1778		usedefault = 1;
1779		reply(500, "Invalid address rejected.");
1780		return;
1781	}
1782
1783	savedaddr = data_dest.su_sin.sin_addr;
1784	savedport = data_dest.su_port;
1785
1786	memset(&data_dest, 0, sizeof(data_dest));
1787	data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1788	data_dest.su_sin6.sin6_family = AF_INET6;
1789	data_dest.su_sin6.sin6_port = savedport;
1790	memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1791	memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1792	       (caddr_t)&savedaddr, sizeof(savedaddr));
1793}
1794#endif
1795