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