ftpcmd.y revision 109380
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 109380 2003-01-16 13:27:58Z 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 logged_in;
79extern	struct passwd *pw;
80extern	int guest;
81extern 	int paranoid;
82extern	int logging;
83extern	int type;
84extern	int form;
85extern	int ftpdebug;
86extern	int timeout;
87extern	int maxtimeout;
88extern  int pdata;
89extern	char *hostname;
90extern	char remotehost[];
91extern	char proctitle[];
92extern	int usedefault;
93extern  int transflag;
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				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 && $6) {
613				q = MD5File($6, p);
614				if (q != NULL)
615					reply(200, "MD5(%s) = %s", $6, p);
616				else
617					perror_reply(550, $6);
618			}
619			if ($6)
620				free($6);
621		}
622	| SITE SP UMASK check_login CRLF
623		{
624			int oldmask;
625
626			if ($4) {
627				oldmask = umask(0);
628				(void) umask(oldmask);
629				reply(200, "Current UMASK is %03o", oldmask);
630			}
631		}
632	| SITE SP UMASK check_login SP octal_number CRLF
633		{
634			int oldmask;
635
636			if ($4) {
637				if (($6 == -1) || ($6 > 0777)) {
638					reply(501, "Bad UMASK value");
639				} else {
640					oldmask = umask($6);
641					reply(200,
642					    "UMASK set to %03o (was %03o)",
643					    $6, oldmask);
644				}
645			}
646		}
647	| SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
648		{
649			if ($4 && ($8 != NULL)) {
650				if (($6 == -1 ) || ($6 > 0777))
651					reply(501, "Bad mode value");
652				else if (chmod($8, $6) < 0)
653					perror_reply(550, $8);
654				else
655					reply(200, "CHMOD command successful.");
656			}
657			if ($8 != NULL)
658				free($8);
659		}
660	| SITE SP check_login IDLE CRLF
661		{
662			if ($3)
663				reply(200,
664			    	    "Current IDLE time limit is %d seconds; max %d",
665				    timeout, maxtimeout);
666		}
667	| SITE SP check_login IDLE SP NUMBER CRLF
668		{
669			if ($3) {
670				if ($6.i < 30 || $6.i > maxtimeout) {
671					reply(501,
672					    "Maximum IDLE time must be between 30 and %d seconds",
673					    maxtimeout);
674				} else {
675					timeout = $6.i;
676					(void) alarm((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				CHAR_BIT, BSD);
697#else /* BSD */
698			reply(215, "UNIX Type: L%d", CHAR_BIT);
699#endif /* BSD */
700#else /* unix */
701			reply(215, "UNKNOWN Type: L%d", CHAR_BIT);
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	| NOTIMPL
757		{
758			nack($1);
759		}
760	| error
761		{
762			yyclearin;		/* discard lookahead data */
763			yyerrok;		/* clear error condition */
764			state = CMD;		/* reset lexer state */
765		}
766	;
767rcmd
768	: RNFR check_login_ro SP pathname CRLF
769		{
770			restart_point = (off_t) 0;
771			if ($2 && $4) {
772				if (fromname)
773					free(fromname);
774				fromname = (char *) 0;
775				if (renamefrom($4))
776					fromname = $4;
777				else
778					free($4);
779			} else if ($4) {
780				free($4);
781			}
782		}
783	| REST check_login SP NUMBER CRLF
784		{
785			if ($2) {
786				if (fromname)
787					free(fromname);
788				fromname = (char *) 0;
789				restart_point = $4.o;
790				reply(350, "Restarting at %llu. %s",
791				    restart_point,
792				    "Send STORE or RETRIEVE to initiate transfer.");
793			}
794		}
795	;
796
797username
798	: STRING
799	;
800
801password
802	: /* empty */
803		{
804			$$ = (char *)calloc(1, sizeof(char));
805		}
806	| STRING
807	;
808
809byte_size
810	: NUMBER
811		{
812			$$ = $1.i;
813		}
814	;
815
816host_port
817	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
818		NUMBER COMMA NUMBER
819		{
820			char *a, *p;
821
822			data_dest.su_len = sizeof(struct sockaddr_in);
823			data_dest.su_family = AF_INET;
824			p = (char *)&data_dest.su_sin.sin_port;
825			p[0] = $9.i; p[1] = $11.i;
826			a = (char *)&data_dest.su_sin.sin_addr;
827			a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
828		}
829	;
830
831host_long_port
832	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
833		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
834		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
835		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
836		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
837		NUMBER
838		{
839			char *a, *p;
840
841			memset(&data_dest, 0, sizeof(data_dest));
842			data_dest.su_len = sizeof(struct sockaddr_in6);
843			data_dest.su_family = AF_INET6;
844			p = (char *)&data_dest.su_port;
845			p[0] = $39.i; p[1] = $41.i;
846			a = (char *)&data_dest.su_sin6.sin6_addr;
847			a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
848			a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
849			a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
850			a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
851			if (his_addr.su_family == AF_INET6) {
852				/* XXX more sanity checks! */
853				data_dest.su_sin6.sin6_scope_id =
854					his_addr.su_sin6.sin6_scope_id;
855			}
856			if ($1.i != 6 || $3.i != 16 || $37.i != 2)
857				memset(&data_dest, 0, sizeof(data_dest));
858		}
859	| NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
860		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
861		NUMBER
862		{
863			char *a, *p;
864
865			memset(&data_dest, 0, sizeof(data_dest));
866			data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
867			data_dest.su_family = AF_INET;
868			p = (char *)&data_dest.su_port;
869			p[0] = $15.i; p[1] = $17.i;
870			a = (char *)&data_dest.su_sin.sin_addr;
871			a[0] =  $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
872			if ($1.i != 4 || $3.i != 4 || $13.i != 2)
873				memset(&data_dest, 0, sizeof(data_dest));
874		}
875	;
876
877form_code
878	: N
879		{
880			$$ = FORM_N;
881		}
882	| T
883		{
884			$$ = FORM_T;
885		}
886	| C
887		{
888			$$ = FORM_C;
889		}
890	;
891
892type_code
893	: A
894		{
895			cmd_type = TYPE_A;
896			cmd_form = FORM_N;
897		}
898	| A SP form_code
899		{
900			cmd_type = TYPE_A;
901			cmd_form = $3;
902		}
903	| E
904		{
905			cmd_type = TYPE_E;
906			cmd_form = FORM_N;
907		}
908	| E SP form_code
909		{
910			cmd_type = TYPE_E;
911			cmd_form = $3;
912		}
913	| I
914		{
915			cmd_type = TYPE_I;
916		}
917	| L
918		{
919			cmd_type = TYPE_L;
920			cmd_bytesz = CHAR_BIT;
921		}
922	| L SP byte_size
923		{
924			cmd_type = TYPE_L;
925			cmd_bytesz = $3;
926		}
927		/* this is for a bug in the BBN ftp */
928	| L byte_size
929		{
930			cmd_type = TYPE_L;
931			cmd_bytesz = $2;
932		}
933	;
934
935struct_code
936	: F
937		{
938			$$ = STRU_F;
939		}
940	| R
941		{
942			$$ = STRU_R;
943		}
944	| P
945		{
946			$$ = STRU_P;
947		}
948	;
949
950mode_code
951	: S
952		{
953			$$ = MODE_S;
954		}
955	| B
956		{
957			$$ = MODE_B;
958		}
959	| C
960		{
961			$$ = MODE_C;
962		}
963	;
964
965pathname
966	: pathstring
967		{
968			/*
969			 * Problem: this production is used for all pathname
970			 * processing, but only gives a 550 error reply.
971			 * This is a valid reply in some cases but not in others.
972			 */
973			if (logged_in && $1) {
974				glob_t gl;
975				int flags =
976				 GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
977
978				memset(&gl, 0, sizeof(gl));
979				flags |= GLOB_MAXPATH;
980				gl.gl_matchc = MAXGLOBARGS;
981				if (glob($1, flags, NULL, &gl) ||
982				    gl.gl_pathc == 0) {
983					reply(550, "wildcard expansion error");
984					$$ = NULL;
985				} else if (gl.gl_pathc > 1) {
986					reply(550, "ambiguous");
987					$$ = NULL;
988				} else {
989					$$ = strdup(gl.gl_pathv[0]);
990				}
991				globfree(&gl);
992				free($1);
993			} else
994				$$ = $1;
995		}
996	;
997
998pathstring
999	: STRING
1000	;
1001
1002octal_number
1003	: NUMBER
1004		{
1005			int ret, dec, multby, digit;
1006
1007			/*
1008			 * Convert a number that was read as decimal number
1009			 * to what it would be if it had been read as octal.
1010			 */
1011			dec = $1.i;
1012			multby = 1;
1013			ret = 0;
1014			while (dec) {
1015				digit = dec%10;
1016				if (digit > 7) {
1017					ret = -1;
1018					break;
1019				}
1020				ret += digit * multby;
1021				multby *= 8;
1022				dec /= 10;
1023			}
1024			$$ = ret;
1025		}
1026	;
1027
1028
1029check_login
1030	: /* empty */
1031		{
1032		$$ = check_login1();
1033		}
1034	;
1035
1036check_login_epsv
1037	: /* empty */
1038		{
1039		if (noepsv) {
1040			reply(500, "EPSV command disabled");
1041			$$ = 0;
1042		}
1043		else
1044			$$ = check_login1();
1045		}
1046	;
1047
1048check_login_ro
1049	: /* empty */
1050		{
1051		if (readonly) {
1052			reply(550, "Permission denied.");
1053			$$ = 0;
1054		}
1055		else
1056			$$ = check_login1();
1057		}
1058	;
1059
1060%%
1061
1062#define	CMD	0	/* beginning of command */
1063#define	ARGS	1	/* expect miscellaneous arguments */
1064#define	STR1	2	/* expect SP followed by STRING */
1065#define	STR2	3	/* expect STRING */
1066#define	OSTR	4	/* optional SP then STRING */
1067#define	ZSTR1	5	/* optional SP then optional STRING */
1068#define	ZSTR2	6	/* optional STRING after SP */
1069#define	SITECMD	7	/* SITE command */
1070#define	NSTR	8	/* Number followed by a string */
1071
1072#define	MAXGLOBARGS	1000
1073
1074#define	MAXASIZE	10240	/* Deny ASCII SIZE on files larger than that */
1075
1076struct tab {
1077	char	*name;
1078	short	token;
1079	short	state;
1080	short	implemented;	/* 1 if command is implemented */
1081	char	*help;
1082};
1083
1084struct tab cmdtab[] = {		/* In order defined in RFC 765 */
1085	{ "USER", USER, STR1, 1,	"<sp> username" },
1086	{ "PASS", PASS, ZSTR1, 1,	"[<sp> [password]]" },
1087	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
1088	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
1089	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
1090	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
1091	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4, b5" },
1092	{ "LPRT", LPRT, ARGS, 1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1093	{ "EPRT", EPRT, STR1, 1,	"<sp> |af|addr|port|" },
1094	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
1095	{ "LPSV", LPSV, ARGS, 1,	"(set server in passive mode)" },
1096	{ "EPSV", EPSV, ARGS, 1,	"[<sp> af|ALL]" },
1097	{ "TYPE", TYPE, ARGS, 1,	"<sp> { A | E | I | L }" },
1098	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
1099	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
1100	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
1101	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
1102	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
1103	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
1104	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
1105	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
1106	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
1107	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
1108	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
1109	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
1110	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
1111	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
1112	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
1113	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
1114	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
1115	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
1116	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
1117	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
1118	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
1119	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
1120	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
1121	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
1122	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
1123	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
1124	{ "NOOP", NOOP, ARGS, 1,	"" },
1125	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
1126	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
1127	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
1128	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
1129	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
1130	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
1131	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
1132	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
1133	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
1134	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
1135	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
1136	{ NULL,   0,    0,    0,	0 }
1137};
1138
1139struct tab sitetab[] = {
1140	{ "MD5", MDFIVE, STR1, 1,	"[ <sp> file-name ]" },
1141	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
1142	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
1143	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
1144	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
1145	{ NULL,   0,    0,    0,	0 }
1146};
1147
1148static char	*copy(char *);
1149static void	 help(struct tab *, char *);
1150static struct tab *
1151		 lookup(struct tab *, char *);
1152static int	 port_check(const char *);
1153static int	 port_check_v6(const char *);
1154static void	 sizecmd(char *);
1155static void	 toolong(int);
1156static void	 v4map_data_dest(void);
1157static int	 yylex(void);
1158
1159static struct tab *
1160lookup(struct tab *p, char *cmd)
1161{
1162
1163	for (; p->name != NULL; p++)
1164		if (strcmp(cmd, p->name) == 0)
1165			return (p);
1166	return (0);
1167}
1168
1169#include <arpa/telnet.h>
1170
1171/*
1172 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1173 */
1174char *
1175getline(char *s, int n, FILE *iop)
1176{
1177	int c;
1178	register char *cs;
1179
1180	cs = s;
1181/* tmpline may contain saved command from urgent mode interruption */
1182	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1183		*cs++ = tmpline[c];
1184		if (tmpline[c] == '\n') {
1185			*cs++ = '\0';
1186			if (ftpdebug)
1187				syslog(LOG_DEBUG, "command: %s", s);
1188			tmpline[0] = '\0';
1189			return(s);
1190		}
1191		if (c == 0)
1192			tmpline[0] = '\0';
1193	}
1194	while ((c = getc(iop)) != EOF) {
1195		c &= 0377;
1196		if (c == IAC) {
1197		    if ((c = getc(iop)) != EOF) {
1198			c &= 0377;
1199			switch (c) {
1200			case WILL:
1201			case WONT:
1202				c = getc(iop);
1203				printf("%c%c%c", IAC, DONT, 0377&c);
1204				(void) fflush(stdout);
1205				continue;
1206			case DO:
1207			case DONT:
1208				c = getc(iop);
1209				printf("%c%c%c", IAC, WONT, 0377&c);
1210				(void) fflush(stdout);
1211				continue;
1212			case IAC:
1213				break;
1214			default:
1215				continue;	/* ignore command */
1216			}
1217		    }
1218		}
1219		*cs++ = c;
1220		if (--n <= 0 || c == '\n')
1221			break;
1222	}
1223	if (c == EOF && cs == s)
1224		return (NULL);
1225	*cs++ = '\0';
1226	if (ftpdebug) {
1227		if (!guest && strncasecmp("pass ", s, 5) == 0) {
1228			/* Don't syslog passwords */
1229			syslog(LOG_DEBUG, "command: %.5s ???", s);
1230		} else {
1231			register char *cp;
1232			register int len;
1233
1234			/* Don't syslog trailing CR-LF */
1235			len = strlen(s);
1236			cp = s + len - 1;
1237			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1238				--cp;
1239				--len;
1240			}
1241			syslog(LOG_DEBUG, "command: %.*s", len, s);
1242		}
1243	}
1244	return (s);
1245}
1246
1247static void
1248toolong(int signo)
1249{
1250
1251	reply(421,
1252	    "Timeout (%d seconds): closing control connection.", timeout);
1253	if (logging)
1254		syslog(LOG_INFO, "User %s timed out after %d seconds",
1255		    (pw ? pw -> pw_name : "unknown"), timeout);
1256	dologout(1);
1257}
1258
1259static int
1260yylex(void)
1261{
1262	static int cpos;
1263	char *cp, *cp2;
1264	struct tab *p;
1265	int n;
1266	char c;
1267
1268	for (;;) {
1269		switch (state) {
1270
1271		case CMD:
1272			(void) signal(SIGALRM, toolong);
1273			(void) alarm((unsigned) timeout);
1274			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1275				reply(221, "You could at least say goodbye.");
1276				dologout(0);
1277			}
1278			(void) alarm(0);
1279#ifdef SETPROCTITLE
1280			if (strncasecmp(cbuf, "PASS", 4) != 0)
1281				setproctitle("%s: %s", proctitle, cbuf);
1282#endif /* SETPROCTITLE */
1283			if ((cp = strchr(cbuf, '\r'))) {
1284				*cp++ = '\n';
1285				*cp = '\0';
1286			}
1287			if ((cp = strpbrk(cbuf, " \n")))
1288				cpos = cp - cbuf;
1289			if (cpos == 0)
1290				cpos = 4;
1291			c = cbuf[cpos];
1292			cbuf[cpos] = '\0';
1293			upper(cbuf);
1294			p = lookup(cmdtab, cbuf);
1295			cbuf[cpos] = c;
1296			if (p != 0) {
1297				yylval.s = p->name;
1298				if (!p->implemented)
1299					return (NOTIMPL); /* state remains CMD */
1300				state = p->state;
1301				return (p->token);
1302			}
1303			break;
1304
1305		case SITECMD:
1306			if (cbuf[cpos] == ' ') {
1307				cpos++;
1308				return (SP);
1309			}
1310			cp = &cbuf[cpos];
1311			if ((cp2 = strpbrk(cp, " \n")))
1312				cpos = cp2 - cbuf;
1313			c = cbuf[cpos];
1314			cbuf[cpos] = '\0';
1315			upper(cp);
1316			p = lookup(sitetab, cp);
1317			cbuf[cpos] = c;
1318			if (guest == 0 && p != 0) {
1319				yylval.s = p->name;
1320				if (!p->implemented) {
1321					state = CMD;
1322					return (NOTIMPL);
1323				}
1324				state = p->state;
1325				return (p->token);
1326			}
1327			state = CMD;
1328			break;
1329
1330		case ZSTR1:
1331		case OSTR:
1332			if (cbuf[cpos] == '\n') {
1333				state = CMD;
1334				return (CRLF);
1335			}
1336			/* FALLTHROUGH */
1337
1338		case STR1:
1339		dostr1:
1340			if (cbuf[cpos] == ' ') {
1341				cpos++;
1342				state = state == OSTR ? STR2 : state+1;
1343				return (SP);
1344			}
1345			break;
1346
1347		case ZSTR2:
1348			if (cbuf[cpos] == '\n') {
1349				state = CMD;
1350				return (CRLF);
1351			}
1352			/* FALLTHROUGH */
1353
1354		case STR2:
1355			cp = &cbuf[cpos];
1356			n = strlen(cp);
1357			cpos += n - 1;
1358			/*
1359			 * Make sure the string is nonempty and \n terminated.
1360			 */
1361			if (n > 1 && cbuf[cpos] == '\n') {
1362				cbuf[cpos] = '\0';
1363				yylval.s = copy(cp);
1364				cbuf[cpos] = '\n';
1365				state = ARGS;
1366				return (STRING);
1367			}
1368			break;
1369
1370		case NSTR:
1371			if (cbuf[cpos] == ' ') {
1372				cpos++;
1373				return (SP);
1374			}
1375			if (isdigit(cbuf[cpos])) {
1376				cp = &cbuf[cpos];
1377				while (isdigit(cbuf[++cpos]))
1378					;
1379				c = cbuf[cpos];
1380				cbuf[cpos] = '\0';
1381				yylval.u.i = atoi(cp);
1382				cbuf[cpos] = c;
1383				state = STR1;
1384				return (NUMBER);
1385			}
1386			state = STR1;
1387			goto dostr1;
1388
1389		case ARGS:
1390			if (isdigit(cbuf[cpos])) {
1391				cp = &cbuf[cpos];
1392				while (isdigit(cbuf[++cpos]))
1393					;
1394				c = cbuf[cpos];
1395				cbuf[cpos] = '\0';
1396				yylval.u.i = atoi(cp);
1397				yylval.u.o = strtoull(cp, (char **)NULL, 10);
1398				cbuf[cpos] = c;
1399				return (NUMBER);
1400			}
1401			if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1402			 && !isalnum(cbuf[cpos + 3])) {
1403				cpos += 3;
1404				return ALL;
1405			}
1406			switch (cbuf[cpos++]) {
1407
1408			case '\n':
1409				state = CMD;
1410				return (CRLF);
1411
1412			case ' ':
1413				return (SP);
1414
1415			case ',':
1416				return (COMMA);
1417
1418			case 'A':
1419			case 'a':
1420				return (A);
1421
1422			case 'B':
1423			case 'b':
1424				return (B);
1425
1426			case 'C':
1427			case 'c':
1428				return (C);
1429
1430			case 'E':
1431			case 'e':
1432				return (E);
1433
1434			case 'F':
1435			case 'f':
1436				return (F);
1437
1438			case 'I':
1439			case 'i':
1440				return (I);
1441
1442			case 'L':
1443			case 'l':
1444				return (L);
1445
1446			case 'N':
1447			case 'n':
1448				return (N);
1449
1450			case 'P':
1451			case 'p':
1452				return (P);
1453
1454			case 'R':
1455			case 'r':
1456				return (R);
1457
1458			case 'S':
1459			case 's':
1460				return (S);
1461
1462			case 'T':
1463			case 't':
1464				return (T);
1465
1466			}
1467			break;
1468
1469		default:
1470			fatalerror("Unknown state in scanner.");
1471		}
1472		state = CMD;
1473		return (LEXERR);
1474	}
1475}
1476
1477void
1478upper(char *s)
1479{
1480	while (*s != '\0') {
1481		if (islower(*s))
1482			*s = toupper(*s);
1483		s++;
1484	}
1485}
1486
1487static char *
1488copy(char *s)
1489{
1490	char *p;
1491
1492	p = malloc((unsigned) strlen(s) + 1);
1493	if (p == NULL)
1494		fatalerror("Ran out of memory.");
1495	(void) strcpy(p, s);
1496	return (p);
1497}
1498
1499static void
1500help(struct tab *ctab, char *s)
1501{
1502	struct tab *c;
1503	int width, NCMDS;
1504	char *type;
1505
1506	if (ctab == sitetab)
1507		type = "SITE ";
1508	else
1509		type = "";
1510	width = 0, NCMDS = 0;
1511	for (c = ctab; c->name != NULL; c++) {
1512		int len = strlen(c->name);
1513
1514		if (len > width)
1515			width = len;
1516		NCMDS++;
1517	}
1518	width = (width + 8) &~ 7;
1519	if (s == 0) {
1520		int i, j, w;
1521		int columns, lines;
1522
1523		lreply(214, "The following %scommands are recognized %s.",
1524		    type, "(* =>'s unimplemented)");
1525		columns = 76 / width;
1526		if (columns == 0)
1527			columns = 1;
1528		lines = (NCMDS + columns - 1) / columns;
1529		for (i = 0; i < lines; i++) {
1530			printf("   ");
1531			for (j = 0; j < columns; j++) {
1532				c = ctab + j * lines + i;
1533				printf("%s%c", c->name,
1534					c->implemented ? ' ' : '*');
1535				if (c + lines >= &ctab[NCMDS])
1536					break;
1537				w = strlen(c->name) + 1;
1538				while (w < width) {
1539					putchar(' ');
1540					w++;
1541				}
1542			}
1543			printf("\r\n");
1544		}
1545		(void) fflush(stdout);
1546		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1547		return;
1548	}
1549	upper(s);
1550	c = lookup(ctab, s);
1551	if (c == (struct tab *)0) {
1552		reply(502, "Unknown command %s.", s);
1553		return;
1554	}
1555	if (c->implemented)
1556		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1557	else
1558		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1559		    c->name, c->help);
1560}
1561
1562static void
1563sizecmd(char *filename)
1564{
1565	switch (type) {
1566	case TYPE_L:
1567	case TYPE_I: {
1568		struct stat stbuf;
1569		if (stat(filename, &stbuf) < 0)
1570			perror_reply(550, filename);
1571		else if (!S_ISREG(stbuf.st_mode))
1572			reply(550, "%s: not a plain file.", filename);
1573		else
1574			reply(213, "%qu", stbuf.st_size);
1575		break; }
1576	case TYPE_A: {
1577		FILE *fin;
1578		int c;
1579		off_t count;
1580		struct stat stbuf;
1581		fin = fopen(filename, "r");
1582		if (fin == NULL) {
1583			perror_reply(550, filename);
1584			return;
1585		}
1586		if (fstat(fileno(fin), &stbuf) < 0) {
1587			perror_reply(550, filename);
1588			(void) fclose(fin);
1589			return;
1590		} else if (!S_ISREG(stbuf.st_mode)) {
1591			reply(550, "%s: not a plain file.", filename);
1592			(void) fclose(fin);
1593			return;
1594		} else if (stbuf.st_size > MAXASIZE) {
1595			reply(550, "%s: too large for type A SIZE.", filename);
1596			(void) fclose(fin);
1597			return;
1598		}
1599
1600		count = 0;
1601		while((c=getc(fin)) != EOF) {
1602			if (c == '\n')	/* will get expanded to \r\n */
1603				count++;
1604			count++;
1605		}
1606		(void) fclose(fin);
1607
1608		reply(213, "%qd", count);
1609		break; }
1610	default:
1611		reply(504, "SIZE not implemented for type %s.",
1612		           typenames[type]);
1613	}
1614}
1615
1616/* Return 1, if port check is done. Return 0, if not yet. */
1617static int
1618port_check(const char *pcmd)
1619{
1620	if (his_addr.su_family == AF_INET) {
1621		if (data_dest.su_family != AF_INET) {
1622			usedefault = 1;
1623			reply(500, "Invalid address rejected.");
1624			return 1;
1625		}
1626		if (paranoid &&
1627		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1628		     memcmp(&data_dest.su_sin.sin_addr,
1629			    &his_addr.su_sin.sin_addr,
1630			    sizeof(data_dest.su_sin.sin_addr)))) {
1631			usedefault = 1;
1632			reply(500, "Illegal PORT range rejected.");
1633		} else {
1634			usedefault = 0;
1635			if (pdata >= 0) {
1636				(void) close(pdata);
1637				pdata = -1;
1638			}
1639			reply(200, "%s command successful.", pcmd);
1640		}
1641		return 1;
1642	}
1643	return 0;
1644}
1645
1646static int
1647check_login1(void)
1648{
1649	if (logged_in)
1650		return 1;
1651	else {
1652		reply(530, "Please login with USER and PASS.");
1653		return 0;
1654	}
1655}
1656
1657#ifdef INET6
1658/* Return 1, if port check is done. Return 0, if not yet. */
1659static int
1660port_check_v6(const char *pcmd)
1661{
1662	if (his_addr.su_family == AF_INET6) {
1663		if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1664			/* Convert data_dest into v4 mapped sockaddr.*/
1665			v4map_data_dest();
1666		if (data_dest.su_family != AF_INET6) {
1667			usedefault = 1;
1668			reply(500, "Invalid address rejected.");
1669			return 1;
1670		}
1671		if (paranoid &&
1672		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1673		     memcmp(&data_dest.su_sin6.sin6_addr,
1674			    &his_addr.su_sin6.sin6_addr,
1675			    sizeof(data_dest.su_sin6.sin6_addr)))) {
1676			usedefault = 1;
1677			reply(500, "Illegal PORT range rejected.");
1678		} else {
1679			usedefault = 0;
1680			if (pdata >= 0) {
1681				(void) close(pdata);
1682				pdata = -1;
1683			}
1684			reply(200, "%s command successful.", pcmd);
1685		}
1686		return 1;
1687	}
1688	return 0;
1689}
1690
1691static void
1692v4map_data_dest(void)
1693{
1694	struct in_addr savedaddr;
1695	int savedport;
1696
1697	if (data_dest.su_family != AF_INET) {
1698		usedefault = 1;
1699		reply(500, "Invalid address rejected.");
1700		return;
1701	}
1702
1703	savedaddr = data_dest.su_sin.sin_addr;
1704	savedport = data_dest.su_port;
1705
1706	memset(&data_dest, 0, sizeof(data_dest));
1707	data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1708	data_dest.su_sin6.sin6_family = AF_INET6;
1709	data_dest.su_sin6.sin6_port = savedport;
1710	memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1711	memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1712	       (caddr_t)&savedaddr, sizeof(savedaddr));
1713}
1714#endif
1715