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