File:  [DragonFly] / src / libexec / ftpd / ftpcmd.y
Revision 1.2: download - view: text, annotated - select for diffs
Tue Jun 17 04:27:07 2003 UTC (10 years, 10 months ago) by dillon
Branches: MAIN
CVS tags: HEAD
Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids.  Most
ids have been removed from !lint sections and moved into comment sections.

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