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