ftpcmd.y revision 22347
1262685Sdelphij/* ftpcmd.y: yacc parser for the FTP daemon. 262449Speter 3262685Sdelphij%%% portions-copyright-cmetz 4166124SrafanPortions of this software are Copyright 1996 by Craig Metz, All Rights 5262685SdelphijReserved. The Inner Net License Version 2 applies to these portions of 6166124Srafanthe software. 7166124SrafanYou should have received a copy of the license with this software. If 8166124Srafanyou didn't get a copy, you may request one from <license@inner.net>. 9166124Srafan 10166124Srafan History: 11166124Srafan 12166124Srafan Modified by cmetz for OPIE 2.3. Moved LS_COMMAND here. 13166124Srafan Modified by cmetz for OPIE 2.2. Fixed a *lot* of warnings. 14166124Srafan Use FUNCTION declaration et al. Removed useless strings. 15166124Srafan Changed some char []s to char *s. Deleted comment address. 16166124Srafan Changed tmpline references to be more pure-pointer 17166124Srafan references. Changed tmpline declaration back to char []. 18166124Srafan Modified at NRL for OPIE 2.1. Minor changes for autoconf. 19166124Srafan Modified at NRL for OPIE 2.01. Added forward declaration for sitetab[] 20166124Srafan -- fixes problems experienced by bison users. Merged in new 21166124Srafan PORT attack fixes from Hobbit. 22166124Srafan Modified at NRL for OPIE 2.0. 23166124Srafan Originally from BSD. 24166124Srafan*/ 25166124Srafan/* 26166124Srafan * Copyright (c) 1985, 1988 Regents of the University of California. 27166124Srafan * All rights reserved. 28166124Srafan * 29166124Srafan * Redistribution and use in source and binary forms, with or without 30166124Srafan * modification, are permitted provided that the following conditions 3162449Speter * are met: 3262449Speter * 1. Redistributions of source code must retain the above copyright 3362449Speter * notice, this list of conditions and the following disclaimer. 3462449Speter * 2. Redistributions in binary form must reproduce the above copyright 3562449Speter * notice, this list of conditions and the following disclaimer in the 36166124Srafan * documentation and/or other materials provided with the distribution. 3762449Speter * 3. All advertising materials mentioning features or use of this software 3862449Speter * must display the following acknowledgement: 3962449Speter * This product includes software developed by the University of 4062449Speter * California, Berkeley and its contributors. 4162449Speter * 4. Neither the name of the University nor the names of its contributors 4262449Speter * may be used to endorse or promote products derived from this software 4362449Speter * without specific prior written permission. 4462449Speter * 4562449Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 4662449Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 4762449Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 4862449Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 4962449Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5062449Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5162449Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 5262449Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 5362449Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 5462449Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 5562449Speter * SUCH DAMAGE. 5662449Speter * 5762449Speter * @(#)ftpcmd.y 5.24 (Berkeley) 2/25/91 5862449Speter */ 5962449Speter 6062449Speter/* 6162449Speter * Grammar for FTP commands. 6262449Speter * See RFC 959. 6362449Speter */ 6462449Speter 6562449Speter%{ 6662449Speter#include "opie_cfg.h" 6762449Speter 6862449Speter#include <sys/param.h> 6962449Speter#include <sys/types.h> 7062449Speter#include <sys/socket.h> 7162449Speter#include <sys/stat.h> 7262449Speter#include <netinet/in.h> 7362449Speter#include <arpa/ftp.h> 7462449Speter#include <signal.h> 7562449Speter#include <setjmp.h> 7662449Speter#include <syslog.h> 7762449Speter#if TM_IN_SYS_TIME 7862449Speter#include <sys/time.h> 7962449Speter#else /* TM_IN_SYS_TIME */ 8062449Speter#include <time.h> 8162449Speter#endif /* TM_IN_SYS_TIME */ 8262449Speter#include <pwd.h> 8362449Speter#include <unistd.h> 8462449Speter#include <stdio.h> 8562449Speter#include <ctype.h> 8662449Speter#include <stdlib.h> 8762449Speter#include <string.h> 8862449Speter 8962449Speter#include "opie.h" 9062449Speter 9162449Speter#if HAVE_LS_G_FLAG 9262449Speter#define LS_COMMAND "/bin/ls -lgA" 9362449Speter#else /* HAVE_LS_G_FLAG */ 9462449Speter#define LS_COMMAND "/bin/ls -lA" 9562449Speter#endif /* HAVE_LS_G_FLAG */ 9662449Speter 9762449Speterextern struct sockaddr_in data_dest; 9862449Speterextern struct sockaddr_in his_addr; 9962449Speterextern int logged_in; 10062449Speterextern struct passwd *pw; 10162449Speterextern int guest; 10262449Speterextern int type; 10362449Speterextern int form; 10462449Speterextern int debug; 10562449Speterextern int timeout; 10662449Speterextern int maxtimeout; 10762449Speterextern int pdata; 10862449Speterextern char *remotehost; 10962449Speterextern char *proctitle; 11062449Speterextern char *globerr; 11162449Speterextern int usedefault; 11262449Speterextern int transflag; 11362449Speterextern char tmpline[]; 11462449Speterchar **ftpglob(); 11562449Speter 11662449SpeterVOIDRET dologout __P((int)); 11762449SpeterVOIDRET upper __P((char *)); 11862449SpeterVOIDRET nack __P((char *)); 11962449SpeterVOIDRET opiefatal __P((char *)); 12062449Speter 12162449SpeterVOIDRET pass __P((char *)); 12262449Speterint user __P((char *)); 12362449SpeterVOIDRET passive __P((void)); 12462449SpeterVOIDRET retrieve __P((char *, char *)); 12562449SpeterVOIDRET store __P((char *, char *, int)); 12662449SpeterVOIDRET send_file_list __P((char *)); 12762449SpeterVOIDRET statfilecmd __P((char *)); 12862449SpeterVOIDRET statcmd __P((void)); 12962449SpeterVOIDRET delete __P((char *)); 13062449SpeterVOIDRET renamecmd __P((char *, char *)); 13162449SpeterVOIDRET cwd __P((char *)); 13262449SpeterVOIDRET makedir __P((char *)); 13362449SpeterVOIDRET removedir __P((char *)); 13462449SpeterVOIDRET pwd __P((void)); 13562449Speter 13662449SpeterVOIDRET sizecmd __P((char *)); 13762449Speter 13862449Speteroff_t restart_point; 13962449Speter 14062449Speterstatic int cmd_type; 14162449Speterstatic int cmd_form; 14262449Speterstatic int cmd_bytesz; 14362449Speterstatic unsigned short cliport = 0; 14462449Speterchar cbuf[512]; 14562449Speterchar *fromname; 14662449Speter 14762449Speterstruct tab { 14862449Speter char *name; 14962449Speter short token; 15062449Speter short state; 15162449Speter short implemented; /* 1 if command is implemented */ 15262449Speter char *help; 15362449Speter}; 15462449Speter 15562449SpeterVOIDRET help __P((struct tab *, char *)); 15662449Speter 15762449Speterstruct tab cmdtab[], sitetab[]; 15862449Speter 15962449Speter%} 16062449Speter 16162449Speter%token 16262449Speter A B C E F I 16362449Speter L N P R S T 16462449Speter 16562449Speter SP CRLF COMMA STRING NUMBER 16662449Speter 16762449Speter USER PASS ACCT REIN QUIT PORT 16862449Speter PASV TYPE STRU MODE RETR STOR 16962449Speter APPE MLFL MAIL MSND MSOM MSAM 17062449Speter MRSQ MRCP ALLO REST RNFR RNTO 17162449Speter ABOR DELE CWD LIST NLST SITE 17262449Speter STAT HELP NOOP MKD RMD PWD 17362449Speter CDUP STOU SMNT SYST SIZE MDTM 17462449Speter 17562449Speter UMASK IDLE CHMOD 17662449Speter 17762449Speter LEXERR 17862449Speter 17962449Speter%start cmd_list 18062449Speter 18162449Speter%% 18262449Speter 18362449Spetercmd_list: /* empty */ 18462449Speter | cmd_list cmd 18562449Speter = { 18662449Speter fromname = (char *) 0; 18762449Speter restart_point = (off_t) 0; 18862449Speter } 18962449Speter | cmd_list rcmd 19062449Speter ; 19162449Speter 19262449Spetercmd: USER SP username CRLF 19362449Speter = { 19462449Speter user((char *) $3); 19562449Speter free((char *) $3); 19662449Speter } 19762449Speter | PASS SP password CRLF 19862449Speter = { 19962449Speter pass((char *) $3); 20062449Speter free((char *) $3); 20162449Speter } 20262449Speter | PORT check_login SP host_port CRLF 20362449Speter = { 20462449Speter usedefault = 0; 20562449Speter if (pdata >= 0) { 20662449Speter (void) close(pdata); 20762449Speter pdata = -1; 20862449Speter } 20962449Speter/* H* port fix, part B: admonish the twit. 21062449Speter Also require login before PORT works */ 21162449Speter if ($2) { 21262449Speter if ((cliport > 1023) && (data_dest.sin_addr.s_addr > 0)) { 21362449Speter reply(200, "PORT command successful."); 21462449Speter } else { 21562449Speter syslog (LOG_WARNING, "refused %s from %s", 21662449Speter cbuf, remotehost); 21762449Speter reply(500, "You've GOT to be joking."); 21862449Speter } 21962449Speter } 22062449Speter } 22162449Speter/* | PASV CRLF 22262449Speter = { 22362449Speter passive(); 22462449Speter } */ 22562449Speter | PASV check_login CRLF 22662449Speter = { 22762449Speter/* Require login for PASV, too. This actually fixes a bug -- telnet to an 22862449Speter unfixed wu-ftpd and type PASV first off, and it crashes! */ 22962449Speter if ($2) { 23062449Speter passive(); 23162449Speter } 23262449Speter } 23362449Speter | TYPE SP type_code CRLF 23462449Speter = { 23562449Speter switch (cmd_type) { 23662449Speter 23762449Speter case TYPE_A: 23862449Speter if (cmd_form == FORM_N) { 23962449Speter reply(200, "Type set to A."); 24062449Speter type = cmd_type; 24162449Speter form = cmd_form; 24262449Speter } else 24362449Speter reply(504, "Form must be N."); 24462449Speter break; 24562449Speter 24662449Speter case TYPE_E: 24762449Speter reply(504, "Type E not implemented."); 24862449Speter break; 24962449Speter 25062449Speter case TYPE_I: 25162449Speter reply(200, "Type set to I."); 25262449Speter type = cmd_type; 25362449Speter break; 25462449Speter 25562449Speter case TYPE_L: 25662449Speter#if NBBY == 8 25762449Speter if (cmd_bytesz == 8) { 25862449Speter reply(200, 25962449Speter "Type set to L (byte size 8)."); 26062449Speter type = cmd_type; 26162449Speter } else 26262449Speter reply(504, "Byte size must be 8."); 26362449Speter#else /* NBBY == 8 */ 26462449Speter UNIMPLEMENTED for NBBY != 8 26562449Speter#endif /* NBBY == 8 */ 26662449Speter } 26762449Speter } 26862449Speter | STRU SP struct_code CRLF 26962449Speter = { 27062449Speter switch ($3) { 27162449Speter 27262449Speter case STRU_F: 27362449Speter reply(200, "STRU F ok."); 27462449Speter break; 27562449Speter 27662449Speter default: 27762449Speter reply(504, "Unimplemented STRU type."); 27862449Speter } 27962449Speter } 28062449Speter | MODE SP mode_code CRLF 28162449Speter = { 28262449Speter switch ($3) { 28362449Speter 28462449Speter case MODE_S: 28562449Speter reply(200, "MODE S ok."); 28662449Speter break; 28762449Speter 28862449Speter default: 28962449Speter reply(502, "Unimplemented MODE type."); 29062449Speter } 29162449Speter } 29262449Speter | ALLO SP NUMBER CRLF 29362449Speter = { 29462449Speter reply(202, "ALLO command ignored."); 29562449Speter } 29662449Speter | ALLO SP NUMBER SP R SP NUMBER CRLF 29762449Speter = { 29862449Speter reply(202, "ALLO command ignored."); 29962449Speter } 30062449Speter | RETR check_login SP pathname CRLF 30162449Speter = { 30262449Speter if ($2 && $4) 30362449Speter retrieve((char *) 0, (char *) $4); 30462449Speter if ($4) 30562449Speter free((char *) $4); 30662449Speter } 30762449Speter | STOR check_login SP pathname CRLF 30862449Speter = { 30962449Speter if ($2 && $4) 31062449Speter store((char *) $4, "w", 0); 31162449Speter if ($4) 31262449Speter free((char *) $4); 31362449Speter } 31462449Speter | APPE check_login SP pathname CRLF 31562449Speter = { 31662449Speter if ($2 && $4) 31762449Speter store((char *) $4, "a", 0); 31862449Speter if ($4) 31962449Speter free((char *) $4); 32062449Speter } 32162449Speter | NLST check_login CRLF 32262449Speter = { 32362449Speter if ($2) 32462449Speter send_file_list("."); 32562449Speter } 32662449Speter | NLST check_login SP STRING CRLF 32762449Speter = { 32862449Speter if ($2 && $4) 32962449Speter send_file_list((char *) $4); 33062449Speter if ($4) 33162449Speter free((char *) $4); 33262449Speter } 33362449Speter | LIST check_login CRLF 33462449Speter = { 33562449Speter if ($2) 33662449Speter retrieve(LS_COMMAND, ""); 33762449Speter } 33862449Speter | LIST check_login SP pathname CRLF 33962449Speter = { 34062449Speter if ($2 && $4) 34162449Speter { 34262449Speter char buffer[sizeof(LS_COMMAND)+3]; 34362449Speter strcpy(buffer, LS_COMMAND); 34462449Speter strcat(buffer, " %s"); 34562449Speter retrieve(buffer, (char *) $4); 34662449Speter } 34762449Speter if ($4) 34862449Speter free((char *) $4); 34962449Speter } 35062449Speter | STAT check_login SP pathname CRLF 35162449Speter = { 35262449Speter if ($2 && $4) 35362449Speter statfilecmd((char *) $4); 35462449Speter if ($4) 35562449Speter free((char *) $4); 35662449Speter } 35762449Speter | STAT CRLF 35862449Speter = { 35962449Speter statcmd(); 36062449Speter } 36162449Speter | DELE check_login SP pathname CRLF 36262449Speter = { 36362449Speter if ($2 && $4) 36462449Speter delete((char *) $4); 36562449Speter if ($4) 36662449Speter free((char *) $4); 36762449Speter } 36862449Speter | RNTO SP pathname CRLF 36962449Speter = { 37062449Speter if (fromname) { 37162449Speter renamecmd(fromname, (char *) $3); 37262449Speter free(fromname); 37362449Speter fromname = (char *) 0; 37462449Speter } else { 37562449Speter reply(503, "Bad sequence of commands."); 37662449Speter } 37762449Speter free((char *) $3); 37862449Speter } 37962449Speter | ABOR CRLF 38062449Speter = { 38162449Speter reply(225, "ABOR command successful."); 38262449Speter } 38362449Speter | CWD check_login CRLF 38462449Speter = { 38562449Speter if ($2) 38662449Speter cwd(pw->pw_dir); 38762449Speter } 38862449Speter | CWD check_login SP pathname CRLF 38962449Speter = { 39062449Speter if ($2 && $4) 39162449Speter cwd((char *) $4); 39262449Speter if ($4) 39362449Speter free((char *) $4); 39462449Speter } 39562449Speter | HELP CRLF 39662449Speter = { 39762449Speter help(cmdtab, (char *) 0); 39862449Speter } 39962449Speter | HELP SP STRING CRLF 40062449Speter = { 40162449Speter register char *cp = (char *)$3; 40262449Speter 40362449Speter if (strncasecmp(cp, "SITE", 4) == 0) { 404166124Srafan cp = (char *)$3 + 4; 40562449Speter if (*cp == ' ') 40662449Speter cp++; 40762449Speter if (*cp) 40862449Speter help(sitetab, cp); 40962449Speter else 41062449Speter help(sitetab, (char *) 0); 41162449Speter } else 41262449Speter help(cmdtab, (char *) $3); 41362449Speter } 41462449Speter | NOOP CRLF 41562449Speter = { 41662449Speter reply(200, "NOOP command successful."); 41762449Speter } 41862449Speter | MKD check_login SP pathname CRLF 41962449Speter = { 42062449Speter if ($2 && $4) 42162449Speter makedir((char *) $4); 42262449Speter if ($4) 42362449Speter free((char *) $4); 42462449Speter } 42562449Speter | RMD check_login SP pathname CRLF 42662449Speter = { 42762449Speter if ($2 && $4) 42862449Speter removedir((char *) $4); 42962449Speter if ($4) 43062449Speter free((char *) $4); 43162449Speter } 43262449Speter | PWD check_login CRLF 43362449Speter = { 43462449Speter if ($2) 43562449Speter pwd(); 43662449Speter } 43762449Speter | CDUP check_login CRLF 43862449Speter = { 43962449Speter if ($2) 44062449Speter cwd(".."); 44162449Speter } 44262449Speter | SITE SP HELP CRLF 44362449Speter = { 44462449Speter help(sitetab, (char *) 0); 44562449Speter } 44662449Speter | SITE SP HELP SP STRING CRLF 44762449Speter = { 44862449Speter help(sitetab, (char *) $5); 44962449Speter } 45062449Speter | SITE SP UMASK check_login CRLF 45162449Speter = { 45262449Speter int oldmask; 45362449Speter 45462449Speter if ($4) { 45562449Speter oldmask = umask(0); 45662449Speter (void) umask(oldmask); 45762449Speter reply(200, "Current UMASK is %03o", oldmask); 45862449Speter } 45962449Speter } 46062449Speter | SITE SP UMASK check_login SP octal_number CRLF 46162449Speter = { 46262449Speter int oldmask; 46362449Speter 46462449Speter if ($4) { 46562449Speter if (($6 == -1) || ($6 > 0777)) { 46662449Speter reply(501, "Bad UMASK value"); 46762449Speter } else { 46862449Speter oldmask = umask($6); 46962449Speter reply(200, 47062449Speter "UMASK set to %03o (was %03o)", 47162449Speter $6, oldmask); 47262449Speter } 47362449Speter } 47462449Speter } 47562449Speter | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 47662449Speter = { 47762449Speter if ($4 && $8) { 47862449Speter if ($6 > 0777) 47962449Speter reply(501, 48062449Speter "CHMOD: Mode value must be between 0 and 0777"); 48162449Speter else if (chmod((char *) $8, $6) < 0) 48262449Speter perror_reply(550, (char *) $8); 48362449Speter else 48462449Speter reply(200, "CHMOD command successful."); 48562449Speter } 48662449Speter if ($8) 48762449Speter free((char *) $8); 48862449Speter } 48962449Speter | SITE SP IDLE CRLF 49062449Speter = { 49162449Speter reply(200, 49262449Speter "Current IDLE time limit is %d seconds; max %d", 49362449Speter timeout, maxtimeout); 49462449Speter } 49562449Speter | SITE SP IDLE SP NUMBER CRLF 49662449Speter = { 49762449Speter if ($5 < 30 || $5 > maxtimeout) { 49862449Speter reply(501, 49962449Speter "Maximum IDLE time must be between 30 and %d seconds", 50062449Speter maxtimeout); 50162449Speter } else { 50262449Speter timeout = $5; 50362449Speter (void) alarm((unsigned) timeout); 50462449Speter reply(200, 50562449Speter "Maximum IDLE time set to %d seconds", 50662449Speter timeout); 50762449Speter } 50862449Speter } 50962449Speter | STOU check_login SP pathname CRLF 51062449Speter = { 51162449Speter if ($2 && $4) 51262449Speter store((char *) $4, "w", 1); 51362449Speter if ($4) 51462449Speter free((char *) $4); 51562449Speter } 51662449Speter | SYST CRLF 51762449Speter = { 51862449Speter#ifdef unix 51962449Speter#ifdef BSD 52062449Speter reply(215, "UNIX Type: L%d Version: BSD-%d", 52162449Speter NBBY, BSD); 52262449Speter#else /* BSD */ 52362449Speter reply(215, "UNIX Type: L%d", NBBY); 52462449Speter#endif /* BSD */ 52562449Speter#else /* unix */ 52662449Speter reply(215, "UNKNOWN Type: L%d", NBBY); 52762449Speter#endif /* unix */ 52862449Speter } 52962449Speter 53062449Speter /* 53162449Speter * SIZE is not in RFC959, but Postel has blessed it and 53262449Speter * it will be in the updated RFC. 53362449Speter * 53462449Speter * Return size of file in a format suitable for 53562449Speter * using with RESTART (we just count bytes). 53662449Speter */ 53762449Speter | SIZE check_login SP pathname CRLF 53862449Speter = { 53962449Speter if ($2 && $4) 54062449Speter sizecmd((char *) $4); 54162449Speter if ($4) 54262449Speter free((char *) $4); 54362449Speter } 54462449Speter 54562449Speter /* 54662449Speter * MDTM is not in RFC959, but Postel has blessed it and 54762449Speter * it will be in the updated RFC. 54862449Speter * 54962449Speter * Return modification time of file as an ISO 3307 55062449Speter * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 55162449Speter * where xxx is the fractional second (of any precision, 55262449Speter * not necessarily 3 digits) 55362449Speter */ 55462449Speter | MDTM check_login SP pathname CRLF 55562449Speter = { 55662449Speter if ($2 && $4) { 55762449Speter struct stat stbuf; 55862449Speter if (stat((char *) $4, &stbuf) < 0) 55962449Speter perror_reply(550, (char *) $4); 56062449Speter else if ((stbuf.st_mode&S_IFMT) != S_IFREG) { 56162449Speter reply(550, "%s: not a plain file.", 56262449Speter (char *) $4); 56362449Speter } else { 56462449Speter register struct tm *t; 56562449Speter struct tm *gmtime(); 56662449Speter t = gmtime(&stbuf.st_mtime); 56762449Speter reply(213, 56862449Speter "19%02d%02d%02d%02d%02d%02d", 56962449Speter t->tm_year, t->tm_mon+1, t->tm_mday, 57062449Speter t->tm_hour, t->tm_min, t->tm_sec); 57162449Speter } 57262449Speter } 57362449Speter if ($4) 57462449Speter free((char *) $4); 57562449Speter } 57662449Speter | QUIT CRLF 57762449Speter = { 57862449Speter reply(221, "Goodbye."); 57962449Speter dologout(0); 58062449Speter } 58162449Speter | error CRLF 58262449Speter = { 58362449Speter yyerrok; 58462449Speter } 585166124Srafan ; 58662449Speterrcmd: RNFR check_login SP pathname CRLF 58762449Speter = { 58862449Speter char *renamefrom(); 58962449Speter 59062449Speter restart_point = (off_t) 0; 59162449Speter if ($2 && $4) { 59262449Speter fromname = renamefrom((char *) $4); 59362449Speter if (fromname == (char *) 0 && $4) { 59462449Speter free((char *) $4); 59562449Speter } 59662449Speter } 59762449Speter } 59862449Speter | REST SP byte_size CRLF 59962449Speter = { 60062449Speter long atol(); 60162449Speter 60262449Speter fromname = (char *) 0; 60362449Speter restart_point = $3; 60462449Speter reply(350, "Restarting at %ld. %s", restart_point, 60562449Speter "Send STORE or RETRIEVE to initiate transfer."); 60662449Speter } 60762449Speter ; 60862449Speter 60962449Speterusername: STRING 61062449Speter ; 61162449Speter 61262449Speterpassword: /* empty */ 61362449Speter = { 61462449Speter *(char **)&($$) = (char *)calloc(1, sizeof(char)); 61562449Speter } 61662449Speter | STRING 61762449Speter ; 61862449Speter 61962449Speterbyte_size: NUMBER 62062449Speter ; 62162449Speter 62262449Speterhost_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 62362449Speter NUMBER COMMA NUMBER 62462449Speter = { 62562449Speter register char *a, *p; 62662449Speter 62762449Speter a = (char *)&data_dest.sin_addr; 62862449Speter a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 62962449Speter 63062449Speter/* H* port fix, part A-1: Check the args against the client addr */ 63162449Speter p = (char *)&his_addr.sin_addr; 63262449Speter if (memcmp (a, p, sizeof (data_dest.sin_addr))) 63362449Speter memset (a, 0, sizeof (data_dest.sin_addr)); /* XXX */ 63462449Speter 63562449Speter p = (char *)&data_dest.sin_port; 63662449Speter 63762449Speter/* H* port fix, part A-2: only allow client ports in "user space" */ 63862449Speter p[0] = 0; p[1] = 0; 63962449Speter cliport = ($9 << 8) + $11; 64062449Speter if (cliport > 1023) { 64162449Speter p[0] = $9; p[1] = $11; 64262449Speter } 64362449Speter 64462449Speter p[0] = $9; p[1] = $11; 64562449Speter data_dest.sin_family = AF_INET; 64662449Speter } 64762449Speter ; 64862449Speter 64962449Speterform_code: N 65062449Speter = { 65162449Speter $$ = FORM_N; 65262449Speter } 65362449Speter | T 65462449Speter = { 65562449Speter $$ = FORM_T; 65662449Speter } 65762449Speter | C 65862449Speter = { 65962449Speter $$ = FORM_C; 66062449Speter } 66162449Speter ; 66262449Speter 66362449Spetertype_code: A 66462449Speter = { 66562449Speter cmd_type = TYPE_A; 66662449Speter cmd_form = FORM_N; 66762449Speter } 66862449Speter | A SP form_code 66962449Speter = { 67062449Speter cmd_type = TYPE_A; 67162449Speter cmd_form = $3; 67262449Speter } 67362449Speter | E 67462449Speter = { 67562449Speter cmd_type = TYPE_E; 67662449Speter cmd_form = FORM_N; 67762449Speter } 67862449Speter | E SP form_code 67962449Speter = { 68062449Speter cmd_type = TYPE_E; 68162449Speter cmd_form = $3; 68262449Speter } 68362449Speter | I 68462449Speter = { 68562449Speter cmd_type = TYPE_I; 68662449Speter } 68762449Speter | L 68862449Speter = { 68962449Speter cmd_type = TYPE_L; 69062449Speter cmd_bytesz = NBBY; 69162449Speter } 69262449Speter | L SP byte_size 69362449Speter = { 69462449Speter cmd_type = TYPE_L; 69562449Speter cmd_bytesz = $3; 69662449Speter } 69762449Speter /* this is for a bug in the BBN ftp */ 69862449Speter | L byte_size 69962449Speter = { 70062449Speter cmd_type = TYPE_L; 70162449Speter cmd_bytesz = $2; 70262449Speter } 70362449Speter ; 70462449Speter 70562449Speterstruct_code: F 70662449Speter = { 70762449Speter $$ = STRU_F; 70862449Speter } 70962449Speter | R 71062449Speter = { 71162449Speter $$ = STRU_R; 71262449Speter } 71362449Speter | P 71462449Speter = { 71562449Speter $$ = STRU_P; 71662449Speter } 71762449Speter ; 71862449Speter 71962449Spetermode_code: S 72062449Speter = { 72162449Speter $$ = MODE_S; 72262449Speter } 72362449Speter | B 72462449Speter = { 72562449Speter $$ = MODE_B; 72662449Speter } 72762449Speter | C 72862449Speter = { 72962449Speter $$ = MODE_C; 73062449Speter } 73162449Speter ; 73262449Speter 73362449Speterpathname: pathstring 73462449Speter = { 73562449Speter /* 73662449Speter * Problem: this production is used for all pathname 73762449Speter * processing, but only gives a 550 error reply. 73862449Speter * This is a valid reply in some cases but not in others. 73962449Speter */ 74062449Speter if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) { 74162449Speter *(char **)&($$) = *ftpglob((char *) $1); 74262449Speter if (globerr != NULL) { 74362449Speter reply(550, globerr); 74462449Speter/* $$ = NULL; */ 74562449Speter $$ = 0; 74662449Speter } 74762449Speter free((char *) $1); 74862449Speter } else 74962449Speter $$ = $1; 75062449Speter } 75162449Speter ; 75262449Speter 75362449Speterpathstring: STRING 75462449Speter ; 75562449Speter 75662449Speteroctal_number: NUMBER 75762449Speter = { 75862449Speter register int ret, dec, multby, digit; 75962449Speter 76062449Speter /* 76162449Speter * Convert a number that was read as decimal number 76262449Speter * to what it would be if it had been read as octal. 76362449Speter */ 76462449Speter dec = $1; 76562449Speter multby = 1; 76662449Speter ret = 0; 76762449Speter while (dec) { 76862449Speter digit = dec%10; 76962449Speter if (digit > 7) { 77062449Speter ret = -1; 77162449Speter break; 77262449Speter } 77362449Speter ret += digit * multby; 77462449Speter multby *= 8; 77562449Speter dec /= 10; 77662449Speter } 77762449Speter $$ = ret; 77862449Speter } 77962449Speter ; 78062449Speter 78162449Spetercheck_login: /* empty */ 78262449Speter = { 78362449Speter if (logged_in) 78462449Speter $$ = 1; 78562449Speter else { 78662449Speter reply(530, "Please login with USER and PASS."); 78762449Speter $$ = 0; 78862449Speter } 78962449Speter } 79062449Speter ; 79162449Speter 79262449Speter%% 79362449Speter 79462449Speterextern jmp_buf errcatch; 79562449Speter 79662449Speter#define CMD 0 /* beginning of command */ 79762449Speter#define ARGS 1 /* expect miscellaneous arguments */ 79862449Speter#define STR1 2 /* expect SP followed by STRING */ 79962449Speter#define STR2 3 /* expect STRING */ 80062449Speter#define OSTR 4 /* optional SP then STRING */ 80162449Speter#define ZSTR1 5 /* SP then optional STRING */ 80262449Speter#define ZSTR2 6 /* optional STRING after SP */ 80362449Speter#define SITECMD 7 /* SITE command */ 80462449Speter#define NSTR 8 /* Number followed by a string */ 80562449Speter 80662449Speterstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 80762449Speter { "USER", USER, STR1, 1, "<sp> username" }, 80862449Speter { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 80962449Speter { "ACCT", ACCT, STR1, 0, "(specify account)" }, 81062449Speter { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 81162449Speter { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 81262449Speter { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 81362449Speter { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 81462449Speter { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 81562449Speter { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 81662449Speter { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 81762449Speter { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 81862449Speter { "RETR", RETR, STR1, 1, "<sp> file-name" }, 81962449Speter { "STOR", STOR, STR1, 1, "<sp> file-name" }, 82062449Speter { "APPE", APPE, STR1, 1, "<sp> file-name" }, 82162449Speter { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 82262449Speter { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 82362449Speter { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 82462449Speter { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 82562449Speter { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 82662449Speter { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 82762449Speter { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 82862449Speter { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 82962449Speter { "REST", REST, ARGS, 1, "(restart command)" }, 83062449Speter { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 83162449Speter { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 83262449Speter { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 83362449Speter { "DELE", DELE, STR1, 1, "<sp> file-name" }, 83462449Speter { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 83562449Speter { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 83662449Speter { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 83762449Speter { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 83862449Speter { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 83962449Speter { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 84062449Speter { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 84162449Speter { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 84262449Speter { "NOOP", NOOP, ARGS, 1, "" }, 84362449Speter { "MKD", MKD, STR1, 1, "<sp> path-name" }, 84462449Speter { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 84562449Speter { "RMD", RMD, STR1, 1, "<sp> path-name" }, 84662449Speter { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 84762449Speter { "PWD", PWD, ARGS, 1, "(return current directory)" }, 84862449Speter { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 84962449Speter { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 85062449Speter { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 85162449Speter { "STOU", STOU, STR1, 1, "<sp> file-name" }, 85262449Speter { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 85362449Speter { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 85462449Speter { NULL, 0, 0, 0, 0 } 85562449Speter}; 85662449Speter 85762449Speterstruct tab sitetab[] = { 85862449Speter { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 85962449Speter { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 86062449Speter { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 86162449Speter { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 86262449Speter { NULL, 0, 0, 0, 0 } 86362449Speter}; 86462449Speter 86562449Speterstruct tab *lookup FUNCTION((p, cmd), register struct tab *p AND char *cmd) 86662449Speter{ 86762449Speter 86862449Speter for (; p->name != NULL; p++) 86962449Speter if (strcmp(cmd, p->name) == 0) 87062449Speter return (p); 87162449Speter return (0); 87262449Speter} 87362449Speter 87462449Speter#include <arpa/telnet.h> 87562449Speter 87662449Speter/* 87762449Speter * getline - a hacked up version of fgets to ignore TELNET escape codes. 87862449Speter */ 87962449Speterchar *getline FUNCTION((s, n, iop), char *s AND int n AND FILE *iop) 88062449Speter{ 88162449Speter register c; 88262449Speter register char *cs; 88362449Speter 88462449Speter cs = s; 88562449Speter/* tmpline may contain saved command from urgent mode interruption */ 88662449Speter for (c = 0; *(tmpline + c) && --n > 0; ++c) { 88762449Speter *cs++ = *(tmpline + c); 88862449Speter if (*(tmpline + c) == '\n') { 88962449Speter *cs++ = '\0'; 89062449Speter if (debug) 89162449Speter syslog(LOG_DEBUG, "command: %s", s); 89262449Speter *tmpline = '\0'; 89362449Speter return(s); 89462449Speter } 89562449Speter if (c == 0) 89662449Speter *tmpline = '\0'; 89762449Speter } 89862449Speter while ((c = getc(iop)) != EOF) { 89962449Speter c &= 0377; 90062449Speter if (c == IAC) { 90162449Speter if ((c = getc(iop)) != EOF) { 90262449Speter c &= 0377; 90362449Speter switch (c) { 90462449Speter case WILL: 90562449Speter case WONT: 90662449Speter c = getc(iop); 90762449Speter printf("%c%c%c", IAC, DONT, 0377&c); 90862449Speter (void) fflush(stdout); 90962449Speter continue; 91062449Speter case DO: 91162449Speter case DONT: 91262449Speter c = getc(iop); 91362449Speter printf("%c%c%c", IAC, WONT, 0377&c); 91462449Speter (void) fflush(stdout); 915 continue; 916 case IAC: 917 break; 918 default: 919 continue; /* ignore command */ 920 } 921 } 922 } 923 *cs++ = c; 924 if (--n <= 0 || c == '\n') 925 break; 926 } 927 if (c == EOF && cs == s) 928 return (NULL); 929 *cs++ = '\0'; 930 if (debug) 931 syslog(LOG_DEBUG, "command: %s", s); 932 return (s); 933} 934 935static VOIDRET toolong FUNCTION((input), int input) 936{ 937 time_t now; 938 939 reply(421, "Timeout (%d seconds): closing control connection.", timeout); 940 (void) time(&now); 941 syslog(LOG_INFO, "User %s timed out after %d seconds at %s", 942 (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 943 dologout(1); 944} 945 946int yylex FUNCTION_NOARGS 947{ 948 static int cpos, state; 949 register char *cp, *cp2; 950 register struct tab *p; 951 int n; 952 char c, *copy(); 953 954 for (;;) { 955 switch (state) { 956 957 case CMD: 958 (void) signal(SIGALRM, toolong); 959 (void) alarm((unsigned) timeout); 960 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 961 reply(221, "You could at least say goodbye."); 962 dologout(0); 963 } 964 (void) alarm(0); 965#ifdef SETPROCTITLE 966 if (strncasecmp(cbuf, "PASS", 4) != NULL) 967 setproctitle("%s: %s", proctitle, cbuf); 968#endif /* SETPROCTITLE */ 969 if ((cp = strchr(cbuf, '\r'))) { 970 *cp++ = '\n'; 971 *cp = '\0'; 972 } 973 if ((cp = strpbrk(cbuf, " \n"))) 974 cpos = cp - cbuf; 975 if (cpos == 0) 976 cpos = 4; 977 c = cbuf[cpos]; 978 cbuf[cpos] = '\0'; 979 upper(cbuf); 980 p = lookup(cmdtab, cbuf); 981 cbuf[cpos] = c; 982 if (p != 0) { 983 if (p->implemented == 0) { 984 nack(p->name); 985 longjmp(errcatch,0); 986 /* NOTREACHED */ 987 } 988 state = p->state; 989 *(char **)&yylval = p->name; 990 return (p->token); 991 } 992 break; 993 994 case SITECMD: 995 if (cbuf[cpos] == ' ') { 996 cpos++; 997 return (SP); 998 } 999 cp = &cbuf[cpos]; 1000 if ((cp2 = strpbrk(cp, " \n"))) 1001 cpos = cp2 - cbuf; 1002 c = cbuf[cpos]; 1003 cbuf[cpos] = '\0'; 1004 upper(cp); 1005 p = lookup(sitetab, cp); 1006 cbuf[cpos] = c; 1007 if (p != 0) { 1008 if (p->implemented == 0) { 1009 state = CMD; 1010 nack(p->name); 1011 longjmp(errcatch,0); 1012 /* NOTREACHED */ 1013 } 1014 state = p->state; 1015 *(char **)&yylval = p->name; 1016 return (p->token); 1017 } 1018 state = CMD; 1019 break; 1020 1021 case OSTR: 1022 if (cbuf[cpos] == '\n') { 1023 state = CMD; 1024 return (CRLF); 1025 } 1026 /* FALLTHROUGH */ 1027 1028 case STR1: 1029 case ZSTR1: 1030 dostr1: 1031 if (cbuf[cpos] == ' ') { 1032 cpos++; 1033 state = state == OSTR ? STR2 : ++state; 1034 return (SP); 1035 } 1036 break; 1037 1038 case ZSTR2: 1039 if (cbuf[cpos] == '\n') { 1040 state = CMD; 1041 return (CRLF); 1042 } 1043 /* FALLTHROUGH */ 1044 1045 case STR2: 1046 cp = &cbuf[cpos]; 1047 n = strlen(cp); 1048 cpos += n - 1; 1049 /* 1050 * Make sure the string is nonempty and \n terminated. 1051 */ 1052 if (n > 1 && cbuf[cpos] == '\n') { 1053 cbuf[cpos] = '\0'; 1054 *(char **)&yylval = copy(cp); 1055 cbuf[cpos] = '\n'; 1056 state = ARGS; 1057 return (STRING); 1058 } 1059 break; 1060 1061 case NSTR: 1062 if (cbuf[cpos] == ' ') { 1063 cpos++; 1064 return (SP); 1065 } 1066 if (isdigit(cbuf[cpos])) { 1067 cp = &cbuf[cpos]; 1068 while (isdigit(cbuf[++cpos])) 1069 ; 1070 c = cbuf[cpos]; 1071 cbuf[cpos] = '\0'; 1072 yylval = atoi(cp); 1073 cbuf[cpos] = c; 1074 state = STR1; 1075 return (NUMBER); 1076 } 1077 state = STR1; 1078 goto dostr1; 1079 1080 case ARGS: 1081 if (isdigit(cbuf[cpos])) { 1082 cp = &cbuf[cpos]; 1083 while (isdigit(cbuf[++cpos])) 1084 ; 1085 c = cbuf[cpos]; 1086 cbuf[cpos] = '\0'; 1087 yylval = atoi(cp); 1088 cbuf[cpos] = c; 1089 return (NUMBER); 1090 } 1091 switch (cbuf[cpos++]) { 1092 1093 case '\n': 1094 state = CMD; 1095 return (CRLF); 1096 1097 case ' ': 1098 return (SP); 1099 1100 case ',': 1101 return (COMMA); 1102 1103 case 'A': 1104 case 'a': 1105 return (A); 1106 1107 case 'B': 1108 case 'b': 1109 return (B); 1110 1111 case 'C': 1112 case 'c': 1113 return (C); 1114 1115 case 'E': 1116 case 'e': 1117 return (E); 1118 1119 case 'F': 1120 case 'f': 1121 return (F); 1122 1123 case 'I': 1124 case 'i': 1125 return (I); 1126 1127 case 'L': 1128 case 'l': 1129 return (L); 1130 1131 case 'N': 1132 case 'n': 1133 return (N); 1134 1135 case 'P': 1136 case 'p': 1137 return (P); 1138 1139 case 'R': 1140 case 'r': 1141 return (R); 1142 1143 case 'S': 1144 case 's': 1145 return (S); 1146 1147 case 'T': 1148 case 't': 1149 return (T); 1150 1151 } 1152 break; 1153 1154 default: 1155 opiefatal("Unknown state in scanner."); 1156 } 1157 yyerror((char *) 0); 1158 state = CMD; 1159 longjmp(errcatch,0); 1160 } 1161} 1162 1163VOIDRET upper FUNCTION((s), char *s) 1164{ 1165 while (*s != '\0') { 1166 if (islower(*s)) 1167 *s = toupper(*s); 1168 s++; 1169 } 1170} 1171 1172char *copy FUNCTION((s), char *s) 1173{ 1174 char *p; 1175 1176 p = malloc((unsigned) strlen(s) + 1); 1177 if (p == NULL) 1178 opiefatal("Ran out of memory."); 1179 (void) strcpy(p, s); 1180 return (p); 1181} 1182 1183VOIDRET help FUNCTION((ctab, s), struct tab *ctab AND char *s) 1184{ 1185 register struct tab *c; 1186 register int width, NCMDS; 1187 char *type; 1188 1189 if (ctab == sitetab) 1190 type = "SITE "; 1191 else 1192 type = ""; 1193 width = 0, NCMDS = 0; 1194 for (c = ctab; c->name != NULL; c++) { 1195 int len = strlen(c->name); 1196 1197 if (len > width) 1198 width = len; 1199 NCMDS++; 1200 } 1201 width = (width + 8) &~ 7; 1202 if (s == 0) { 1203 register int i, j, w; 1204 int columns, lines; 1205 1206 lreply(214, "The following %scommands are recognized %s.", 1207 type, "(* =>'s unimplemented)"); 1208 columns = 76 / width; 1209 if (columns == 0) 1210 columns = 1; 1211 lines = (NCMDS + columns - 1) / columns; 1212 for (i = 0; i < lines; i++) { 1213 printf(" "); 1214 for (j = 0; j < columns; j++) { 1215 c = ctab + j * lines + i; 1216 printf("%s%c", c->name, 1217 c->implemented ? ' ' : '*'); 1218 if (c + lines >= &ctab[NCMDS]) 1219 break; 1220 w = strlen(c->name) + 1; 1221 while (w < width) { 1222 putchar(' '); 1223 w++; 1224 } 1225 } 1226 printf("\r\n"); 1227 } 1228 (void) fflush(stdout); 1229 return; 1230 } 1231 upper(s); 1232 c = lookup(ctab, s); 1233 if (c == (struct tab *)0) { 1234 reply(502, "Unknown command %s.", s); 1235 return; 1236 } 1237 if (c->implemented) 1238 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1239 else 1240 reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1241 c->name, c->help); 1242} 1243 1244VOIDRET sizecmd FUNCTION((filename), char *filename) 1245{ 1246 switch (type) { 1247 case TYPE_L: 1248 case TYPE_I: { 1249 struct stat stbuf; 1250 if (stat(filename, &stbuf) < 0 || 1251 (stbuf.st_mode&S_IFMT) != S_IFREG) 1252 reply(550, "%s: not a plain file.", filename); 1253 else 1254 reply(213, "%lu", stbuf.st_size); 1255 break;} 1256 case TYPE_A: { 1257 FILE *fin; 1258 register int c; 1259 register long count; 1260 struct stat stbuf; 1261 fin = fopen(filename, "r"); 1262 if (fin == NULL) { 1263 perror_reply(550, filename); 1264 return; 1265 } 1266 if (fstat(fileno(fin), &stbuf) < 0 || 1267 (stbuf.st_mode&S_IFMT) != S_IFREG) { 1268 reply(550, "%s: not a plain file.", filename); 1269 (void) fclose(fin); 1270 return; 1271 } 1272 1273 count = 0; 1274 while((c=getc(fin)) != EOF) { 1275 if (c == '\n') /* will get expanded to \r\n */ 1276 count++; 1277 count++; 1278 } 1279 (void) fclose(fin); 1280 1281 reply(213, "%ld", count); 1282 break;} 1283 default: 1284 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1285 } 1286} 1287