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