ftpcmd.y revision 101806
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 101806 2002-08-13 13:56:42Z 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 pathstring CRLF
479		{
480			if ($2)
481				send_file_list($4);
482			free($4);
483		}
484	| LIST check_login CRLF
485		{
486			if ($2)
487				retrieve("/bin/ls -lgA", "");
488		}
489	| LIST check_login SP pathstring CRLF
490		{
491			if ($2)
492				retrieve("/bin/ls -lgA %s", $4);
493			free($4);
494		}
495	| STAT check_login SP pathname CRLF
496		{
497			if ($2 && $4 != NULL)
498				statfilecmd($4);
499			if ($4 != NULL)
500				free($4);
501		}
502	| STAT check_login CRLF
503		{
504			if ($2) {
505				statcmd();
506			}
507		}
508	| DELE check_login_ro SP pathname CRLF
509		{
510			if ($2 && $4 != NULL)
511				delete($4);
512			if ($4 != NULL)
513				free($4);
514		}
515	| RNTO check_login_ro SP pathname CRLF
516		{
517			if ($2 && $4 != NULL) {
518				if (fromname) {
519					renamecmd(fromname, $4);
520					free(fromname);
521					fromname = (char *) 0;
522				} else {
523					reply(503, "Bad sequence of commands.");
524				}
525			}
526			if ($4 != NULL)
527				free($4);
528		}
529	| ABOR check_login CRLF
530		{
531			if ($2)
532				reply(225, "ABOR command successful.");
533		}
534	| CWD check_login CRLF
535		{
536			if ($2) {
537				if (guest)
538					cwd("/");
539				else
540					cwd(pw->pw_dir);
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#ifdef unix
693#ifdef BSD
694			reply(215, "UNIX Type: L%d Version: BSD-%d",
695				NBBY, BSD);
696#else /* BSD */
697			reply(215, "UNIX Type: L%d", NBBY);
698#endif /* BSD */
699#else /* unix */
700			reply(215, "UNKNOWN Type: L%d", NBBY);
701#endif /* unix */
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	| error
756		{
757			yyclearin;		/* discard lookahead data */
758			yyerrok;		/* clear error condition */
759			state = 0;		/* reset lexer state */
760		}
761	;
762rcmd
763	: RNFR check_login_ro SP pathname CRLF
764		{
765			restart_point = (off_t) 0;
766			if ($2 && $4) {
767				if (fromname)
768					free(fromname);
769				fromname = (char *) 0;
770				if (renamefrom($4))
771					fromname = $4;
772				else
773					free($4);
774			} else if ($4) {
775				free($4);
776			}
777		}
778	| REST check_login SP NUMBER CRLF
779		{
780			if ($2) {
781				if (fromname)
782					free(fromname);
783				fromname = (char *) 0;
784				restart_point = $4.o;
785				reply(350, "Restarting at %llu. %s",
786				    restart_point,
787				    "Send STORE or RETRIEVE to initiate transfer.");
788			}
789		}
790	;
791
792username
793	: STRING
794	;
795
796password
797	: /* empty */
798		{
799			$$ = (char *)calloc(1, sizeof(char));
800		}
801	| STRING
802	;
803
804byte_size
805	: NUMBER
806		{
807			$$ = $1.i;
808		}
809	;
810
811host_port
812	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
813		NUMBER COMMA NUMBER
814		{
815			char *a, *p;
816
817			data_dest.su_len = sizeof(struct sockaddr_in);
818			data_dest.su_family = AF_INET;
819			p = (char *)&data_dest.su_sin.sin_port;
820			p[0] = $9.i; p[1] = $11.i;
821			a = (char *)&data_dest.su_sin.sin_addr;
822			a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
823		}
824	;
825
826host_long_port
827	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
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
833		{
834			char *a, *p;
835
836			memset(&data_dest, 0, sizeof(data_dest));
837			data_dest.su_len = sizeof(struct sockaddr_in6);
838			data_dest.su_family = AF_INET6;
839			p = (char *)&data_dest.su_port;
840			p[0] = $39.i; p[1] = $41.i;
841			a = (char *)&data_dest.su_sin6.sin6_addr;
842			a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
843			a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
844			a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
845			a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
846			if (his_addr.su_family == AF_INET6) {
847				/* XXX more sanity checks! */
848				data_dest.su_sin6.sin6_scope_id =
849					his_addr.su_sin6.sin6_scope_id;
850			}
851			if ($1.i != 6 || $3.i != 16 || $37.i != 2)
852				memset(&data_dest, 0, sizeof(data_dest));
853		}
854	| NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
855		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
856		NUMBER
857		{
858			char *a, *p;
859
860			memset(&data_dest, 0, sizeof(data_dest));
861			data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
862			data_dest.su_family = AF_INET;
863			p = (char *)&data_dest.su_port;
864			p[0] = $15.i; p[1] = $17.i;
865			a = (char *)&data_dest.su_sin.sin_addr;
866			a[0] =  $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
867			if ($1.i != 4 || $3.i != 4 || $13.i != 2)
868				memset(&data_dest, 0, sizeof(data_dest));
869		}
870	;
871
872form_code
873	: N
874		{
875			$$ = FORM_N;
876		}
877	| T
878		{
879			$$ = FORM_T;
880		}
881	| C
882		{
883			$$ = FORM_C;
884		}
885	;
886
887type_code
888	: A
889		{
890			cmd_type = TYPE_A;
891			cmd_form = FORM_N;
892		}
893	| A SP form_code
894		{
895			cmd_type = TYPE_A;
896			cmd_form = $3;
897		}
898	| E
899		{
900			cmd_type = TYPE_E;
901			cmd_form = FORM_N;
902		}
903	| E SP form_code
904		{
905			cmd_type = TYPE_E;
906			cmd_form = $3;
907		}
908	| I
909		{
910			cmd_type = TYPE_I;
911		}
912	| L
913		{
914			cmd_type = TYPE_L;
915			cmd_bytesz = NBBY;
916		}
917	| L SP byte_size
918		{
919			cmd_type = TYPE_L;
920			cmd_bytesz = $3;
921		}
922		/* this is for a bug in the BBN ftp */
923	| L byte_size
924		{
925			cmd_type = TYPE_L;
926			cmd_bytesz = $2;
927		}
928	;
929
930struct_code
931	: F
932		{
933			$$ = STRU_F;
934		}
935	| R
936		{
937			$$ = STRU_R;
938		}
939	| P
940		{
941			$$ = STRU_P;
942		}
943	;
944
945mode_code
946	: S
947		{
948			$$ = MODE_S;
949		}
950	| B
951		{
952			$$ = MODE_B;
953		}
954	| C
955		{
956			$$ = MODE_C;
957		}
958	;
959
960pathname
961	: pathstring
962		{
963			/*
964			 * Problem: this production is used for all pathname
965			 * processing, but only gives a 550 error reply.
966			 * This is a valid reply in some cases but not in others.
967			 */
968			if (logged_in && $1) {
969				glob_t gl;
970				int flags =
971				 GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
972
973				memset(&gl, 0, sizeof(gl));
974				flags |= GLOB_MAXPATH;
975				gl.gl_matchc = MAXGLOBARGS;
976				if (glob($1, flags, NULL, &gl) ||
977				    gl.gl_pathc == 0) {
978					reply(550, "wildcard expansion error");
979					$$ = NULL;
980				} else if (gl.gl_pathc > 1) {
981					reply(550, "ambiguous");
982					$$ = NULL;
983				} else {
984					$$ = strdup(gl.gl_pathv[0]);
985				}
986				globfree(&gl);
987				free($1);
988			} else
989				$$ = $1;
990		}
991	;
992
993pathstring
994	: STRING
995	;
996
997octal_number
998	: NUMBER
999		{
1000			int ret, dec, multby, digit;
1001
1002			/*
1003			 * Convert a number that was read as decimal number
1004			 * to what it would be if it had been read as octal.
1005			 */
1006			dec = $1.i;
1007			multby = 1;
1008			ret = 0;
1009			while (dec) {
1010				digit = dec%10;
1011				if (digit > 7) {
1012					ret = -1;
1013					break;
1014				}
1015				ret += digit * multby;
1016				multby *= 8;
1017				dec /= 10;
1018			}
1019			$$ = ret;
1020		}
1021	;
1022
1023
1024check_login
1025	: /* empty */
1026		{
1027		$$ = check_login1();
1028		}
1029	;
1030
1031check_login_epsv
1032	: /* empty */
1033		{
1034		if (noepsv) {
1035			reply(500, "EPSV command disabled");
1036			$$ = 0;
1037		}
1038		else
1039			$$ = check_login1();
1040		}
1041	;
1042
1043check_login_ro
1044	: /* empty */
1045		{
1046		if (readonly) {
1047			reply(550, "Permission denied.");
1048			$$ = 0;
1049		}
1050		else
1051			$$ = check_login1();
1052		}
1053	;
1054
1055%%
1056
1057#define	CMD	0	/* beginning of command */
1058#define	ARGS	1	/* expect miscellaneous arguments */
1059#define	STR1	2	/* expect SP followed by STRING */
1060#define	STR2	3	/* expect STRING */
1061#define	OSTR	4	/* optional SP then STRING */
1062#define	ZSTR1	5	/* optional SP then optional STRING */
1063#define	ZSTR2	6	/* optional STRING after SP */
1064#define	SITECMD	7	/* SITE command */
1065#define	NSTR	8	/* Number followed by a string */
1066
1067#define	MAXGLOBARGS	1000
1068
1069#define	MAXASIZE	10240	/* Deny ASCII SIZE on files larger than that */
1070
1071struct tab {
1072	char	*name;
1073	short	token;
1074	short	state;
1075	short	implemented;	/* 1 if command is implemented */
1076	char	*help;
1077};
1078
1079struct tab cmdtab[] = {		/* In order defined in RFC 765 */
1080	{ "USER", USER, STR1, 1,	"<sp> username" },
1081	{ "PASS", PASS, ZSTR1, 1,	"[<sp> [password]]" },
1082	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
1083	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
1084	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
1085	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
1086	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4, b5" },
1087	{ "LPRT", LPRT, ARGS, 1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1088	{ "EPRT", EPRT, STR1, 1,	"<sp> |af|addr|port|" },
1089	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
1090	{ "LPSV", LPSV, ARGS, 1,	"(set server in passive mode)" },
1091	{ "EPSV", EPSV, ARGS, 1,	"[<sp> af|ALL]" },
1092	{ "TYPE", TYPE, ARGS, 1,	"<sp> { A | E | I | L }" },
1093	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
1094	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
1095	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
1096	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
1097	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
1098	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
1099	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
1100	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
1101	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
1102	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
1103	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
1104	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
1105	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
1106	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
1107	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
1108	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
1109	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
1110	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
1111	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
1112	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
1113	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
1114	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
1115	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
1116	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
1117	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
1118	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
1119	{ "NOOP", NOOP, ARGS, 1,	"" },
1120	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
1121	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
1122	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
1123	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
1124	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
1125	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
1126	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
1127	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
1128	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
1129	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
1130	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
1131	{ NULL,   0,    0,    0,	0 }
1132};
1133
1134struct tab sitetab[] = {
1135	{ "MD5", MDFIVE, STR1, 1,	"[ <sp> file-name ]" },
1136	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
1137	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
1138	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
1139	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
1140	{ NULL,   0,    0,    0,	0 }
1141};
1142
1143static char	*copy(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				if (p->implemented == 0) {
1293					nack(p->name);
1294					return (LEXERR);
1295				}
1296				state = p->state;
1297				yylval.s = p->name;
1298				return (p->token);
1299			}
1300			break;
1301
1302		case SITECMD:
1303			if (cbuf[cpos] == ' ') {
1304				cpos++;
1305				return (SP);
1306			}
1307			cp = &cbuf[cpos];
1308			if ((cp2 = strpbrk(cp, " \n")))
1309				cpos = cp2 - cbuf;
1310			c = cbuf[cpos];
1311			cbuf[cpos] = '\0';
1312			upper(cp);
1313			p = lookup(sitetab, cp);
1314			cbuf[cpos] = c;
1315			if (guest == 0 && p != 0) {
1316				if (p->implemented == 0) {
1317					state = CMD;
1318					nack(p->name);
1319					return (LEXERR);
1320				}
1321				state = p->state;
1322				yylval.s = p->name;
1323				return (p->token);
1324			}
1325			state = CMD;
1326			break;
1327
1328		case ZSTR1:
1329		case OSTR:
1330			if (cbuf[cpos] == '\n') {
1331				state = CMD;
1332				return (CRLF);
1333			}
1334			/* FALLTHROUGH */
1335
1336		case STR1:
1337		dostr1:
1338			if (cbuf[cpos] == ' ') {
1339				cpos++;
1340				state = state == OSTR ? STR2 : state+1;
1341				return (SP);
1342			}
1343			break;
1344
1345		case ZSTR2:
1346			if (cbuf[cpos] == '\n') {
1347				state = CMD;
1348				return (CRLF);
1349			}
1350			/* FALLTHROUGH */
1351
1352		case STR2:
1353			cp = &cbuf[cpos];
1354			n = strlen(cp);
1355			cpos += n - 1;
1356			/*
1357			 * Make sure the string is nonempty and \n terminated.
1358			 */
1359			if (n > 1 && cbuf[cpos] == '\n') {
1360				cbuf[cpos] = '\0';
1361				yylval.s = copy(cp);
1362				cbuf[cpos] = '\n';
1363				state = ARGS;
1364				return (STRING);
1365			}
1366			break;
1367
1368		case NSTR:
1369			if (cbuf[cpos] == ' ') {
1370				cpos++;
1371				return (SP);
1372			}
1373			if (isdigit(cbuf[cpos])) {
1374				cp = &cbuf[cpos];
1375				while (isdigit(cbuf[++cpos]))
1376					;
1377				c = cbuf[cpos];
1378				cbuf[cpos] = '\0';
1379				yylval.u.i = atoi(cp);
1380				cbuf[cpos] = c;
1381				state = STR1;
1382				return (NUMBER);
1383			}
1384			state = STR1;
1385			goto dostr1;
1386
1387		case ARGS:
1388			if (isdigit(cbuf[cpos])) {
1389				cp = &cbuf[cpos];
1390				while (isdigit(cbuf[++cpos]))
1391					;
1392				c = cbuf[cpos];
1393				cbuf[cpos] = '\0';
1394				yylval.u.i = atoi(cp);
1395				yylval.u.o = strtoull(cp, (char **)NULL, 10);
1396				cbuf[cpos] = c;
1397				return (NUMBER);
1398			}
1399			if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1400			 && !isalnum(cbuf[cpos + 3])) {
1401				cpos += 3;
1402				return ALL;
1403			}
1404			switch (cbuf[cpos++]) {
1405
1406			case '\n':
1407				state = CMD;
1408				return (CRLF);
1409
1410			case ' ':
1411				return (SP);
1412
1413			case ',':
1414				return (COMMA);
1415
1416			case 'A':
1417			case 'a':
1418				return (A);
1419
1420			case 'B':
1421			case 'b':
1422				return (B);
1423
1424			case 'C':
1425			case 'c':
1426				return (C);
1427
1428			case 'E':
1429			case 'e':
1430				return (E);
1431
1432			case 'F':
1433			case 'f':
1434				return (F);
1435
1436			case 'I':
1437			case 'i':
1438				return (I);
1439
1440			case 'L':
1441			case 'l':
1442				return (L);
1443
1444			case 'N':
1445			case 'n':
1446				return (N);
1447
1448			case 'P':
1449			case 'p':
1450				return (P);
1451
1452			case 'R':
1453			case 'r':
1454				return (R);
1455
1456			case 'S':
1457			case 's':
1458				return (S);
1459
1460			case 'T':
1461			case 't':
1462				return (T);
1463
1464			}
1465			break;
1466
1467		default:
1468			fatalerror("Unknown state in scanner.");
1469		}
1470		state = CMD;
1471		return (LEXERR);
1472	}
1473}
1474
1475void
1476upper(char *s)
1477{
1478	while (*s != '\0') {
1479		if (islower(*s))
1480			*s = toupper(*s);
1481		s++;
1482	}
1483}
1484
1485static char *
1486copy(char *s)
1487{
1488	char *p;
1489
1490	p = malloc((unsigned) strlen(s) + 1);
1491	if (p == NULL)
1492		fatalerror("Ran out of memory.");
1493	(void) strcpy(p, s);
1494	return (p);
1495}
1496
1497static void
1498help(struct tab *ctab, char *s)
1499{
1500	struct tab *c;
1501	int width, NCMDS;
1502	char *type;
1503
1504	if (ctab == sitetab)
1505		type = "SITE ";
1506	else
1507		type = "";
1508	width = 0, NCMDS = 0;
1509	for (c = ctab; c->name != NULL; c++) {
1510		int len = strlen(c->name);
1511
1512		if (len > width)
1513			width = len;
1514		NCMDS++;
1515	}
1516	width = (width + 8) &~ 7;
1517	if (s == 0) {
1518		int i, j, w;
1519		int columns, lines;
1520
1521		lreply(214, "The following %scommands are recognized %s.",
1522		    type, "(* =>'s unimplemented)");
1523		columns = 76 / width;
1524		if (columns == 0)
1525			columns = 1;
1526		lines = (NCMDS + columns - 1) / columns;
1527		for (i = 0; i < lines; i++) {
1528			printf("   ");
1529			for (j = 0; j < columns; j++) {
1530				c = ctab + j * lines + i;
1531				printf("%s%c", c->name,
1532					c->implemented ? ' ' : '*');
1533				if (c + lines >= &ctab[NCMDS])
1534					break;
1535				w = strlen(c->name) + 1;
1536				while (w < width) {
1537					putchar(' ');
1538					w++;
1539				}
1540			}
1541			printf("\r\n");
1542		}
1543		(void) fflush(stdout);
1544		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
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#ifdef INET6
1656/* Return 1, if port check is done. Return 0, if not yet. */
1657static int
1658port_check_v6(const char *pcmd)
1659{
1660	if (his_addr.su_family == AF_INET6) {
1661		if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1662			/* Convert data_dest into v4 mapped sockaddr.*/
1663			v4map_data_dest();
1664		if (data_dest.su_family != AF_INET6) {
1665			usedefault = 1;
1666			reply(500, "Invalid address rejected.");
1667			return 1;
1668		}
1669		if (paranoid &&
1670		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1671		     memcmp(&data_dest.su_sin6.sin6_addr,
1672			    &his_addr.su_sin6.sin6_addr,
1673			    sizeof(data_dest.su_sin6.sin6_addr)))) {
1674			usedefault = 1;
1675			reply(500, "Illegal PORT range rejected.");
1676		} else {
1677			usedefault = 0;
1678			if (pdata >= 0) {
1679				(void) close(pdata);
1680				pdata = -1;
1681			}
1682			reply(200, "%s command successful.", pcmd);
1683		}
1684		return 1;
1685	}
1686	return 0;
1687}
1688
1689static void
1690v4map_data_dest(void)
1691{
1692	struct in_addr savedaddr;
1693	int savedport;
1694
1695	if (data_dest.su_family != AF_INET) {
1696		usedefault = 1;
1697		reply(500, "Invalid address rejected.");
1698		return;
1699	}
1700
1701	savedaddr = data_dest.su_sin.sin_addr;
1702	savedport = data_dest.su_port;
1703
1704	memset(&data_dest, 0, sizeof(data_dest));
1705	data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1706	data_dest.su_sin6.sin6_family = AF_INET6;
1707	data_dest.su_sin6.sin6_port = savedport;
1708	memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1709	memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1710	       (caddr_t)&savedaddr, sizeof(savedaddr));
1711}
1712#endif
1713