ftpcmd.y revision 110036
1121054Semax/*
2121054Semax * Copyright (c) 1985, 1988, 1993, 1994
3121054Semax *	The Regents of the University of California.  All rights reserved.
4121054Semax *
5121054Semax * Redistribution and use in source and binary forms, with or without
6121054Semax * modification, are permitted provided that the following conditions
7121054Semax * are met:
8121054Semax * 1. Redistributions of source code must retain the above copyright
9121054Semax *    notice, this list of conditions and the following disclaimer.
10121054Semax * 2. Redistributions in binary form must reproduce the above copyright
11121054Semax *    notice, this list of conditions and the following disclaimer in the
12121054Semax *    documentation and/or other materials provided with the distribution.
13121054Semax * 3. All advertising materials mentioning features or use of this software
14121054Semax *    must display the following acknowledgement:
15121054Semax *	This product includes software developed by the University of
16121054Semax *	California, Berkeley and its contributors.
17121054Semax * 4. Neither the name of the University nor the names of its contributors
18121054Semax *    may be used to endorse or promote products derived from this software
19121054Semax *    without specific prior written permission.
20121054Semax *
21121054Semax * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22121054Semax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23121054Semax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24121054Semax * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25121054Semax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26121054Semax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27121054Semax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28121054Semax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29121054Semax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30121054Semax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31121054Semax * SUCH DAMAGE.
32146691Semax *
33121054Semax *	@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94
34121054Semax */
35121054Semax
36121054Semax/*
37121054Semax * Grammar for FTP commands.
38121054Semax * See RFC 959.
39121054Semax */
40121054Semax
41124317Semax%{
42121054Semax
43121054Semax#ifndef lint
44121054Semax#if 0
45121054Semaxstatic char sccsid[] = "@(#)ftpcmd.y	8.3 (Berkeley) 4/6/94";
46121054Semax#endif
47121054Semaxstatic const char rcsid[] =
48121054Semax  "$FreeBSD: head/libexec/ftpd/ftpcmd.y 110036 2003-01-29 10:07:27Z yar $";
49121054Semax#endif /* not lint */
50121054Semax
51121054Semax#include <sys/param.h>
52121054Semax#include <sys/socket.h>
53121054Semax#include <sys/stat.h>
54121054Semax
55121054Semax#include <netinet/in.h>
56121054Semax#include <arpa/ftp.h>
57124317Semax
58121054Semax#include <ctype.h>
59121054Semax#include <errno.h>
60121054Semax#include <glob.h>
61121054Semax#include <libutil.h>
62121054Semax#include <limits.h>
63121054Semax#include <md5.h>
64121054Semax#include <netdb.h>
65121054Semax#include <pwd.h>
66121054Semax#include <signal.h>
67121054Semax#include <stdio.h>
68121054Semax#include <stdlib.h>
69121054Semax#include <string.h>
70121054Semax#include <syslog.h>
71121054Semax#include <time.h>
72121054Semax#include <unistd.h>
73121054Semax
74124317Semax#include "extern.h"
75121054Semax#include "pathnames.h"
76124317Semax
77121054Semaxextern	union sockunion data_dest, his_addr;
78121054Semaxextern	int logged_in;
79121054Semaxextern	struct passwd *pw;
80128076Semaxextern	int guest;
81121054Semaxextern	char *homedir;
82121054Semaxextern 	int paranoid;
83121054Semaxextern	int logging;
84121054Semaxextern	int type;
85121054Semaxextern	int form;
86121054Semaxextern	int ftpdebug;
87121054Semaxextern	int timeout;
88121054Semaxextern	int maxtimeout;
89121054Semaxextern  int pdata;
90121054Semaxextern	char *hostname;
91121054Semaxextern	char remotehost[];
92121054Semaxextern	char proctitle[];
93121054Semaxextern	int usedefault;
94121054Semaxextern  int transflag;
95121054Semaxextern  char tmpline[];
96121054Semaxextern	int readonly;
97121054Semaxextern	int noepsv;
98121054Semaxextern	int noretr;
99121054Semaxextern	int noguestretr;
100121054Semaxextern	char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */
101121054Semax
102121054Semaxoff_t	restart_point;
103121054Semax
104121054Semaxstatic	int cmd_type;
105290395Semaxstatic	int cmd_form;
106290395Semaxstatic	int cmd_bytesz;
107290395Semaxstatic	int state;
108290395Semaxchar	cbuf[512];
109290395Semaxchar	*fromname = (char *) 0;
110290395Semax
111121054Semaxextern int epsvall;
112121054Semax
113121054Semax%}
114121054Semax
115121054Semax%union {
116121054Semax	struct {
117121054Semax		off_t	o;
118121054Semax		int	i;
119121054Semax	} u;
120121054Semax	char   *s;
121121054Semax}
122121054Semax
123121054Semax%token
124121054Semax	A	B	C	E	F	I
125121054Semax	L	N	P	R	S	T
126121054Semax	ALL
127121054Semax
128146691Semax	SP	CRLF	COMMA
129121054Semax
130146691Semax	USER	PASS	ACCT	REIN	QUIT	PORT
131146691Semax	PASV	TYPE	STRU	MODE	RETR	STOR
132146691Semax	APPE	MLFL	MAIL	MSND	MSOM	MSAM
133146691Semax	MRSQ	MRCP	ALLO	REST	RNFR	RNTO
134146691Semax	ABOR	DELE	CWD	LIST	NLST	SITE
135146691Semax	STAT	HELP	NOOP	MKD	RMD	PWD
136121054Semax	CDUP	STOU	SMNT	SYST	SIZE	MDTM
137121054Semax	LPRT	LPSV	EPRT	EPSV
138121054Semax
139121054Semax	UMASK	IDLE	CHMOD	MDFIVE
140121054Semax
141121054Semax	LEXERR	NOTIMPL
142121054Semax
143121054Semax%token	<s> STRING
144121054Semax%token	<u> NUMBER
145121054Semax
146121054Semax%type	<u.i> check_login octal_number byte_size
147121054Semax%type	<u.i> check_login_ro check_login_epsv
148121054Semax%type	<u.i> struct_code mode_code type_code form_code
149121054Semax%type	<s> pathstring pathname password username
150121054Semax%type	<s> ALL NOTIMPL
151121054Semax
152121054Semax%start	cmd_list
153121054Semax
154121054Semax%%
155121054Semax
156121054Semaxcmd_list
157121054Semax	: /* empty */
158121054Semax	| cmd_list cmd
159121054Semax		{
160121054Semax			if (fromname)
161121054Semax				free(fromname);
162121054Semax			fromname = (char *) 0;
163124317Semax			restart_point = (off_t) 0;
164121054Semax		}
165121054Semax	| cmd_list rcmd
166121054Semax	;
167121054Semax
168121054Semaxcmd
169121054Semax	: USER SP username CRLF
170121054Semax		{
171121054Semax			user($3);
172124317Semax			free($3);
173121054Semax		}
174121054Semax	| PASS SP password CRLF
175121054Semax		{
176121054Semax			pass($3);
177121054Semax			free($3);
178121054Semax		}
179121054Semax	| PASS CRLF
180121054Semax		{
181121054Semax			pass("");
182121054Semax		}
183121054Semax	| PORT check_login SP host_port CRLF
184121054Semax		{
185121054Semax			if (epsvall) {
186121054Semax				reply(501, "no PORT allowed after EPSV ALL");
187121054Semax				goto port_done;
188121054Semax			}
189146691Semax			if (!$2)
190121054Semax				goto port_done;
191146691Semax			if (port_check("PORT") == 1)
192146691Semax				goto port_done;
193146691Semax#ifdef INET6
194146691Semax			if ((his_addr.su_family != AF_INET6 ||
195146691Semax			     !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
196146691Semax				/* shoud never happen */
197121054Semax				usedefault = 1;
198121054Semax				reply(500, "Invalid address rejected.");
199121054Semax				goto port_done;
200121054Semax			}
201121054Semax			port_check_v6("pcmd");
202121054Semax#endif
203121054Semax		port_done:
204121054Semax		}
205121054Semax	| LPRT check_login SP host_long_port CRLF
206121054Semax		{
207121054Semax			if (epsvall) {
208121054Semax				reply(501, "no LPRT allowed after EPSV ALL");
209121054Semax				goto lprt_done;
210121054Semax			}
211121054Semax			if (!$2)
212121054Semax				goto lprt_done;
213121054Semax			if (port_check("LPRT") == 1)
214121054Semax				goto lprt_done;
215121054Semax#ifdef INET6
216121054Semax			if (his_addr.su_family != AF_INET6) {
217121054Semax				usedefault = 1;
218121054Semax				reply(500, "Invalid address rejected.");
219121054Semax				goto lprt_done;
220121054Semax			}
221121054Semax			if (port_check_v6("LPRT") == 1)
222121054Semax				goto lprt_done;
223121054Semax#endif
224121054Semax		lprt_done:
225121054Semax		}
226121054Semax	| EPRT check_login SP STRING CRLF
227121054Semax		{
228121054Semax			char delim;
229121054Semax			char *tmp = NULL;
230121054Semax			char *p, *q;
231121054Semax			char *result[3];
232121054Semax			struct addrinfo hints;
233121054Semax			struct addrinfo *res;
234121054Semax			int i;
235121054Semax
236121054Semax			if (epsvall) {
237121054Semax				reply(501, "no EPRT allowed after EPSV ALL");
238121054Semax				goto eprt_done;
239121054Semax			}
240128076Semax			if (!$2)
241121054Semax				goto eprt_done;
242121054Semax
243121054Semax			memset(&data_dest, 0, sizeof(data_dest));
244121054Semax			tmp = strdup($4);
245121054Semax			if (ftpdebug)
246146691Semax				syslog(LOG_DEBUG, "%s", tmp);
247124317Semax			if (!tmp) {
248146691Semax				fatalerror("not enough core");
249146691Semax				/*NOTREACHED*/
250124317Semax			}
251121054Semax			p = tmp;
252121054Semax			delim = p[0];
253146691Semax			p++;
254146691Semax			memset(result, 0, sizeof(result));
255146691Semax			for (i = 0; i < 3; i++) {
256146691Semax				q = strchr(p, delim);
257146691Semax				if (!q || *q != delim) {
258146691Semax		parsefail:
259146691Semax					reply(500,
260146691Semax						"Invalid argument, rejected.");
261146691Semax					if (tmp)
262146691Semax						free(tmp);
263146691Semax					usedefault = 1;
264121054Semax					goto eprt_done;
265121054Semax				}
266121054Semax				*q++ = '\0';
267290395Semax				result[i] = p;
268290395Semax				if (ftpdebug)
269290395Semax					syslog(LOG_DEBUG, "%d: %s", i, p);
270121054Semax				p = q;
271121054Semax			}
272121054Semax
273121054Semax			/* some more sanity check */
274121054Semax			p = result[0];
275290395Semax			while (*p) {
276290395Semax				if (!isdigit(*p))
277290395Semax					goto parsefail;
278121054Semax				p++;
279121054Semax			}
280121054Semax			p = result[2];
281121054Semax			while (*p) {
282121054Semax				if (!isdigit(*p))
283290395Semax					goto parsefail;
284290395Semax				p++;
285290395Semax			}
286121054Semax
287121054Semax			/* grab address */
288121054Semax			memset(&hints, 0, sizeof(hints));
289121054Semax			if (atoi(result[0]) == 1)
290121054Semax				hints.ai_family = PF_INET;
291290395Semax#ifdef INET6
292121054Semax			else if (atoi(result[0]) == 2)
293121054Semax				hints.ai_family = PF_INET6;
294121054Semax#endif
295121054Semax			else
296121054Semax				hints.ai_family = PF_UNSPEC;	/*XXX*/
297121054Semax			hints.ai_socktype = SOCK_STREAM;
298121054Semax			i = getaddrinfo(result[1], result[2], &hints, &res);
299290395Semax			if (i)
300121054Semax				goto parsefail;
301121054Semax			memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
302121054Semax#ifdef INET6
303121054Semax			if (his_addr.su_family == AF_INET6
304121054Semax			    && data_dest.su_family == AF_INET6) {
305121054Semax				/* XXX more sanity checks! */
306121054Semax				data_dest.su_sin6.sin6_scope_id =
307290395Semax					his_addr.su_sin6.sin6_scope_id;
308121054Semax			}
309121054Semax#endif
310121054Semax			free(tmp);
311121054Semax			tmp = NULL;
312121054Semax
313121054Semax			if (port_check("EPRT") == 1)
314121054Semax				goto eprt_done;
315121054Semax#ifdef INET6
316121054Semax			if (his_addr.su_family != AF_INET6) {
317121054Semax				usedefault = 1;
318121054Semax				reply(500, "Invalid address rejected.");
319121054Semax				goto eprt_done;
320121054Semax			}
321121054Semax			if (port_check_v6("EPRT") == 1)
322124317Semax				goto eprt_done;
323121054Semax#endif
324124317Semax		eprt_done:
325121054Semax			free($4);
326121054Semax		}
327121054Semax	| PASV check_login CRLF
328128076Semax		{
329121054Semax			if (epsvall)
330121054Semax				reply(501, "no PASV allowed after EPSV ALL");
331121054Semax			else if ($2)
332121054Semax				passive();
333121054Semax		}
334121054Semax	| LPSV check_login CRLF
335121054Semax		{
336121054Semax			if (epsvall)
337121054Semax				reply(501, "no LPSV allowed after EPSV ALL");
338121054Semax			else if ($2)
339121054Semax				long_passive("LPSV", PF_UNSPEC);
340121054Semax		}
341121054Semax	| EPSV check_login_epsv SP NUMBER CRLF
342121054Semax		{
343121054Semax			if ($2) {
344121054Semax				int pf;
345121054Semax				switch ($4.i) {
346121054Semax				case 1:
347121054Semax					pf = PF_INET;
348121054Semax					break;
349121054Semax#ifdef INET6
350121054Semax				case 2:
351121054Semax					pf = PF_INET6;
352121054Semax					break;
353290395Semax#endif
354290395Semax				default:
355290395Semax					pf = -1;	/*junk value*/
356290395Semax					break;
357290395Semax				}
358290395Semax				long_passive("EPSV", pf);
359121054Semax			}
360121054Semax		}
361121054Semax	| EPSV check_login_epsv SP ALL CRLF
362121054Semax		{
363121054Semax			if ($2) {
364121054Semax				reply(200,
365121054Semax				      "EPSV ALL command successful.");
366121054Semax				epsvall++;
367121054Semax			}
368121054Semax		}
369121054Semax	| EPSV check_login_epsv CRLF
370121054Semax		{
371121054Semax			if ($2)
372121054Semax				long_passive("EPSV", PF_UNSPEC);
373121054Semax		}
374121054Semax	| TYPE check_login SP type_code CRLF
375121054Semax		{
376121054Semax			if ($2) {
377121054Semax				switch (cmd_type) {
378121054Semax
379121054Semax				case TYPE_A:
380121054Semax					if (cmd_form == FORM_N) {
381290395Semax						reply(200, "Type set to A.");
382290395Semax						type = cmd_type;
383290395Semax						form = cmd_form;
384290395Semax					} else
385290395Semax						reply(504, "Form must be N.");
386290395Semax					break;
387121054Semax
388121054Semax				case TYPE_E:
389121054Semax					reply(504, "Type E not implemented.");
390121054Semax					break;
391121054Semax
392121054Semax				case TYPE_I:
393121054Semax					reply(200, "Type set to I.");
394121054Semax					type = cmd_type;
395121054Semax					break;
396121054Semax
397121054Semax				case TYPE_L:
398121054Semax#if CHAR_BIT == 8
399121054Semax					if (cmd_bytesz == 8) {
400121054Semax						reply(200,
401121054Semax						    "Type set to L (byte size 8).");
402121054Semax						type = cmd_type;
403121054Semax					} else
404121054Semax						reply(504, "Byte size must be 8.");
405121054Semax#else /* CHAR_BIT == 8 */
406121054Semax					UNIMPLEMENTED for CHAR_BIT != 8
407121054Semax#endif /* CHAR_BIT == 8 */
408124317Semax				}
409121054Semax			}
410124317Semax		}
411121054Semax	| STRU check_login SP struct_code CRLF
412121054Semax		{
413121054Semax			if ($2) {
414128076Semax				switch ($4) {
415121054Semax
416121054Semax				case STRU_F:
417121054Semax					reply(200, "STRU F ok.");
418121054Semax					break;
419121054Semax
420121054Semax				default:
421121054Semax					reply(504, "Unimplemented STRU type.");
422121054Semax				}
423121054Semax			}
424121054Semax		}
425121054Semax	| MODE check_login SP mode_code CRLF
426121054Semax		{
427121054Semax			if ($2) {
428121054Semax				switch ($4) {
429121054Semax
430121054Semax				case MODE_S:
431121054Semax					reply(200, "MODE S ok.");
432121054Semax					break;
433121054Semax
434121054Semax				default:
435121054Semax					reply(502, "Unimplemented MODE type.");
436121054Semax				}
437121054Semax			}
438121054Semax		}
439290395Semax	| ALLO check_login SP NUMBER CRLF
440290395Semax		{
441290395Semax			if ($2) {
442290395Semax				reply(202, "ALLO command ignored.");
443290395Semax			}
444290395Semax		}
445121054Semax	| ALLO check_login SP NUMBER SP R SP NUMBER CRLF
446121054Semax		{
447121054Semax			if ($2) {
448121054Semax				reply(202, "ALLO command ignored.");
449121054Semax			}
450121054Semax		}
451121054Semax	| RETR check_login SP pathname CRLF
452121054Semax		{
453121054Semax			if (noretr || (guest && noguestretr))
454121054Semax				reply(500, "RETR command is disabled");
455121054Semax			else if ($2 && $4 != NULL)
456121054Semax				retrieve((char *) 0, $4);
457121054Semax
458121054Semax			if ($4 != NULL)
459121054Semax				free($4);
460121054Semax		}
461121054Semax	| STOR check_login_ro SP pathname CRLF
462121054Semax		{
463121054Semax			if ($2 && $4 != NULL)
464121054Semax				store($4, "w", 0);
465121054Semax			if ($4 != NULL)
466121054Semax				free($4);
467121054Semax		}
468290395Semax	| APPE check_login_ro SP pathname CRLF
469290395Semax		{
470290395Semax			if ($2 && $4 != NULL)
471290395Semax				store($4, "a", 0);
472290395Semax			if ($4 != NULL)
473290395Semax				free($4);
474290395Semax		}
475121054Semax	| NLST check_login CRLF
476121054Semax		{
477121054Semax			if ($2)
478121054Semax				send_file_list(".");
479121054Semax		}
480121054Semax	| NLST check_login SP pathstring CRLF
481121054Semax		{
482121054Semax			if ($2)
483121054Semax				send_file_list($4);
484121054Semax			free($4);
485121054Semax		}
486121054Semax	| LIST check_login CRLF
487121054Semax		{
488121054Semax			if ($2)
489121054Semax				retrieve(_PATH_LS " -lgA", "");
490121054Semax		}
491121054Semax	| LIST check_login SP pathstring CRLF
492146691Semax		{
493121054Semax			if ($2)
494146691Semax				retrieve(_PATH_LS " -lgA %s", $4);
495146691Semax			free($4);
496146691Semax		}
497146691Semax	| STAT check_login SP pathname CRLF
498146691Semax		{
499146691Semax			if ($2 && $4 != NULL)
500121054Semax				statfilecmd($4);
501121054Semax			if ($4 != NULL)
502121054Semax				free($4);
503121054Semax		}
504121054Semax	| STAT check_login CRLF
505121054Semax		{
506121054Semax			if ($2) {
507121054Semax				statcmd();
508121054Semax			}
509121054Semax		}
510121054Semax	| DELE check_login_ro SP pathname CRLF
511121054Semax		{
512121054Semax			if ($2 && $4 != NULL)
513121054Semax				delete($4);
514121054Semax			if ($4 != NULL)
515121054Semax				free($4);
516121054Semax		}
517121054Semax	| RNTO check_login_ro SP pathname CRLF
518121054Semax		{
519121054Semax			if ($2 && $4 != NULL) {
520121054Semax				if (fromname) {
521121054Semax					renamecmd(fromname, $4);
522121054Semax					free(fromname);
523121054Semax					fromname = (char *) 0;
524121054Semax				} else {
525121054Semax					reply(503, "Bad sequence of commands.");
526121054Semax				}
527121054Semax			}
528121054Semax			if ($4 != NULL)
529121054Semax				free($4);
530121054Semax		}
531124317Semax	| ABOR check_login CRLF
532121054Semax		{
533121054Semax			if ($2)
534121054Semax				reply(225, "ABOR command successful.");
535121054Semax		}
536121054Semax	| CWD check_login CRLF
537121054Semax		{
538121054Semax			if ($2) {
539121054Semax				cwd(homedir);
540121054Semax			}
541121054Semax		}
542121054Semax	| CWD check_login SP pathname CRLF
543121054Semax		{
544121054Semax			if ($2 && $4 != NULL)
545121054Semax				cwd($4);
546121054Semax			if ($4 != NULL)
547121054Semax				free($4);
548121054Semax		}
549121054Semax	| HELP CRLF
550121054Semax		{
551121054Semax			help(cmdtab, (char *) 0);
552121054Semax		}
553121054Semax	| HELP SP STRING CRLF
554121054Semax		{
555121054Semax			char *cp = $3;
556121054Semax
557121054Semax			if (strncasecmp(cp, "SITE", 4) == 0) {
558121054Semax				cp = $3 + 4;
559121054Semax				if (*cp == ' ')
560121054Semax					cp++;
561121054Semax				if (*cp)
562121054Semax					help(sitetab, cp);
563121054Semax				else
564121054Semax					help(sitetab, (char *) 0);
565121054Semax			} else
566121054Semax				help(cmdtab, $3);
567121054Semax			free($3);
568121054Semax		}
569121054Semax	| NOOP CRLF
570121054Semax		{
571121054Semax			reply(200, "NOOP command successful.");
572121054Semax		}
573121054Semax	| MKD check_login_ro SP pathname CRLF
574121054Semax		{
575121054Semax			if ($2 && $4 != NULL)
576121054Semax				makedir($4);
577121054Semax			if ($4 != NULL)
578121054Semax				free($4);
579121054Semax		}
580121054Semax	| RMD check_login_ro SP pathname CRLF
581121054Semax		{
582121054Semax			if ($2 && $4 != NULL)
583121054Semax				removedir($4);
584121054Semax			if ($4 != NULL)
585121054Semax				free($4);
586121054Semax		}
587121054Semax	| PWD check_login CRLF
588121054Semax		{
589121054Semax			if ($2)
590121054Semax				pwd();
591121054Semax		}
592121054Semax	| CDUP check_login CRLF
593121054Semax		{
594121054Semax			if ($2)
595121054Semax				cwd("..");
596121054Semax		}
597121054Semax	| SITE SP HELP CRLF
598121054Semax		{
599121054Semax			help(sitetab, (char *) 0);
600121054Semax		}
601121054Semax	| SITE SP HELP SP STRING CRLF
602121054Semax		{
603121054Semax			help(sitetab, $5);
604121054Semax			free($5);
605121054Semax		}
606121054Semax	| SITE SP MDFIVE check_login SP pathname CRLF
607121054Semax		{
608121054Semax			char p[64], *q;
609121054Semax
610121054Semax			if ($4 && $6) {
611121054Semax				q = MD5File($6, p);
612121054Semax				if (q != NULL)
613121054Semax					reply(200, "MD5(%s) = %s", $6, p);
614121054Semax				else
615121054Semax					perror_reply(550, $6);
616124317Semax			}
617121054Semax			if ($6)
618121054Semax				free($6);
619121054Semax		}
620121054Semax	| SITE SP UMASK check_login CRLF
621121054Semax		{
622121054Semax			int oldmask;
623121054Semax
624121054Semax			if ($4) {
625121054Semax				oldmask = umask(0);
626121054Semax				(void) umask(oldmask);
627121054Semax				reply(200, "Current UMASK is %03o", oldmask);
628121054Semax			}
629121054Semax		}
630121054Semax	| SITE SP UMASK check_login SP octal_number CRLF
631121054Semax		{
632121054Semax			int oldmask;
633121054Semax
634121054Semax			if ($4) {
635121054Semax				if (($6 == -1) || ($6 > 0777)) {
636121054Semax					reply(501, "Bad UMASK value");
637121054Semax				} else {
638121054Semax					oldmask = umask($6);
639121054Semax					reply(200,
640121054Semax					    "UMASK set to %03o (was %03o)",
641121054Semax					    $6, oldmask);
642121054Semax				}
643121054Semax			}
644121054Semax		}
645121054Semax	| SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
646121054Semax		{
647121054Semax			if ($4 && ($8 != NULL)) {
648121054Semax				if (($6 == -1 ) || ($6 > 0777))
649121054Semax					reply(501, "Bad mode value");
650121054Semax				else if (chmod($8, $6) < 0)
651121054Semax					perror_reply(550, $8);
652121054Semax				else
653121054Semax					reply(200, "CHMOD command successful.");
654121054Semax			}
655121054Semax			if ($8 != NULL)
656121054Semax				free($8);
657121054Semax		}
658121054Semax	| SITE SP check_login IDLE CRLF
659121054Semax		{
660121054Semax			if ($3)
661121054Semax				reply(200,
662121054Semax			    	    "Current IDLE time limit is %d seconds; max %d",
663121054Semax				    timeout, maxtimeout);
664121054Semax		}
665121054Semax	| SITE SP check_login IDLE SP NUMBER CRLF
666121054Semax		{
667121054Semax			if ($3) {
668121054Semax				if ($6.i < 30 || $6.i > maxtimeout) {
669121054Semax					reply(501,
670121054Semax					    "Maximum IDLE time must be between 30 and %d seconds",
671121054Semax					    maxtimeout);
672121054Semax				} else {
673121054Semax					timeout = $6.i;
674121054Semax					(void) alarm((unsigned) timeout);
675121054Semax					reply(200,
676121054Semax					    "Maximum IDLE time set to %d seconds",
677121054Semax					    timeout);
678121054Semax				}
679121054Semax			}
680121054Semax		}
681121054Semax	| STOU check_login_ro SP pathname CRLF
682121054Semax		{
683121054Semax			if ($2 && $4 != NULL)
684121054Semax				store($4, "w", 1);
685121054Semax			if ($4 != NULL)
686121054Semax				free($4);
687121054Semax		}
688121054Semax	| SYST check_login CRLF
689121054Semax		{
690121054Semax			if ($2)
691121054Semax#ifdef unix
692121054Semax#ifdef BSD
693121054Semax			reply(215, "UNIX Type: L%d Version: BSD-%d",
694121054Semax				CHAR_BIT, BSD);
695121054Semax#else /* BSD */
696121054Semax			reply(215, "UNIX Type: L%d", CHAR_BIT);
697121054Semax#endif /* BSD */
698121054Semax#else /* unix */
699121054Semax			reply(215, "UNKNOWN Type: L%d", CHAR_BIT);
700121054Semax#endif /* unix */
701121054Semax		}
702121054Semax
703121054Semax		/*
704121054Semax		 * SIZE is not in RFC959, but Postel has blessed it and
705121054Semax		 * it will be in the updated RFC.
706121054Semax		 *
707121054Semax		 * Return size of file in a format suitable for
708121054Semax		 * using with RESTART (we just count bytes).
709121054Semax		 */
710121054Semax	| SIZE check_login SP pathname CRLF
711121054Semax		{
712121054Semax			if ($2 && $4 != NULL)
713121054Semax				sizecmd($4);
714121054Semax			if ($4 != NULL)
715121054Semax				free($4);
716121054Semax		}
717121054Semax
718121054Semax		/*
719121054Semax		 * MDTM is not in RFC959, but Postel has blessed it and
720121054Semax		 * it will be in the updated RFC.
721121054Semax		 *
722121054Semax		 * Return modification time of file as an ISO 3307
723121054Semax		 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
724121054Semax		 * where xxx is the fractional second (of any precision,
725121054Semax		 * not necessarily 3 digits)
726121054Semax		 */
727121054Semax	| MDTM check_login SP pathname CRLF
728121054Semax		{
729121054Semax			if ($2 && $4 != NULL) {
730121054Semax				struct stat stbuf;
731121054Semax				if (stat($4, &stbuf) < 0)
732121054Semax					reply(550, "%s: %s",
733121054Semax					    $4, strerror(errno));
734121054Semax				else if (!S_ISREG(stbuf.st_mode)) {
735121054Semax					reply(550, "%s: not a plain file.", $4);
736121054Semax				} else {
737121054Semax					struct tm *t;
738121054Semax					t = gmtime(&stbuf.st_mtime);
739121054Semax					reply(213,
740121054Semax					    "%04d%02d%02d%02d%02d%02d",
741121054Semax					    1900 + t->tm_year,
742121054Semax					    t->tm_mon+1, t->tm_mday,
743121054Semax					    t->tm_hour, t->tm_min, t->tm_sec);
744121054Semax				}
745121054Semax			}
746121054Semax			if ($4 != NULL)
747121054Semax				free($4);
748		}
749	| QUIT CRLF
750		{
751			reply(221, "Goodbye.");
752			dologout(0);
753		}
754	| NOTIMPL
755		{
756			nack($1);
757		}
758	| error
759		{
760			yyclearin;		/* discard lookahead data */
761			yyerrok;		/* clear error condition */
762			state = CMD;		/* reset lexer state */
763		}
764	;
765rcmd
766	: RNFR check_login_ro SP pathname CRLF
767		{
768			restart_point = (off_t) 0;
769			if ($2 && $4) {
770				if (fromname)
771					free(fromname);
772				fromname = (char *) 0;
773				if (renamefrom($4))
774					fromname = $4;
775				else
776					free($4);
777			} else if ($4) {
778				free($4);
779			}
780		}
781	| REST check_login SP NUMBER CRLF
782		{
783			if ($2) {
784				if (fromname)
785					free(fromname);
786				fromname = (char *) 0;
787				restart_point = $4.o;
788				reply(350, "Restarting at %llu. %s",
789				    restart_point,
790				    "Send STORE or RETRIEVE to initiate transfer.");
791			}
792		}
793	;
794
795username
796	: STRING
797	;
798
799password
800	: /* empty */
801		{
802			$$ = (char *)calloc(1, sizeof(char));
803		}
804	| STRING
805	;
806
807byte_size
808	: NUMBER
809		{
810			$$ = $1.i;
811		}
812	;
813
814host_port
815	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
816		NUMBER COMMA NUMBER
817		{
818			char *a, *p;
819
820			data_dest.su_len = sizeof(struct sockaddr_in);
821			data_dest.su_family = AF_INET;
822			p = (char *)&data_dest.su_sin.sin_port;
823			p[0] = $9.i; p[1] = $11.i;
824			a = (char *)&data_dest.su_sin.sin_addr;
825			a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
826		}
827	;
828
829host_long_port
830	: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
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
836		{
837			char *a, *p;
838
839			memset(&data_dest, 0, sizeof(data_dest));
840			data_dest.su_len = sizeof(struct sockaddr_in6);
841			data_dest.su_family = AF_INET6;
842			p = (char *)&data_dest.su_port;
843			p[0] = $39.i; p[1] = $41.i;
844			a = (char *)&data_dest.su_sin6.sin6_addr;
845			a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
846			a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
847			a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
848			a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
849			if (his_addr.su_family == AF_INET6) {
850				/* XXX more sanity checks! */
851				data_dest.su_sin6.sin6_scope_id =
852					his_addr.su_sin6.sin6_scope_id;
853			}
854			if ($1.i != 6 || $3.i != 16 || $37.i != 2)
855				memset(&data_dest, 0, sizeof(data_dest));
856		}
857	| NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
858		NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
859		NUMBER
860		{
861			char *a, *p;
862
863			memset(&data_dest, 0, sizeof(data_dest));
864			data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
865			data_dest.su_family = AF_INET;
866			p = (char *)&data_dest.su_port;
867			p[0] = $15.i; p[1] = $17.i;
868			a = (char *)&data_dest.su_sin.sin_addr;
869			a[0] =  $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
870			if ($1.i != 4 || $3.i != 4 || $13.i != 2)
871				memset(&data_dest, 0, sizeof(data_dest));
872		}
873	;
874
875form_code
876	: N
877		{
878			$$ = FORM_N;
879		}
880	| T
881		{
882			$$ = FORM_T;
883		}
884	| C
885		{
886			$$ = FORM_C;
887		}
888	;
889
890type_code
891	: A
892		{
893			cmd_type = TYPE_A;
894			cmd_form = FORM_N;
895		}
896	| A SP form_code
897		{
898			cmd_type = TYPE_A;
899			cmd_form = $3;
900		}
901	| E
902		{
903			cmd_type = TYPE_E;
904			cmd_form = FORM_N;
905		}
906	| E SP form_code
907		{
908			cmd_type = TYPE_E;
909			cmd_form = $3;
910		}
911	| I
912		{
913			cmd_type = TYPE_I;
914		}
915	| L
916		{
917			cmd_type = TYPE_L;
918			cmd_bytesz = CHAR_BIT;
919		}
920	| L SP byte_size
921		{
922			cmd_type = TYPE_L;
923			cmd_bytesz = $3;
924		}
925		/* this is for a bug in the BBN ftp */
926	| L byte_size
927		{
928			cmd_type = TYPE_L;
929			cmd_bytesz = $2;
930		}
931	;
932
933struct_code
934	: F
935		{
936			$$ = STRU_F;
937		}
938	| R
939		{
940			$$ = STRU_R;
941		}
942	| P
943		{
944			$$ = STRU_P;
945		}
946	;
947
948mode_code
949	: S
950		{
951			$$ = MODE_S;
952		}
953	| B
954		{
955			$$ = MODE_B;
956		}
957	| C
958		{
959			$$ = MODE_C;
960		}
961	;
962
963pathname
964	: pathstring
965		{
966			/*
967			 * Problem: this production is used for all pathname
968			 * processing, but only gives a 550 error reply.
969			 * This is a valid reply in some cases but not in others.
970			 */
971			if (logged_in && $1) {
972				glob_t gl;
973				char *p, **pp;
974				int flags =
975				 GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
976				int n;
977
978				memset(&gl, 0, sizeof(gl));
979				flags |= GLOB_LIMIT;
980				gl.gl_matchc = MAXGLOBARGS;
981				if (glob($1, flags, NULL, &gl) ||
982				    gl.gl_pathc == 0) {
983					reply(550, "wildcard expansion error");
984					$$ = NULL;
985				} else {
986					n = 0;
987					for (pp = gl.gl_pathv; *pp; pp++)
988						if (strcspn(*pp, "\r\n") ==
989						    strlen(*pp)) {
990							p = *pp;
991							n++;
992						}
993					if (n == 0)
994						$$ = strdup($1);
995					else if (n == 1)
996						$$ = strdup(p);
997					else {
998						reply(550, "ambiguous");
999						$$ = NULL;
1000					}
1001				}
1002				globfree(&gl);
1003				free($1);
1004			} else
1005				$$ = $1;
1006		}
1007	;
1008
1009pathstring
1010	: STRING
1011	;
1012
1013octal_number
1014	: NUMBER
1015		{
1016			int ret, dec, multby, digit;
1017
1018			/*
1019			 * Convert a number that was read as decimal number
1020			 * to what it would be if it had been read as octal.
1021			 */
1022			dec = $1.i;
1023			multby = 1;
1024			ret = 0;
1025			while (dec) {
1026				digit = dec%10;
1027				if (digit > 7) {
1028					ret = -1;
1029					break;
1030				}
1031				ret += digit * multby;
1032				multby *= 8;
1033				dec /= 10;
1034			}
1035			$$ = ret;
1036		}
1037	;
1038
1039
1040check_login
1041	: /* empty */
1042		{
1043		$$ = check_login1();
1044		}
1045	;
1046
1047check_login_epsv
1048	: /* empty */
1049		{
1050		if (noepsv) {
1051			reply(500, "EPSV command disabled");
1052			$$ = 0;
1053		}
1054		else
1055			$$ = check_login1();
1056		}
1057	;
1058
1059check_login_ro
1060	: /* empty */
1061		{
1062		if (readonly) {
1063			reply(550, "Permission denied.");
1064			$$ = 0;
1065		}
1066		else
1067			$$ = check_login1();
1068		}
1069	;
1070
1071%%
1072
1073#define	CMD	0	/* beginning of command */
1074#define	ARGS	1	/* expect miscellaneous arguments */
1075#define	STR1	2	/* expect SP followed by STRING */
1076#define	STR2	3	/* expect STRING */
1077#define	OSTR	4	/* optional SP then STRING */
1078#define	ZSTR1	5	/* optional SP then optional STRING */
1079#define	ZSTR2	6	/* optional STRING after SP */
1080#define	SITECMD	7	/* SITE command */
1081#define	NSTR	8	/* Number followed by a string */
1082
1083#define	MAXGLOBARGS	1000
1084
1085#define	MAXASIZE	10240	/* Deny ASCII SIZE on files larger than that */
1086
1087struct tab {
1088	char	*name;
1089	short	token;
1090	short	state;
1091	short	implemented;	/* 1 if command is implemented */
1092	char	*help;
1093};
1094
1095struct tab cmdtab[] = {		/* In order defined in RFC 765 */
1096	{ "USER", USER, STR1, 1,	"<sp> username" },
1097	{ "PASS", PASS, ZSTR1, 1,	"[<sp> [password]]" },
1098	{ "ACCT", ACCT, STR1, 0,	"(specify account)" },
1099	{ "SMNT", SMNT, ARGS, 0,	"(structure mount)" },
1100	{ "REIN", REIN, ARGS, 0,	"(reinitialize server state)" },
1101	{ "QUIT", QUIT, ARGS, 1,	"(terminate service)", },
1102	{ "PORT", PORT, ARGS, 1,	"<sp> b0, b1, b2, b3, b4, b5" },
1103	{ "LPRT", LPRT, ARGS, 1,	"<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1104	{ "EPRT", EPRT, STR1, 1,	"<sp> |af|addr|port|" },
1105	{ "PASV", PASV, ARGS, 1,	"(set server in passive mode)" },
1106	{ "LPSV", LPSV, ARGS, 1,	"(set server in passive mode)" },
1107	{ "EPSV", EPSV, ARGS, 1,	"[<sp> af|ALL]" },
1108	{ "TYPE", TYPE, ARGS, 1,	"<sp> { A | E | I | L }" },
1109	{ "STRU", STRU, ARGS, 1,	"(specify file structure)" },
1110	{ "MODE", MODE, ARGS, 1,	"(specify transfer mode)" },
1111	{ "RETR", RETR, STR1, 1,	"<sp> file-name" },
1112	{ "STOR", STOR, STR1, 1,	"<sp> file-name" },
1113	{ "APPE", APPE, STR1, 1,	"<sp> file-name" },
1114	{ "MLFL", MLFL, OSTR, 0,	"(mail file)" },
1115	{ "MAIL", MAIL, OSTR, 0,	"(mail to user)" },
1116	{ "MSND", MSND, OSTR, 0,	"(mail send to terminal)" },
1117	{ "MSOM", MSOM, OSTR, 0,	"(mail send to terminal or mailbox)" },
1118	{ "MSAM", MSAM, OSTR, 0,	"(mail send to terminal and mailbox)" },
1119	{ "MRSQ", MRSQ, OSTR, 0,	"(mail recipient scheme question)" },
1120	{ "MRCP", MRCP, STR1, 0,	"(mail recipient)" },
1121	{ "ALLO", ALLO, ARGS, 1,	"allocate storage (vacuously)" },
1122	{ "REST", REST, ARGS, 1,	"<sp> offset (restart command)" },
1123	{ "RNFR", RNFR, STR1, 1,	"<sp> file-name" },
1124	{ "RNTO", RNTO, STR1, 1,	"<sp> file-name" },
1125	{ "ABOR", ABOR, ARGS, 1,	"(abort operation)" },
1126	{ "DELE", DELE, STR1, 1,	"<sp> file-name" },
1127	{ "CWD",  CWD,  OSTR, 1,	"[ <sp> directory-name ]" },
1128	{ "XCWD", CWD,	OSTR, 1,	"[ <sp> directory-name ]" },
1129	{ "LIST", LIST, OSTR, 1,	"[ <sp> path-name ]" },
1130	{ "NLST", NLST, OSTR, 1,	"[ <sp> path-name ]" },
1131	{ "SITE", SITE, SITECMD, 1,	"site-cmd [ <sp> arguments ]" },
1132	{ "SYST", SYST, ARGS, 1,	"(get type of operating system)" },
1133	{ "STAT", STAT, OSTR, 1,	"[ <sp> path-name ]" },
1134	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
1135	{ "NOOP", NOOP, ARGS, 1,	"" },
1136	{ "MKD",  MKD,  STR1, 1,	"<sp> path-name" },
1137	{ "XMKD", MKD,  STR1, 1,	"<sp> path-name" },
1138	{ "RMD",  RMD,  STR1, 1,	"<sp> path-name" },
1139	{ "XRMD", RMD,  STR1, 1,	"<sp> path-name" },
1140	{ "PWD",  PWD,  ARGS, 1,	"(return current directory)" },
1141	{ "XPWD", PWD,  ARGS, 1,	"(return current directory)" },
1142	{ "CDUP", CDUP, ARGS, 1,	"(change to parent directory)" },
1143	{ "XCUP", CDUP, ARGS, 1,	"(change to parent directory)" },
1144	{ "STOU", STOU, STR1, 1,	"<sp> file-name" },
1145	{ "SIZE", SIZE, OSTR, 1,	"<sp> path-name" },
1146	{ "MDTM", MDTM, OSTR, 1,	"<sp> path-name" },
1147	{ NULL,   0,    0,    0,	0 }
1148};
1149
1150struct tab sitetab[] = {
1151	{ "MD5", MDFIVE, STR1, 1,	"[ <sp> file-name ]" },
1152	{ "UMASK", UMASK, ARGS, 1,	"[ <sp> umask ]" },
1153	{ "IDLE", IDLE, ARGS, 1,	"[ <sp> maximum-idle-time ]" },
1154	{ "CHMOD", CHMOD, NSTR, 1,	"<sp> mode <sp> file-name" },
1155	{ "HELP", HELP, OSTR, 1,	"[ <sp> <string> ]" },
1156	{ NULL,   0,    0,    0,	0 }
1157};
1158
1159static char	*copy(char *);
1160static void	 help(struct tab *, char *);
1161static struct tab *
1162		 lookup(struct tab *, char *);
1163static int	 port_check(const char *);
1164static int	 port_check_v6(const char *);
1165static void	 sizecmd(char *);
1166static void	 toolong(int);
1167static void	 v4map_data_dest(void);
1168static int	 yylex(void);
1169
1170static struct tab *
1171lookup(struct tab *p, char *cmd)
1172{
1173
1174	for (; p->name != NULL; p++)
1175		if (strcmp(cmd, p->name) == 0)
1176			return (p);
1177	return (0);
1178}
1179
1180#include <arpa/telnet.h>
1181
1182/*
1183 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1184 */
1185char *
1186getline(char *s, int n, FILE *iop)
1187{
1188	int c;
1189	register char *cs;
1190
1191	cs = s;
1192/* tmpline may contain saved command from urgent mode interruption */
1193	for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1194		*cs++ = tmpline[c];
1195		if (tmpline[c] == '\n') {
1196			*cs++ = '\0';
1197			if (ftpdebug)
1198				syslog(LOG_DEBUG, "command: %s", s);
1199			tmpline[0] = '\0';
1200			return(s);
1201		}
1202		if (c == 0)
1203			tmpline[0] = '\0';
1204	}
1205	while ((c = getc(iop)) != EOF) {
1206		c &= 0377;
1207		if (c == IAC) {
1208		    if ((c = getc(iop)) != EOF) {
1209			c &= 0377;
1210			switch (c) {
1211			case WILL:
1212			case WONT:
1213				c = getc(iop);
1214				printf("%c%c%c", IAC, DONT, 0377&c);
1215				(void) fflush(stdout);
1216				continue;
1217			case DO:
1218			case DONT:
1219				c = getc(iop);
1220				printf("%c%c%c", IAC, WONT, 0377&c);
1221				(void) fflush(stdout);
1222				continue;
1223			case IAC:
1224				break;
1225			default:
1226				continue;	/* ignore command */
1227			}
1228		    }
1229		}
1230		*cs++ = c;
1231		if (--n <= 0 || c == '\n')
1232			break;
1233	}
1234	if (c == EOF && cs == s)
1235		return (NULL);
1236	*cs++ = '\0';
1237	if (ftpdebug) {
1238		if (!guest && strncasecmp("pass ", s, 5) == 0) {
1239			/* Don't syslog passwords */
1240			syslog(LOG_DEBUG, "command: %.5s ???", s);
1241		} else {
1242			register char *cp;
1243			register int len;
1244
1245			/* Don't syslog trailing CR-LF */
1246			len = strlen(s);
1247			cp = s + len - 1;
1248			while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1249				--cp;
1250				--len;
1251			}
1252			syslog(LOG_DEBUG, "command: %.*s", len, s);
1253		}
1254	}
1255	return (s);
1256}
1257
1258static void
1259toolong(int signo)
1260{
1261
1262	reply(421,
1263	    "Timeout (%d seconds): closing control connection.", timeout);
1264	if (logging)
1265		syslog(LOG_INFO, "User %s timed out after %d seconds",
1266		    (pw ? pw -> pw_name : "unknown"), timeout);
1267	dologout(1);
1268}
1269
1270static int
1271yylex(void)
1272{
1273	static int cpos;
1274	char *cp, *cp2;
1275	struct tab *p;
1276	int n;
1277	char c;
1278
1279	for (;;) {
1280		switch (state) {
1281
1282		case CMD:
1283			(void) signal(SIGALRM, toolong);
1284			(void) alarm((unsigned) timeout);
1285			if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1286				reply(221, "You could at least say goodbye.");
1287				dologout(0);
1288			}
1289			(void) alarm(0);
1290#ifdef SETPROCTITLE
1291			if (strncasecmp(cbuf, "PASS", 4) != 0)
1292				setproctitle("%s: %s", proctitle, cbuf);
1293#endif /* SETPROCTITLE */
1294			if ((cp = strchr(cbuf, '\r'))) {
1295				*cp++ = '\n';
1296				*cp = '\0';
1297			}
1298			if ((cp = strpbrk(cbuf, " \n")))
1299				cpos = cp - cbuf;
1300			if (cpos == 0)
1301				cpos = 4;
1302			c = cbuf[cpos];
1303			cbuf[cpos] = '\0';
1304			upper(cbuf);
1305			p = lookup(cmdtab, cbuf);
1306			cbuf[cpos] = c;
1307			if (p != 0) {
1308				yylval.s = p->name;
1309				if (!p->implemented)
1310					return (NOTIMPL); /* state remains CMD */
1311				state = p->state;
1312				return (p->token);
1313			}
1314			break;
1315
1316		case SITECMD:
1317			if (cbuf[cpos] == ' ') {
1318				cpos++;
1319				return (SP);
1320			}
1321			cp = &cbuf[cpos];
1322			if ((cp2 = strpbrk(cp, " \n")))
1323				cpos = cp2 - cbuf;
1324			c = cbuf[cpos];
1325			cbuf[cpos] = '\0';
1326			upper(cp);
1327			p = lookup(sitetab, cp);
1328			cbuf[cpos] = c;
1329			if (guest == 0 && p != 0) {
1330				yylval.s = p->name;
1331				if (!p->implemented) {
1332					state = CMD;
1333					return (NOTIMPL);
1334				}
1335				state = p->state;
1336				return (p->token);
1337			}
1338			state = CMD;
1339			break;
1340
1341		case ZSTR1:
1342		case OSTR:
1343			if (cbuf[cpos] == '\n') {
1344				state = CMD;
1345				return (CRLF);
1346			}
1347			/* FALLTHROUGH */
1348
1349		case STR1:
1350		dostr1:
1351			if (cbuf[cpos] == ' ') {
1352				cpos++;
1353				state = state == OSTR ? STR2 : state+1;
1354				return (SP);
1355			}
1356			break;
1357
1358		case ZSTR2:
1359			if (cbuf[cpos] == '\n') {
1360				state = CMD;
1361				return (CRLF);
1362			}
1363			/* FALLTHROUGH */
1364
1365		case STR2:
1366			cp = &cbuf[cpos];
1367			n = strlen(cp);
1368			cpos += n - 1;
1369			/*
1370			 * Make sure the string is nonempty and \n terminated.
1371			 */
1372			if (n > 1 && cbuf[cpos] == '\n') {
1373				cbuf[cpos] = '\0';
1374				yylval.s = copy(cp);
1375				cbuf[cpos] = '\n';
1376				state = ARGS;
1377				return (STRING);
1378			}
1379			break;
1380
1381		case NSTR:
1382			if (cbuf[cpos] == ' ') {
1383				cpos++;
1384				return (SP);
1385			}
1386			if (isdigit(cbuf[cpos])) {
1387				cp = &cbuf[cpos];
1388				while (isdigit(cbuf[++cpos]))
1389					;
1390				c = cbuf[cpos];
1391				cbuf[cpos] = '\0';
1392				yylval.u.i = atoi(cp);
1393				cbuf[cpos] = c;
1394				state = STR1;
1395				return (NUMBER);
1396			}
1397			state = STR1;
1398			goto dostr1;
1399
1400		case ARGS:
1401			if (isdigit(cbuf[cpos])) {
1402				cp = &cbuf[cpos];
1403				while (isdigit(cbuf[++cpos]))
1404					;
1405				c = cbuf[cpos];
1406				cbuf[cpos] = '\0';
1407				yylval.u.i = atoi(cp);
1408				yylval.u.o = strtoull(cp, (char **)NULL, 10);
1409				cbuf[cpos] = c;
1410				return (NUMBER);
1411			}
1412			if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1413			 && !isalnum(cbuf[cpos + 3])) {
1414				cpos += 3;
1415				return ALL;
1416			}
1417			switch (cbuf[cpos++]) {
1418
1419			case '\n':
1420				state = CMD;
1421				return (CRLF);
1422
1423			case ' ':
1424				return (SP);
1425
1426			case ',':
1427				return (COMMA);
1428
1429			case 'A':
1430			case 'a':
1431				return (A);
1432
1433			case 'B':
1434			case 'b':
1435				return (B);
1436
1437			case 'C':
1438			case 'c':
1439				return (C);
1440
1441			case 'E':
1442			case 'e':
1443				return (E);
1444
1445			case 'F':
1446			case 'f':
1447				return (F);
1448
1449			case 'I':
1450			case 'i':
1451				return (I);
1452
1453			case 'L':
1454			case 'l':
1455				return (L);
1456
1457			case 'N':
1458			case 'n':
1459				return (N);
1460
1461			case 'P':
1462			case 'p':
1463				return (P);
1464
1465			case 'R':
1466			case 'r':
1467				return (R);
1468
1469			case 'S':
1470			case 's':
1471				return (S);
1472
1473			case 'T':
1474			case 't':
1475				return (T);
1476
1477			}
1478			break;
1479
1480		default:
1481			fatalerror("Unknown state in scanner.");
1482		}
1483		state = CMD;
1484		return (LEXERR);
1485	}
1486}
1487
1488void
1489upper(char *s)
1490{
1491	while (*s != '\0') {
1492		if (islower(*s))
1493			*s = toupper(*s);
1494		s++;
1495	}
1496}
1497
1498static char *
1499copy(char *s)
1500{
1501	char *p;
1502
1503	p = malloc((unsigned) strlen(s) + 1);
1504	if (p == NULL)
1505		fatalerror("Ran out of memory.");
1506	(void) strcpy(p, s);
1507	return (p);
1508}
1509
1510static void
1511help(struct tab *ctab, char *s)
1512{
1513	struct tab *c;
1514	int width, NCMDS;
1515	char *type;
1516
1517	if (ctab == sitetab)
1518		type = "SITE ";
1519	else
1520		type = "";
1521	width = 0, NCMDS = 0;
1522	for (c = ctab; c->name != NULL; c++) {
1523		int len = strlen(c->name);
1524
1525		if (len > width)
1526			width = len;
1527		NCMDS++;
1528	}
1529	width = (width + 8) &~ 7;
1530	if (s == 0) {
1531		int i, j, w;
1532		int columns, lines;
1533
1534		lreply(214, "The following %scommands are recognized %s.",
1535		    type, "(* =>'s unimplemented)");
1536		columns = 76 / width;
1537		if (columns == 0)
1538			columns = 1;
1539		lines = (NCMDS + columns - 1) / columns;
1540		for (i = 0; i < lines; i++) {
1541			printf("   ");
1542			for (j = 0; j < columns; j++) {
1543				c = ctab + j * lines + i;
1544				printf("%s%c", c->name,
1545					c->implemented ? ' ' : '*');
1546				if (c + lines >= &ctab[NCMDS])
1547					break;
1548				w = strlen(c->name) + 1;
1549				while (w < width) {
1550					putchar(' ');
1551					w++;
1552				}
1553			}
1554			printf("\r\n");
1555		}
1556		(void) fflush(stdout);
1557		reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1558		return;
1559	}
1560	upper(s);
1561	c = lookup(ctab, s);
1562	if (c == (struct tab *)0) {
1563		reply(502, "Unknown command %s.", s);
1564		return;
1565	}
1566	if (c->implemented)
1567		reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1568	else
1569		reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1570		    c->name, c->help);
1571}
1572
1573static void
1574sizecmd(char *filename)
1575{
1576	switch (type) {
1577	case TYPE_L:
1578	case TYPE_I: {
1579		struct stat stbuf;
1580		if (stat(filename, &stbuf) < 0)
1581			perror_reply(550, filename);
1582		else if (!S_ISREG(stbuf.st_mode))
1583			reply(550, "%s: not a plain file.", filename);
1584		else
1585			reply(213, "%qu", stbuf.st_size);
1586		break; }
1587	case TYPE_A: {
1588		FILE *fin;
1589		int c;
1590		off_t count;
1591		struct stat stbuf;
1592		fin = fopen(filename, "r");
1593		if (fin == NULL) {
1594			perror_reply(550, filename);
1595			return;
1596		}
1597		if (fstat(fileno(fin), &stbuf) < 0) {
1598			perror_reply(550, filename);
1599			(void) fclose(fin);
1600			return;
1601		} else if (!S_ISREG(stbuf.st_mode)) {
1602			reply(550, "%s: not a plain file.", filename);
1603			(void) fclose(fin);
1604			return;
1605		} else if (stbuf.st_size > MAXASIZE) {
1606			reply(550, "%s: too large for type A SIZE.", filename);
1607			(void) fclose(fin);
1608			return;
1609		}
1610
1611		count = 0;
1612		while((c=getc(fin)) != EOF) {
1613			if (c == '\n')	/* will get expanded to \r\n */
1614				count++;
1615			count++;
1616		}
1617		(void) fclose(fin);
1618
1619		reply(213, "%qd", count);
1620		break; }
1621	default:
1622		reply(504, "SIZE not implemented for type %s.",
1623		           typenames[type]);
1624	}
1625}
1626
1627/* Return 1, if port check is done. Return 0, if not yet. */
1628static int
1629port_check(const char *pcmd)
1630{
1631	if (his_addr.su_family == AF_INET) {
1632		if (data_dest.su_family != AF_INET) {
1633			usedefault = 1;
1634			reply(500, "Invalid address rejected.");
1635			return 1;
1636		}
1637		if (paranoid &&
1638		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1639		     memcmp(&data_dest.su_sin.sin_addr,
1640			    &his_addr.su_sin.sin_addr,
1641			    sizeof(data_dest.su_sin.sin_addr)))) {
1642			usedefault = 1;
1643			reply(500, "Illegal PORT range rejected.");
1644		} else {
1645			usedefault = 0;
1646			if (pdata >= 0) {
1647				(void) close(pdata);
1648				pdata = -1;
1649			}
1650			reply(200, "%s command successful.", pcmd);
1651		}
1652		return 1;
1653	}
1654	return 0;
1655}
1656
1657static int
1658check_login1(void)
1659{
1660	if (logged_in)
1661		return 1;
1662	else {
1663		reply(530, "Please login with USER and PASS.");
1664		return 0;
1665	}
1666}
1667
1668#ifdef INET6
1669/* Return 1, if port check is done. Return 0, if not yet. */
1670static int
1671port_check_v6(const char *pcmd)
1672{
1673	if (his_addr.su_family == AF_INET6) {
1674		if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1675			/* Convert data_dest into v4 mapped sockaddr.*/
1676			v4map_data_dest();
1677		if (data_dest.su_family != AF_INET6) {
1678			usedefault = 1;
1679			reply(500, "Invalid address rejected.");
1680			return 1;
1681		}
1682		if (paranoid &&
1683		    ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1684		     memcmp(&data_dest.su_sin6.sin6_addr,
1685			    &his_addr.su_sin6.sin6_addr,
1686			    sizeof(data_dest.su_sin6.sin6_addr)))) {
1687			usedefault = 1;
1688			reply(500, "Illegal PORT range rejected.");
1689		} else {
1690			usedefault = 0;
1691			if (pdata >= 0) {
1692				(void) close(pdata);
1693				pdata = -1;
1694			}
1695			reply(200, "%s command successful.", pcmd);
1696		}
1697		return 1;
1698	}
1699	return 0;
1700}
1701
1702static void
1703v4map_data_dest(void)
1704{
1705	struct in_addr savedaddr;
1706	int savedport;
1707
1708	if (data_dest.su_family != AF_INET) {
1709		usedefault = 1;
1710		reply(500, "Invalid address rejected.");
1711		return;
1712	}
1713
1714	savedaddr = data_dest.su_sin.sin_addr;
1715	savedport = data_dest.su_port;
1716
1717	memset(&data_dest, 0, sizeof(data_dest));
1718	data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1719	data_dest.su_sin6.sin6_family = AF_INET6;
1720	data_dest.su_sin6.sin6_port = savedport;
1721	memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1722	memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1723	       (caddr_t)&savedaddr, sizeof(savedaddr));
1724}
1725#endif
1726