ftpcmd.y revision 101378
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 101378 2002-08-05 14:10:57Z 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
76extern	union sockunion data_dest, his_addr;
77extern	int logged_in;
78extern	struct passwd *pw;
79extern	int guest;
80extern 	int paranoid;
81extern	int logging;
82extern	int type;
83extern	int form;
84extern	int ftpdebug;
85extern	int timeout;
86extern	int maxtimeout;
87extern  int pdata;
88extern	char *hostname;
89extern	char remotehost[];
90extern	char proctitle[];
91extern	int usedefault;
92extern  int transflag;
93extern  char tmpline[];
94extern	int readonly;
95extern	int noepsv;
96extern	int noretr;
97extern	int noguestretr;
98extern	char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */
99
100off_t	restart_point;
101
102static	int cmd_type;
103static	int cmd_form;
104static	int cmd_bytesz;
105static	int state;
106char	cbuf[512];
107char	*fromname = (char *) 0;
108
109extern int epsvall;
110
111%}
112
113%union {
114	struct {
115		off_t	o;
116		int	i;
117	} u;
118	char   *s;
119}
120
121%token
122	A	B	C	E	F	I
123	L	N	P	R	S	T
124	ALL
125
126	SP	CRLF	COMMA
127
128	USER	PASS	ACCT	REIN	QUIT	PORT
129	PASV	TYPE	STRU	MODE	RETR	STOR
130	APPE	MLFL	MAIL	MSND	MSOM	MSAM
131	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
132	ABOR	DELE	CWD	LIST	NLST	SITE
133	STAT	HELP	NOOP	MKD	RMD	PWD
134	CDUP	STOU	SMNT	SYST	SIZE	MDTM
135	LPRT	LPSV	EPRT	EPSV
136
137	UMASK	IDLE	CHMOD	MDFIVE
138
139	LEXERR
140
141%token	<s> STRING
142%token	<u> NUMBER
143
144%type	<u.i> check_login octal_number byte_size
145%type	<u.i> check_login_ro check_login_epsv
146%type	<u.i> struct_code mode_code type_code form_code
147%type	<s> pathstring pathname password username
148%type	<s> ALL
149
150%start	cmd_list
151
152%%
153
154cmd_list
155	: /* empty */
156	| cmd_list cmd
157		{
158			if (fromname)
159				free(fromname);
160			fromname = (char *) 0;
161			restart_point = (off_t) 0;
162		}
163	| cmd_list rcmd
164	;
165
166cmd
167	: USER SP username CRLF
168		{
169			user($3);
170			free($3);
171		}
172	| PASS SP password CRLF
173		{
174			pass($3);
175			free($3);
176		}
177	| PASS CRLF
178		{
179			pass("");
180		}
181	| PORT check_login SP host_port CRLF
182		{
183			if (epsvall) {
184				reply(501, "no PORT allowed after EPSV ALL");
185				goto port_done;
186			}
187			if (!$2)
188				goto port_done;
189			if (port_check("PORT") == 1)
190				goto port_done;
191#ifdef INET6
192			if ((his_addr.su_family != AF_INET6 ||
193			     !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
194				/* shoud never happen */
195				usedefault = 1;
196				reply(500, "Invalid address rejected.");
197				goto port_done;
198			}
199			port_check_v6("pcmd");
200#endif
201		port_done:
202		}
203	| LPRT check_login SP host_long_port CRLF
204		{
205			if (epsvall) {
206				reply(501, "no LPRT allowed after EPSV ALL");
207				goto lprt_done;
208			}
209			if (!$2)
210				goto lprt_done;
211			if (port_check("LPRT") == 1)
212				goto lprt_done;
213#ifdef INET6
214			if (his_addr.su_family != AF_INET6) {
215				usedefault = 1;
216				reply(500, "Invalid address rejected.");
217				goto lprt_done;
218			}
219			if (port_check_v6("LPRT") == 1)
220				goto lprt_done;
221#endif
222		lprt_done:
223		}
224	| EPRT check_login SP STRING CRLF
225		{
226			char delim;
227			char *tmp = NULL;
228			char *p, *q;
229			char *result[3];
230			struct addrinfo hints;
231			struct addrinfo *res;
232			int i;
233
234			if (epsvall) {
235				reply(501, "no EPRT allowed after EPSV ALL");
236				goto eprt_done;
237			}
238			if (!$2)
239				goto eprt_done;
240
241			memset(&data_dest, 0, sizeof(data_dest));
242			tmp = strdup($4);
243			if (ftpdebug)
244				syslog(LOG_DEBUG, "%s", tmp);
245			if (!tmp) {
246				fatalerror("not enough core");
247				/*NOTREACHED*/
248			}
249			p = tmp;
250			delim = p[0];
251			p++;
252			memset(result, 0, sizeof(result));
253			for (i = 0; i < 3; i++) {
254				q = strchr(p, delim);
255				if (!q || *q != delim) {
256		parsefail:
257					reply(500,
258						"Invalid argument, rejected.");
259					if (tmp)
260						free(tmp);
261					usedefault = 1;
262					goto eprt_done;
263				}
264				*q++ = '\0';
265				result[i] = p;
266				if (ftpdebug)
267					syslog(LOG_DEBUG, "%d: %s", i, p);
268				p = q;
269			}
270
271			/* some more sanity check */
272			p = result[0];
273			while (*p) {
274				if (!isdigit(*p))
275					goto parsefail;
276				p++;
277			}
278			p = result[2];
279			while (*p) {
280				if (!isdigit(*p))
281					goto parsefail;
282				p++;
283			}
284
285			/* grab address */
286			memset(&hints, 0, sizeof(hints));
287			if (atoi(result[0]) == 1)
288				hints.ai_family = PF_INET;
289#ifdef INET6
290			else if (atoi(result[0]) == 2)
291				hints.ai_family = PF_INET6;
292#endif
293			else
294				hints.ai_family = PF_UNSPEC;	/*XXX*/
295			hints.ai_socktype = SOCK_STREAM;
296			i = getaddrinfo(result[1], result[2], &hints, &res);
297			if (i)
298				goto parsefail;
299			memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
300#ifdef INET6
301			if (his_addr.su_family == AF_INET6
302			    && data_dest.su_family == AF_INET6) {
303				/* XXX more sanity checks! */
304				data_dest.su_sin6.sin6_scope_id =
305					his_addr.su_sin6.sin6_scope_id;
306			}
307#endif
308			free(tmp);
309			tmp = NULL;
310
311			if (port_check("EPRT") == 1)
312				goto eprt_done;
313#ifdef INET6
314			if (his_addr.su_family != AF_INET6) {
315				usedefault = 1;
316				reply(500, "Invalid address rejected.");
317				goto eprt_done;
318			}
319			if (port_check_v6("EPRT") == 1)
320				goto eprt_done;
321#endif
322		eprt_done:
323			free($4);
324		}
325	| PASV check_login CRLF
326		{
327			if (epsvall)
328				reply(501, "no PASV allowed after EPSV ALL");
329			else if ($2)
330				passive();
331		}
332	| LPSV check_login CRLF
333		{
334			if (epsvall)
335				reply(501, "no LPSV allowed after EPSV ALL");
336			else if ($2)
337				long_passive("LPSV", PF_UNSPEC);
338		}
339	| EPSV check_login_epsv SP NUMBER CRLF
340		{
341			if ($2) {
342				int pf;
343				switch ($4.i) {
344				case 1:
345					pf = PF_INET;
346					break;
347#ifdef INET6
348				case 2:
349					pf = PF_INET6;
350					break;
351#endif
352				default:
353					pf = -1;	/*junk value*/
354					break;
355				}
356				long_passive("EPSV", pf);
357			}
358		}
359	| EPSV check_login_epsv SP ALL CRLF
360		{
361			if ($2) {
362				reply(200,
363				      "EPSV ALL command successful.");
364				epsvall++;
365			}
366		}
367	| EPSV check_login_epsv CRLF
368		{
369			if ($2)
370				long_passive("EPSV", PF_UNSPEC);
371		}
372	| TYPE check_login SP type_code CRLF
373		{
374			if ($2) {
375				switch (cmd_type) {
376
377				case TYPE_A:
378					if (cmd_form == FORM_N) {
379						reply(200, "Type set to A.");
380						type = cmd_type;
381						form = cmd_form;
382					} else
383						reply(504, "Form must be N.");
384					break;
385
386				case TYPE_E:
387					reply(504, "Type E not implemented.");
388					break;
389
390				case TYPE_I:
391					reply(200, "Type set to I.");
392					type = cmd_type;
393					break;
394
395				case TYPE_L:
396#if NBBY == 8
397					if (cmd_bytesz == 8) {
398						reply(200,
399						    "Type set to L (byte size 8).");
400						type = cmd_type;
401					} else
402						reply(504, "Byte size must be 8.");
403#else /* NBBY == 8 */
404					UNIMPLEMENTED for NBBY != 8
405#endif /* NBBY == 8 */
406				}
407			}
408		}
409	| STRU check_login SP struct_code CRLF
410		{
411			if ($2) {
412				switch ($4) {
413
414				case STRU_F:
415					reply(200, "STRU F ok.");
416					break;
417
418				default:
419					reply(504, "Unimplemented STRU type.");
420				}
421			}
422		}
423	| MODE check_login SP mode_code CRLF
424		{
425			if ($2) {
426				switch ($4) {
427
428				case MODE_S:
429					reply(200, "MODE S ok.");
430					break;
431
432				default:
433					reply(502, "Unimplemented MODE type.");
434				}
435			}
436		}
437	| ALLO check_login SP NUMBER CRLF
438		{
439			if ($2) {
440				reply(202, "ALLO command ignored.");
441			}
442		}
443	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
444		{
445			if ($2) {
446				reply(202, "ALLO command ignored.");
447			}
448		}
449	| RETR check_login SP pathname CRLF
450		{
451			if (noretr || (guest && noguestretr))
452				reply(500, "RETR command is disabled");
453			else if ($2 && $4 != NULL)
454				retrieve((char *) 0, $4);
455
456			if ($4 != NULL)
457				free($4);
458		}
459	| STOR check_login_ro SP pathname CRLF
460		{
461			if ($2 && $4 != NULL)
462				store($4, "w", 0);
463			if ($4 != NULL)
464				free($4);
465		}
466	| APPE check_login_ro SP pathname CRLF
467		{
468			if ($2 && $4 != NULL)
469				store($4, "a", 0);
470			if ($4 != NULL)
471				free($4);
472		}
473	| NLST check_login CRLF
474		{
475			if ($2)
476				send_file_list(".");
477		}
478	| NLST check_login SP STRING CRLF
479		{
480			if ($2 && $4 != NULL)
481				send_file_list($4);
482			if ($4 != NULL)
483				free($4);
484		}
485	| LIST check_login CRLF
486		{
487			if ($2)
488				retrieve("/bin/ls -lgA", "");
489		}
490	| LIST check_login SP pathstring CRLF
491		{
492			if ($2 && $4 != NULL)
493				retrieve("/bin/ls -lgA %s", $4);
494			if ($4 != NULL)
495				free($4);
496		}
497	| STAT check_login SP pathname CRLF
498		{
499			if ($2 && $4 != NULL)
500				statfilecmd($4);
501			if ($4 != NULL)
502				free($4);
503		}
504	| STAT check_login CRLF
505		{
506			if ($2) {
507				statcmd();
508			}
509		}
510	| DELE check_login_ro SP pathname CRLF
511		{
512			if ($2 && $4 != NULL)
513				delete($4);
514			if ($4 != NULL)
515				free($4);
516		}
517	| RNTO check_login_ro SP pathname CRLF
518		{
519			if ($2) {
520				if (fromname) {
521					renamecmd(fromname, $4);
522					free(fromname);
523					fromname = (char *) 0;
524				} else {
525					reply(503, "Bad sequence of commands.");
526				}
527			}
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				if (guest)
539					cwd("/");
540				else
541					cwd(pw->pw_dir);
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, (char *) 0);
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, (char *) 0);
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, (char *) 0);
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) {
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((unsigned) 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#ifdef unix
694#ifdef BSD
695			reply(215, "UNIX Type: L%d Version: BSD-%d",
696				NBBY, BSD);
697#else /* BSD */
698			reply(215, "UNIX Type: L%d", NBBY);
699#endif /* BSD */
700#else /* unix */
701			reply(215, "UNKNOWN Type: L%d", NBBY);
702#endif /* unix */
703		}
704
705		/*
706		 * SIZE is not in RFC959, but Postel has blessed it and
707		 * it will be in the updated RFC.
708		 *
709		 * Return size of file in a format suitable for
710		 * using with RESTART (we just count bytes).
711		 */
712	| SIZE check_login SP pathname CRLF
713		{
714			if ($2 && $4 != NULL)
715				sizecmd($4);
716			if ($4 != NULL)
717				free($4);
718		}
719
720		/*
721		 * MDTM is not in RFC959, but Postel has blessed it and
722		 * it will be in the updated RFC.
723		 *
724		 * Return modification time of file as an ISO 3307
725		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
726		 * where xxx is the fractional second (of any precision,
727		 * not necessarily 3 digits)
728		 */
729	| MDTM check_login SP pathname CRLF
730		{
731			if ($2 && $4 != NULL) {
732				struct stat stbuf;
733				if (stat($4, &stbuf) < 0)
734					reply(550, "%s: %s",
735					    $4, strerror(errno));
736				else if (!S_ISREG(stbuf.st_mode)) {
737					reply(550, "%s: not a plain file.", $4);
738				} else {
739					struct tm *t;
740					t = gmtime(&stbuf.st_mtime);
741					reply(213,
742					    "%04d%02d%02d%02d%02d%02d",
743					    1900 + t->tm_year,
744					    t->tm_mon+1, t->tm_mday,
745					    t->tm_hour, t->tm_min, t->tm_sec);
746				}
747			}
748			if ($4 != NULL)
749				free($4);
750		}
751	| QUIT CRLF
752		{
753			reply(221, "Goodbye.");
754			dologout(0);
755		}
756	| error
757		{
758			yyclearin;		/* discard lookahead data */
759			yyerrok;		/* clear error condition */
760			state = 0;		/* reset lexer state */
761		}
762	;
763rcmd
764	: RNFR check_login_ro SP pathname CRLF
765		{
766			restart_point = (off_t) 0;
767			if ($2 && $4) {
768				if (fromname)
769					free(fromname);
770				fromname = (char *) 0;
771				if (renamefrom($4))
772					fromname = $4;
773				else
774					free($4);
775			} else if ($4) {
776				free($4);
777			}
778		}
779	| REST check_login SP NUMBER CRLF
780		{
781			if ($2) {
782				if (fromname)
783					free(fromname);
784				fromname = (char *) 0;
785				restart_point = $4.o;
786				reply(350, "Restarting at %llu. %s",
787				    restart_point,
788				    "Send STORE or RETRIEVE to initiate transfer.");
789			}
790		}
791	;
792
793username
794	: STRING
795	;
796
797password
798	: /* empty */
799		{
800			$$ = (char *)calloc(1, sizeof(char));
801		}
802	| STRING
803	;
804
805byte_size
806	: NUMBER
807		{
808			$$ = $1.i;
809		}
810	;
811
812host_port
813	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
814		NUMBER COMMA NUMBER
815		{
816			char *a, *p;
817
818			data_dest.su_len = sizeof(struct sockaddr_in);
819			data_dest.su_family = AF_INET;
820			p = (char *)&data_dest.su_sin.sin_port;
821			p[0] = $9.i; p[1] = $11.i;
822			a = (char *)&data_dest.su_sin.sin_addr;
823			a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
824		}
825	;
826
827host_long_port
828	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
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
834		{
835			char *a, *p;
836
837			memset(&data_dest, 0, sizeof(data_dest));
838			data_dest.su_len = sizeof(struct sockaddr_in6);
839			data_dest.su_family = AF_INET6;
840			p = (char *)&data_dest.su_port;
841			p[0] = $39.i; p[1] = $41.i;
842			a = (char *)&data_dest.su_sin6.sin6_addr;
843			a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
844			a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
845			a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
846			a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
847			if (his_addr.su_family == AF_INET6) {
848				/* XXX more sanity checks! */
849				data_dest.su_sin6.sin6_scope_id =
850					his_addr.su_sin6.sin6_scope_id;
851			}
852			if ($1.i != 6 || $3.i != 16 || $37.i != 2)
853				memset(&data_dest, 0, sizeof(data_dest));
854		}
855	| NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
856		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
857		NUMBER
858		{
859			char *a, *p;
860
861			memset(&data_dest, 0, sizeof(data_dest));
862			data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
863			data_dest.su_family = AF_INET;
864			p = (char *)&data_dest.su_port;
865			p[0] = $15.i; p[1] = $17.i;
866			a = (char *)&data_dest.su_sin.sin_addr;
867			a[0] =  $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
868			if ($1.i != 4 || $3.i != 4 || $13.i != 2)
869				memset(&data_dest, 0, sizeof(data_dest));
870		}
871	;
872
873form_code
874	: N
875		{
876			$$ = FORM_N;
877		}
878	| T
879		{
880			$$ = FORM_T;
881		}
882	| C
883		{
884			$$ = FORM_C;
885		}
886	;
887
888type_code
889	: A
890		{
891			cmd_type = TYPE_A;
892			cmd_form = FORM_N;
893		}
894	| A SP form_code
895		{
896			cmd_type = TYPE_A;
897			cmd_form = $3;
898		}
899	| E
900		{
901			cmd_type = TYPE_E;
902			cmd_form = FORM_N;
903		}
904	| E SP form_code
905		{
906			cmd_type = TYPE_E;
907			cmd_form = $3;
908		}
909	| I
910		{
911			cmd_type = TYPE_I;
912		}
913	| L
914		{
915			cmd_type = TYPE_L;
916			cmd_bytesz = NBBY;
917		}
918	| L SP byte_size
919		{
920			cmd_type = TYPE_L;
921			cmd_bytesz = $3;
922		}
923		/* this is for a bug in the BBN ftp */
924	| L byte_size
925		{
926			cmd_type = TYPE_L;
927			cmd_bytesz = $2;
928		}
929	;
930
931struct_code
932	: F
933		{
934			$$ = STRU_F;
935		}
936	| R
937		{
938			$$ = STRU_R;
939		}
940	| P
941		{
942			$$ = STRU_P;
943		}
944	;
945
946mode_code
947	: S
948		{
949			$$ = MODE_S;
950		}
951	| B
952		{
953			$$ = MODE_B;
954		}
955	| C
956		{
957			$$ = MODE_C;
958		}
959	;
960
961pathname
962	: pathstring
963		{
964			/*
965			 * Problem: this production is used for all pathname
966			 * processing, but only gives a 550 error reply.
967			 * This is a valid reply in some cases but not in others.
968			 */
969			if (logged_in && $1) {
970				glob_t gl;
971				int flags =
972				 GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
973
974				memset(&gl, 0, sizeof(gl));
975				flags |= GLOB_MAXPATH;
976				gl.gl_matchc = MAXGLOBARGS;
977				if (glob($1, flags, NULL, &gl) ||
978				    gl.gl_pathc == 0) {
979					reply(550, "not found");
980					$$ = NULL;
981				} else if (gl.gl_pathc > 1) {
982					reply(550, "ambiguous");
983					$$ = NULL;
984				} else {
985					$$ = strdup(gl.gl_pathv[0]);
986				}
987				globfree(&gl);
988				free($1);
989			} else
990				$$ = $1;
991		}
992	;
993
994pathstring
995	: STRING
996	;
997
998octal_number
999	: NUMBER
1000		{
1001			int ret, dec, multby, digit;
1002
1003			/*
1004			 * Convert a number that was read as decimal number
1005			 * to what it would be if it had been read as octal.
1006			 */
1007			dec = $1.i;
1008			multby = 1;
1009			ret = 0;
1010			while (dec) {
1011				digit = dec%10;
1012				if (digit > 7) {
1013					ret = -1;
1014					break;
1015				}
1016				ret += digit * multby;
1017				multby *= 8;
1018				dec /= 10;
1019			}
1020			$$ = ret;
1021		}
1022	;
1023
1024
1025check_login
1026	: /* empty */
1027		{
1028		$$ = check_login1();
1029		}
1030	;
1031
1032check_login_epsv
1033	: /* empty */
1034		{
1035		if (noepsv) {
1036			reply(500, "EPSV command disabled");
1037			$$ = 0;
1038		}
1039		else
1040			$$ = check_login1();
1041		}
1042	;
1043
1044check_login_ro
1045	: /* empty */
1046		{
1047		if (readonly) {
1048			reply(550, "Permission denied.");
1049			$$ = 0;
1050		}
1051		else
1052			$$ = check_login1();
1053		}
1054	;
1055
1056%%
1057
1058#define	CMD	0	/* beginning of command */
1059#define	ARGS	1	/* expect miscellaneous arguments */
1060#define	STR1	2	/* expect SP followed by STRING */
1061#define	STR2	3	/* expect STRING */
1062#define	OSTR	4	/* optional SP then STRING */
1063#define	ZSTR1	5	/* optional SP then optional STRING */
1064#define	ZSTR2	6	/* optional STRING after SP */
1065#define	SITECMD	7	/* SITE command */
1066#define	NSTR	8	/* Number followed by a string */
1067
1068#define	MAXGLOBARGS	1000
1069
1070#define	MAXASIZE	10240	/* Deny ASCII SIZE on files larger than that */
1071
1072struct tab {
1073	char	*name;
1074	short	token;
1075	short	state;
1076	short	implemented;	/* 1 if command is implemented */
1077	char	*help;
1078};
1079
1080struct tab cmdtab[] = {		/* In order defined in RFC 765 */
1081	{ "USER", USER, STR1, 1,	"<sp> username" },
1082	{ "PASS", PASS, ZSTR1, 1,	"[<sp> [password]]" },
1083	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
1084	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
1085	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
1086	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
1087	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4" },
1088	{ "LPRT", LPRT, ARGS, 1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1089	{ "EPRT", EPRT, STR1, 1,	"<sp> |af|addr|port|" },
1090	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
1091	{ "LPSV", LPSV, ARGS, 1,	"(set server in passive mode)" },
1092	{ "EPSV", EPSV, ARGS, 1,	"[<sp> af|ALL]" },
1093	{ "TYPE", TYPE, ARGS, 1,	"<sp> [ A | E | I | L ]" },
1094	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
1095	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
1096	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
1097	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
1098	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
1099	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
1100	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
1101	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
1102	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
1103	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
1104	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
1105	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
1106	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
1107	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
1108	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
1109	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
1110	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
1111	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
1112	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
1113	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
1114	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
1115	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
1116	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
1117	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
1118	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
1119	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
1120	{ "NOOP", NOOP, ARGS, 1,	"" },
1121	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
1122	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
1123	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
1124	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
1125	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
1126	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
1127	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
1128	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
1129	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
1130	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
1131	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
1132	{ NULL,   0,    0,    0,	0 }
1133};
1134
1135struct tab sitetab[] = {
1136	{ "MD5", MDFIVE, STR1, 1,	"[ <sp> file-name ]" },
1137	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
1138	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
1139	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
1140	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
1141	{ NULL,   0,    0,    0,	0 }
1142};
1143
1144static char	*copy(char *);
1145static void	 help(struct tab *, char *);
1146static struct tab *
1147		 lookup(struct tab *, char *);
1148static int	 port_check(const char *);
1149static int	 port_check_v6(const char *);
1150static void	 sizecmd(char *);
1151static void	 toolong(int);
1152static void	 v4map_data_dest(void);
1153static int	 yylex(void);
1154
1155static struct tab *
1156lookup(struct tab *p, char *cmd)
1157{
1158
1159	for (; p->name != NULL; p++)
1160		if (strcmp(cmd, p->name) == 0)
1161			return (p);
1162	return (0);
1163}
1164
1165#include <arpa/telnet.h>
1166
1167/*
1168 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1169 */
1170char *
1171getline(char *s, int n, FILE *iop)
1172{
1173	int c;
1174	register char *cs;
1175
1176	cs = s;
1177/* tmpline may contain saved command from urgent mode interruption */
1178	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1179		*cs++ = tmpline[c];
1180		if (tmpline[c] == '\n') {
1181			*cs++ = '\0';
1182			if (ftpdebug)
1183				syslog(LOG_DEBUG, "command: %s", s);
1184			tmpline[0] = '\0';
1185			return(s);
1186		}
1187		if (c == 0)
1188			tmpline[0] = '\0';
1189	}
1190	while ((c = getc(iop)) != EOF) {
1191		c &= 0377;
1192		if (c == IAC) {
1193		    if ((c = getc(iop)) != EOF) {
1194			c &= 0377;
1195			switch (c) {
1196			case WILL:
1197			case WONT:
1198				c = getc(iop);
1199				printf("%c%c%c", IAC, DONT, 0377&c);
1200				(void) fflush(stdout);
1201				continue;
1202			case DO:
1203			case DONT:
1204				c = getc(iop);
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		}
1215		*cs++ = c;
1216		if (--n <= 0 || c == '\n')
1217			break;
1218	}
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				if (p->implemented == 0) {
1294					nack(p->name);
1295					return (LEXERR);
1296				}
1297				state = p->state;
1298				yylval.s = p->name;
1299				return (p->token);
1300			}
1301			break;
1302
1303		case SITECMD:
1304			if (cbuf[cpos] == ' ') {
1305				cpos++;
1306				return (SP);
1307			}
1308			cp = &cbuf[cpos];
1309			if ((cp2 = strpbrk(cp, " \n")))
1310				cpos = cp2 - cbuf;
1311			c = cbuf[cpos];
1312			cbuf[cpos] = '\0';
1313			upper(cp);
1314			p = lookup(sitetab, cp);
1315			cbuf[cpos] = c;
1316			if (guest == 0 && p != 0) {
1317				if (p->implemented == 0) {
1318					state = CMD;
1319					nack(p->name);
1320					return (LEXERR);
1321				}
1322				state = p->state;
1323				yylval.s = p->name;
1324				return (p->token);
1325			}
1326			state = CMD;
1327			break;
1328
1329		case ZSTR1:
1330		case OSTR:
1331			if (cbuf[cpos] == '\n') {
1332				state = CMD;
1333				return (CRLF);
1334			}
1335			/* FALLTHROUGH */
1336
1337		case STR1:
1338		dostr1:
1339			if (cbuf[cpos] == ' ') {
1340				cpos++;
1341				state = state == OSTR ? STR2 : state+1;
1342				return (SP);
1343			}
1344			break;
1345
1346		case ZSTR2:
1347			if (cbuf[cpos] == '\n') {
1348				state = CMD;
1349				return (CRLF);
1350			}
1351			/* FALLTHROUGH */
1352
1353		case STR2:
1354			cp = &cbuf[cpos];
1355			n = strlen(cp);
1356			cpos += n - 1;
1357			/*
1358			 * Make sure the string is nonempty and \n terminated.
1359			 */
1360			if (n > 1 && cbuf[cpos] == '\n') {
1361				cbuf[cpos] = '\0';
1362				yylval.s = copy(cp);
1363				cbuf[cpos] = '\n';
1364				state = ARGS;
1365				return (STRING);
1366			}
1367			break;
1368
1369		case NSTR:
1370			if (cbuf[cpos] == ' ') {
1371				cpos++;
1372				return (SP);
1373			}
1374			if (isdigit(cbuf[cpos])) {
1375				cp = &cbuf[cpos];
1376				while (isdigit(cbuf[++cpos]))
1377					;
1378				c = cbuf[cpos];
1379				cbuf[cpos] = '\0';
1380				yylval.u.i = atoi(cp);
1381				cbuf[cpos] = c;
1382				state = STR1;
1383				return (NUMBER);
1384			}
1385			state = STR1;
1386			goto dostr1;
1387
1388		case ARGS:
1389			if (isdigit(cbuf[cpos])) {
1390				cp = &cbuf[cpos];
1391				while (isdigit(cbuf[++cpos]))
1392					;
1393				c = cbuf[cpos];
1394				cbuf[cpos] = '\0';
1395				yylval.u.i = atoi(cp);
1396				yylval.u.o = strtoull(cp, (char **)NULL, 10);
1397				cbuf[cpos] = c;
1398				return (NUMBER);
1399			}
1400			if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1401			 && !isalnum(cbuf[cpos + 3])) {
1402				cpos += 3;
1403				return ALL;
1404			}
1405			switch (cbuf[cpos++]) {
1406
1407			case '\n':
1408				state = CMD;
1409				return (CRLF);
1410
1411			case ' ':
1412				return (SP);
1413
1414			case ',':
1415				return (COMMA);
1416
1417			case 'A':
1418			case 'a':
1419				return (A);
1420
1421			case 'B':
1422			case 'b':
1423				return (B);
1424
1425			case 'C':
1426			case 'c':
1427				return (C);
1428
1429			case 'E':
1430			case 'e':
1431				return (E);
1432
1433			case 'F':
1434			case 'f':
1435				return (F);
1436
1437			case 'I':
1438			case 'i':
1439				return (I);
1440
1441			case 'L':
1442			case 'l':
1443				return (L);
1444
1445			case 'N':
1446			case 'n':
1447				return (N);
1448
1449			case 'P':
1450			case 'p':
1451				return (P);
1452
1453			case 'R':
1454			case 'r':
1455				return (R);
1456
1457			case 'S':
1458			case 's':
1459				return (S);
1460
1461			case 'T':
1462			case 't':
1463				return (T);
1464
1465			}
1466			break;
1467
1468		default:
1469			fatalerror("Unknown state in scanner.");
1470		}
1471		state = CMD;
1472		return (LEXERR);
1473	}
1474}
1475
1476void
1477upper(char *s)
1478{
1479	while (*s != '\0') {
1480		if (islower(*s))
1481			*s = toupper(*s);
1482		s++;
1483	}
1484}
1485
1486static char *
1487copy(char *s)
1488{
1489	char *p;
1490
1491	p = malloc((unsigned) strlen(s) + 1);
1492	if (p == NULL)
1493		fatalerror("Ran out of memory.");
1494	(void) strcpy(p, s);
1495	return (p);
1496}
1497
1498static void
1499help(struct tab *ctab, char *s)
1500{
1501	struct tab *c;
1502	int width, NCMDS;
1503	char *type;
1504
1505	if (ctab == sitetab)
1506		type = "SITE ";
1507	else
1508		type = "";
1509	width = 0, NCMDS = 0;
1510	for (c = ctab; c->name != NULL; c++) {
1511		int len = strlen(c->name);
1512
1513		if (len > width)
1514			width = len;
1515		NCMDS++;
1516	}
1517	width = (width + 8) &~ 7;
1518	if (s == 0) {
1519		int i, j, w;
1520		int columns, lines;
1521
1522		lreply(214, "The following %scommands are recognized %s.",
1523		    type, "(* =>'s unimplemented)");
1524		columns = 76 / width;
1525		if (columns == 0)
1526			columns = 1;
1527		lines = (NCMDS + columns - 1) / columns;
1528		for (i = 0; i < lines; i++) {
1529			printf("   ");
1530			for (j = 0; j < columns; j++) {
1531				c = ctab + j * lines + i;
1532				printf("%s%c", c->name,
1533					c->implemented ? ' ' : '*');
1534				if (c + lines >= &ctab[NCMDS])
1535					break;
1536				w = strlen(c->name) + 1;
1537				while (w < width) {
1538					putchar(' ');
1539					w++;
1540				}
1541			}
1542			printf("\r\n");
1543		}
1544		(void) fflush(stdout);
1545		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
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#ifdef INET6
1657/* Return 1, if port check is done. Return 0, if not yet. */
1658static int
1659port_check_v6(const char *pcmd)
1660{
1661	if (his_addr.su_family == AF_INET6) {
1662		if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1663			/* Convert data_dest into v4 mapped sockaddr.*/
1664			v4map_data_dest();
1665		if (data_dest.su_family != AF_INET6) {
1666			usedefault = 1;
1667			reply(500, "Invalid address rejected.");
1668			return 1;
1669		}
1670		if (paranoid &&
1671		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1672		     memcmp(&data_dest.su_sin6.sin6_addr,
1673			    &his_addr.su_sin6.sin6_addr,
1674			    sizeof(data_dest.su_sin6.sin6_addr)))) {
1675			usedefault = 1;
1676			reply(500, "Illegal PORT range rejected.");
1677		} else {
1678			usedefault = 0;
1679			if (pdata >= 0) {
1680				(void) close(pdata);
1681				pdata = -1;
1682			}
1683			reply(200, "%s command successful.", pcmd);
1684		}
1685		return 1;
1686	}
1687	return 0;
1688}
1689
1690static void
1691v4map_data_dest(void)
1692{
1693	struct in_addr savedaddr;
1694	int savedport;
1695
1696	if (data_dest.su_family != AF_INET) {
1697		usedefault = 1;
1698		reply(500, "Invalid address rejected.");
1699		return;
1700	}
1701
1702	savedaddr = data_dest.su_sin.sin_addr;
1703	savedport = data_dest.su_port;
1704
1705	memset(&data_dest, 0, sizeof(data_dest));
1706	data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1707	data_dest.su_sin6.sin6_family = AF_INET6;
1708	data_dest.su_sin6.sin6_port = savedport;
1709	memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1710	memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1711	       (caddr_t)&savedaddr, sizeof(savedaddr));
1712}
1713#endif
1714