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