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