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