ftpcmd.y revision 56668
11592Srgrimes/* 21592Srgrimes * Copyright (c) 1985, 1988, 1993, 1994 31592Srgrimes * The Regents of the University of California. All rights reserved. 41592Srgrimes * 51592Srgrimes * Redistribution and use in source and binary forms, with or without 61592Srgrimes * modification, are permitted provided that the following conditions 71592Srgrimes * are met: 81592Srgrimes * 1. Redistributions of source code must retain the above copyright 91592Srgrimes * notice, this list of conditions and the following disclaimer. 101592Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111592Srgrimes * notice, this list of conditions and the following disclaimer in the 121592Srgrimes * documentation and/or other materials provided with the distribution. 131592Srgrimes * 3. All advertising materials mentioning features or use of this software 141592Srgrimes * must display the following acknowledgement: 151592Srgrimes * This product includes software developed by the University of 161592Srgrimes * California, Berkeley and its contributors. 171592Srgrimes * 4. Neither the name of the University nor the names of its contributors 181592Srgrimes * may be used to endorse or promote products derived from this software 191592Srgrimes * without specific prior written permission. 201592Srgrimes * 211592Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221592Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231592Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241592Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251592Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261592Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271592Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281592Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291592Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301592Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311592Srgrimes * SUCH DAMAGE. 321592Srgrimes * 331592Srgrimes * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 341592Srgrimes */ 351592Srgrimes 361592Srgrimes/* 371592Srgrimes * Grammar for FTP commands. 381592Srgrimes * See RFC 959. 391592Srgrimes */ 401592Srgrimes 411592Srgrimes%{ 421592Srgrimes 431592Srgrimes#ifndef lint 4431329Scharnier#if 0 451592Srgrimesstatic char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; 4631329Scharnier#endif 4731329Scharnierstatic const char rcsid[] = 4850476Speter "$FreeBSD: head/libexec/ftpd/ftpcmd.y 56668 2000-01-27 09:28:38Z shin $"; 491592Srgrimes#endif /* not lint */ 501592Srgrimes 511592Srgrimes#include <sys/param.h> 521592Srgrimes#include <sys/socket.h> 531592Srgrimes#include <sys/stat.h> 541592Srgrimes 551592Srgrimes#include <netinet/in.h> 561592Srgrimes#include <arpa/ftp.h> 571592Srgrimes 581592Srgrimes#include <ctype.h> 591592Srgrimes#include <errno.h> 601592Srgrimes#include <glob.h> 6156668Sshin#include <netdb.h> 621592Srgrimes#include <pwd.h> 631592Srgrimes#include <setjmp.h> 641592Srgrimes#include <signal.h> 651592Srgrimes#include <stdio.h> 661592Srgrimes#include <stdlib.h> 671592Srgrimes#include <string.h> 681592Srgrimes#include <syslog.h> 691592Srgrimes#include <time.h> 701592Srgrimes#include <unistd.h> 7113139Speter#include <libutil.h> 721592Srgrimes 731592Srgrimes#include "extern.h" 741592Srgrimes 7556668Sshinextern union sockunion data_dest, his_addr; 761592Srgrimesextern int logged_in; 771592Srgrimesextern struct passwd *pw; 781592Srgrimesextern int guest; 7917435Spstextern int paranoid; 801592Srgrimesextern int logging; 811592Srgrimesextern int type; 821592Srgrimesextern int form; 831592Srgrimesextern int debug; 841592Srgrimesextern int timeout; 851592Srgrimesextern int maxtimeout; 861592Srgrimesextern int pdata; 8727650Sdavidnextern char *hostname; 8827650Sdavidnextern char remotehost[]; 891592Srgrimesextern char proctitle[]; 901592Srgrimesextern int usedefault; 911592Srgrimesextern int transflag; 921592Srgrimesextern char tmpline[]; 931592Srgrimes 941592Srgrimesoff_t restart_point; 951592Srgrimes 961592Srgrimesstatic int cmd_type; 971592Srgrimesstatic int cmd_form; 981592Srgrimesstatic int cmd_bytesz; 991592Srgrimeschar cbuf[512]; 1001592Srgrimeschar *fromname; 1011592Srgrimes 10256668Sshinextern int epsvall; 10356668Sshin 1041592Srgrimes%} 1051592Srgrimes 1061592Srgrimes%union { 1071592Srgrimes int i; 1081592Srgrimes char *s; 1091592Srgrimes} 1101592Srgrimes 1111592Srgrimes%token 1121592Srgrimes A B C E F I 1131592Srgrimes L N P R S T 11456668Sshin ALL 1151592Srgrimes 1161592Srgrimes SP CRLF COMMA 1171592Srgrimes 1181592Srgrimes USER PASS ACCT REIN QUIT PORT 1191592Srgrimes PASV TYPE STRU MODE RETR STOR 1201592Srgrimes APPE MLFL MAIL MSND MSOM MSAM 1211592Srgrimes MRSQ MRCP ALLO REST RNFR RNTO 1221592Srgrimes ABOR DELE CWD LIST NLST SITE 1231592Srgrimes STAT HELP NOOP MKD RMD PWD 1241592Srgrimes CDUP STOU SMNT SYST SIZE MDTM 12556668Sshin LPRT LPSV EPRT EPSV 1261592Srgrimes 1271592Srgrimes UMASK IDLE CHMOD 1281592Srgrimes 1291592Srgrimes LEXERR 1301592Srgrimes 1311592Srgrimes%token <s> STRING 1321592Srgrimes%token <i> NUMBER 1331592Srgrimes 1341592Srgrimes%type <i> check_login octal_number byte_size 1351592Srgrimes%type <i> struct_code mode_code type_code form_code 13656668Sshin%type <s> pathstring pathname password username ext_arg 13756668Sshin%type <s> ALL 1381592Srgrimes 1391592Srgrimes%start cmd_list 1401592Srgrimes 1411592Srgrimes%% 1421592Srgrimes 1431592Srgrimescmd_list 1441592Srgrimes : /* empty */ 1451592Srgrimes | cmd_list cmd 1461592Srgrimes { 1471592Srgrimes fromname = (char *) 0; 1481592Srgrimes restart_point = (off_t) 0; 1491592Srgrimes } 1501592Srgrimes | cmd_list rcmd 1511592Srgrimes ; 1521592Srgrimes 1531592Srgrimescmd 1541592Srgrimes : USER SP username CRLF 1551592Srgrimes { 1561592Srgrimes user($3); 1571592Srgrimes free($3); 1581592Srgrimes } 1591592Srgrimes | PASS SP password CRLF 1601592Srgrimes { 1611592Srgrimes pass($3); 1621592Srgrimes free($3); 1631592Srgrimes } 16417433Spst | PORT check_login SP host_port CRLF 1651592Srgrimes { 16656668Sshin if (epsvall) { 16756668Sshin reply(501, "no PORT allowed after EPSV ALL"); 16856668Sshin goto port_done; 16956668Sshin } 17056668Sshin if (!$2) 17156668Sshin goto port_done; 17256668Sshin if (port_check("PORT") == 1) 17356668Sshin goto port_done; 17456668Sshin#ifdef INET6 17556668Sshin if ((his_addr.su_family != AF_INET6 || 17656668Sshin !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { 17756668Sshin /* shoud never happen */ 17856668Sshin usedefault = 1; 17956668Sshin reply(500, "Invalid address rejected."); 18056668Sshin goto port_done; 18156668Sshin } 18256668Sshin port_check_v6("pcmd"); 18356668Sshin#endif 18456668Sshin port_done: 18556668Sshin } 18656668Sshin | LPRT check_login SP host_long_port CRLF 18756668Sshin { 18856668Sshin if (epsvall) { 18956668Sshin reply(501, "no LPRT allowed after EPSV ALL"); 19056668Sshin goto lprt_done; 19156668Sshin } 19256668Sshin if (!$2) 19356668Sshin goto lprt_done; 19456668Sshin if (port_check("LPRT") == 1) 19556668Sshin goto lprt_done; 19656668Sshin#ifdef INET6 19756668Sshin if (his_addr.su_family != AF_INET6) { 19856668Sshin usedefault = 1; 19956668Sshin reply(500, "Invalid address rejected."); 20056668Sshin goto lprt_done; 20156668Sshin } 20256668Sshin if (port_check_v6("LPRT") == 1) 20356668Sshin goto lprt_done; 20456668Sshin#endif 20556668Sshin lprt_done: 20656668Sshin } 20756668Sshin | EPRT check_login SP STRING CRLF 20856668Sshin { 20956668Sshin char delim; 21056668Sshin char *tmp = NULL; 21156668Sshin char *p, *q; 21256668Sshin char *result[3]; 21356668Sshin struct addrinfo hints; 21456668Sshin struct addrinfo *res; 21556668Sshin int i; 21656668Sshin 21756668Sshin if (epsvall) { 21856668Sshin reply(501, "no EPRT allowed after EPSV ALL"); 21956668Sshin goto eprt_done; 22056668Sshin } 22156668Sshin if (!$2) 22256668Sshin goto eprt_done; 22356668Sshin 22456668Sshin memset(&data_dest, 0, sizeof(data_dest)); 22556668Sshin tmp = strdup($4); 22656668Sshin if (debug) 22756668Sshin syslog(LOG_DEBUG, "%s", tmp); 22856668Sshin if (!tmp) { 22956668Sshin fatal("not enough core"); 23056668Sshin /*NOTREACHED*/ 23156668Sshin } 23256668Sshin p = tmp; 23356668Sshin delim = p[0]; 23456668Sshin p++; 23556668Sshin memset(result, 0, sizeof(result)); 23656668Sshin for (i = 0; i < 3; i++) { 23756668Sshin q = strchr(p, delim); 23856668Sshin if (!q || *q != delim) { 23956668Sshin parsefail: 24056668Sshin reply(500, 24156668Sshin "Invalid argument, rejected."); 24256668Sshin if (tmp) 24356668Sshin free(tmp); 24417433Spst usedefault = 1; 24556668Sshin goto eprt_done; 24617433Spst } 24756668Sshin *q++ = '\0'; 24856668Sshin result[i] = p; 24956668Sshin if (debug) 25056668Sshin syslog(LOG_DEBUG, "%d: %s", i, p); 25156668Sshin p = q; 2521592Srgrimes } 25356668Sshin 25456668Sshin /* some more sanity check */ 25556668Sshin p = result[0]; 25656668Sshin while (*p) { 25756668Sshin if (!isdigit(*p)) 25856668Sshin goto parsefail; 25956668Sshin p++; 26056668Sshin } 26156668Sshin p = result[2]; 26256668Sshin while (*p) { 26356668Sshin if (!isdigit(*p)) 26456668Sshin goto parsefail; 26556668Sshin p++; 26656668Sshin } 26756668Sshin 26856668Sshin /* grab address */ 26956668Sshin memset(&hints, 0, sizeof(hints)); 27056668Sshin if (atoi(result[0]) == 1) 27156668Sshin hints.ai_family = PF_INET; 27256668Sshin#ifdef INET6 27356668Sshin else if (atoi(result[0]) == 2) 27456668Sshin hints.ai_family = PF_INET6; 27556668Sshin#endif 27656668Sshin else 27756668Sshin hints.ai_family = PF_UNSPEC; /*XXX*/ 27856668Sshin hints.ai_socktype = SOCK_STREAM; 27956668Sshin i = getaddrinfo(result[1], result[2], &hints, &res); 28056668Sshin if (i) 28156668Sshin goto parsefail; 28256668Sshin memcpy(&data_dest, res->ai_addr, res->ai_addrlen); 28356668Sshin#ifdef INET6 28456668Sshin if (his_addr.su_family == AF_INET6 28556668Sshin && data_dest.su_family == AF_INET6) { 28656668Sshin /* XXX more sanity checks! */ 28756668Sshin data_dest.su_sin6.sin6_scope_id = 28856668Sshin his_addr.su_sin6.sin6_scope_id; 28956668Sshin } 29056668Sshin#endif 29156668Sshin free(tmp); 29256668Sshin tmp = NULL; 29356668Sshin 29456668Sshin if (port_check("EPRT") == 1) 29556668Sshin goto eprt_done; 29656668Sshin#ifdef INET6 29756668Sshin if (his_addr.su_family != AF_INET6) { 29856668Sshin usedefault = 1; 29956668Sshin reply(500, "Invalid address rejected."); 30056668Sshin goto eprt_done; 30156668Sshin } 30256668Sshin if (port_check_v6("EPRT") == 1) 30356668Sshin goto eprt_done; 30456668Sshin#endif 30556668Sshin eprt_done:; 3061592Srgrimes } 30717433Spst | PASV check_login CRLF 3081592Srgrimes { 30956668Sshin if (epsvall) 31056668Sshin reply(501, "no PASV allowed after EPSV ALL"); 31156668Sshin else if ($2) 31217433Spst passive(); 3131592Srgrimes } 31456668Sshin | LPSV check_login CRLF 31556668Sshin { 31656668Sshin if (epsvall) 31756668Sshin reply(501, "no LPSV allowed after EPSV ALL"); 31856668Sshin else if ($2) 31956668Sshin long_passive("LPSV", PF_UNSPEC); 32056668Sshin } 32156668Sshin | EPSV check_login SP NUMBER CRLF 32256668Sshin { 32356668Sshin if ($2) { 32456668Sshin int pf; 32556668Sshin switch ($4) { 32656668Sshin case 1: 32756668Sshin pf = PF_INET; 32856668Sshin break; 32956668Sshin#ifdef INET6 33056668Sshin case 2: 33156668Sshin pf = PF_INET6; 33256668Sshin break; 33356668Sshin#endif 33456668Sshin default: 33556668Sshin pf = -1; /*junk value*/ 33656668Sshin break; 33756668Sshin } 33856668Sshin long_passive("EPSV", pf); 33956668Sshin } 34056668Sshin } 34156668Sshin | EPSV check_login SP ALL CRLF 34256668Sshin { 34356668Sshin if ($2) { 34456668Sshin reply(200, 34556668Sshin "EPSV ALL command successful."); 34656668Sshin epsvall++; 34756668Sshin } 34856668Sshin } 34956668Sshin | EPSV check_login CRLF 35056668Sshin { 35156668Sshin if ($2) 35256668Sshin long_passive("EPSV", PF_UNSPEC); 35356668Sshin } 3541592Srgrimes | TYPE SP type_code CRLF 3551592Srgrimes { 3561592Srgrimes switch (cmd_type) { 3571592Srgrimes 3581592Srgrimes case TYPE_A: 3591592Srgrimes if (cmd_form == FORM_N) { 3601592Srgrimes reply(200, "Type set to A."); 3611592Srgrimes type = cmd_type; 3621592Srgrimes form = cmd_form; 3631592Srgrimes } else 3641592Srgrimes reply(504, "Form must be N."); 3651592Srgrimes break; 3661592Srgrimes 3671592Srgrimes case TYPE_E: 3681592Srgrimes reply(504, "Type E not implemented."); 3691592Srgrimes break; 3701592Srgrimes 3711592Srgrimes case TYPE_I: 3721592Srgrimes reply(200, "Type set to I."); 3731592Srgrimes type = cmd_type; 3741592Srgrimes break; 3751592Srgrimes 3761592Srgrimes case TYPE_L: 3771592Srgrimes#if NBBY == 8 3781592Srgrimes if (cmd_bytesz == 8) { 3791592Srgrimes reply(200, 3801592Srgrimes "Type set to L (byte size 8)."); 3811592Srgrimes type = cmd_type; 3821592Srgrimes } else 3831592Srgrimes reply(504, "Byte size must be 8."); 3841592Srgrimes#else /* NBBY == 8 */ 3851592Srgrimes UNIMPLEMENTED for NBBY != 8 3861592Srgrimes#endif /* NBBY == 8 */ 3871592Srgrimes } 3881592Srgrimes } 3891592Srgrimes | STRU SP struct_code CRLF 3901592Srgrimes { 3911592Srgrimes switch ($3) { 3921592Srgrimes 3931592Srgrimes case STRU_F: 3941592Srgrimes reply(200, "STRU F ok."); 3951592Srgrimes break; 3961592Srgrimes 3971592Srgrimes default: 3981592Srgrimes reply(504, "Unimplemented STRU type."); 3991592Srgrimes } 4001592Srgrimes } 4011592Srgrimes | MODE SP mode_code CRLF 4021592Srgrimes { 4031592Srgrimes switch ($3) { 4041592Srgrimes 4051592Srgrimes case MODE_S: 4061592Srgrimes reply(200, "MODE S ok."); 4071592Srgrimes break; 4081592Srgrimes 4091592Srgrimes default: 4101592Srgrimes reply(502, "Unimplemented MODE type."); 4111592Srgrimes } 4121592Srgrimes } 4131592Srgrimes | ALLO SP NUMBER CRLF 4141592Srgrimes { 4151592Srgrimes reply(202, "ALLO command ignored."); 4161592Srgrimes } 4171592Srgrimes | ALLO SP NUMBER SP R SP NUMBER CRLF 4181592Srgrimes { 4191592Srgrimes reply(202, "ALLO command ignored."); 4201592Srgrimes } 4211592Srgrimes | RETR check_login SP pathname CRLF 4221592Srgrimes { 4231592Srgrimes if ($2 && $4 != NULL) 4241592Srgrimes retrieve((char *) 0, $4); 4251592Srgrimes if ($4 != NULL) 4261592Srgrimes free($4); 4271592Srgrimes } 4281592Srgrimes | STOR check_login SP pathname CRLF 4291592Srgrimes { 4301592Srgrimes if ($2 && $4 != NULL) 4311592Srgrimes store($4, "w", 0); 4321592Srgrimes if ($4 != NULL) 4331592Srgrimes free($4); 4341592Srgrimes } 4351592Srgrimes | APPE check_login SP pathname CRLF 4361592Srgrimes { 4371592Srgrimes if ($2 && $4 != NULL) 4381592Srgrimes store($4, "a", 0); 4391592Srgrimes if ($4 != NULL) 4401592Srgrimes free($4); 4411592Srgrimes } 4421592Srgrimes | NLST check_login CRLF 4431592Srgrimes { 4441592Srgrimes if ($2) 4451592Srgrimes send_file_list("."); 4461592Srgrimes } 4471592Srgrimes | NLST check_login SP STRING CRLF 4481592Srgrimes { 4491592Srgrimes if ($2 && $4 != NULL) 4501592Srgrimes send_file_list($4); 4511592Srgrimes if ($4 != NULL) 4521592Srgrimes free($4); 4531592Srgrimes } 4541592Srgrimes | LIST check_login CRLF 4551592Srgrimes { 4561592Srgrimes if ($2) 4571592Srgrimes retrieve("/bin/ls -lgA", ""); 4581592Srgrimes } 4591592Srgrimes | LIST check_login SP pathname CRLF 4601592Srgrimes { 4611592Srgrimes if ($2 && $4 != NULL) 4621592Srgrimes retrieve("/bin/ls -lgA %s", $4); 4631592Srgrimes if ($4 != NULL) 4641592Srgrimes free($4); 4651592Srgrimes } 4661592Srgrimes | STAT check_login SP pathname CRLF 4671592Srgrimes { 4681592Srgrimes if ($2 && $4 != NULL) 4691592Srgrimes statfilecmd($4); 4701592Srgrimes if ($4 != NULL) 4711592Srgrimes free($4); 4721592Srgrimes } 4731592Srgrimes | STAT CRLF 4741592Srgrimes { 4751592Srgrimes statcmd(); 4761592Srgrimes } 4771592Srgrimes | DELE check_login SP pathname CRLF 4781592Srgrimes { 4791592Srgrimes if ($2 && $4 != NULL) 4801592Srgrimes delete($4); 4811592Srgrimes if ($4 != NULL) 4821592Srgrimes free($4); 4831592Srgrimes } 48417433Spst | RNTO check_login SP pathname CRLF 4851592Srgrimes { 48617433Spst if ($2) { 48717433Spst if (fromname) { 48817433Spst renamecmd(fromname, $4); 48917433Spst free(fromname); 49017433Spst fromname = (char *) 0; 49117433Spst } else { 49217433Spst reply(503, "Bad sequence of commands."); 49317433Spst } 4941592Srgrimes } 49517433Spst free($4); 4961592Srgrimes } 4971592Srgrimes | ABOR CRLF 4981592Srgrimes { 4991592Srgrimes reply(225, "ABOR command successful."); 5001592Srgrimes } 5011592Srgrimes | CWD check_login CRLF 5021592Srgrimes { 5031592Srgrimes if ($2) 5041592Srgrimes cwd(pw->pw_dir); 5051592Srgrimes } 5061592Srgrimes | CWD check_login SP pathname CRLF 5071592Srgrimes { 5081592Srgrimes if ($2 && $4 != NULL) 5091592Srgrimes cwd($4); 5101592Srgrimes if ($4 != NULL) 5111592Srgrimes free($4); 5121592Srgrimes } 5131592Srgrimes | HELP CRLF 5141592Srgrimes { 5151592Srgrimes help(cmdtab, (char *) 0); 5161592Srgrimes } 5171592Srgrimes | HELP SP STRING CRLF 5181592Srgrimes { 5191592Srgrimes char *cp = $3; 5201592Srgrimes 5211592Srgrimes if (strncasecmp(cp, "SITE", 4) == 0) { 5221592Srgrimes cp = $3 + 4; 5231592Srgrimes if (*cp == ' ') 5241592Srgrimes cp++; 5251592Srgrimes if (*cp) 5261592Srgrimes help(sitetab, cp); 5271592Srgrimes else 5281592Srgrimes help(sitetab, (char *) 0); 5291592Srgrimes } else 5301592Srgrimes help(cmdtab, $3); 5311592Srgrimes } 5321592Srgrimes | NOOP CRLF 5331592Srgrimes { 5341592Srgrimes reply(200, "NOOP command successful."); 5351592Srgrimes } 5361592Srgrimes | MKD check_login SP pathname CRLF 5371592Srgrimes { 5381592Srgrimes if ($2 && $4 != NULL) 5391592Srgrimes makedir($4); 5401592Srgrimes if ($4 != NULL) 5411592Srgrimes free($4); 5421592Srgrimes } 5431592Srgrimes | RMD check_login SP pathname CRLF 5441592Srgrimes { 5451592Srgrimes if ($2 && $4 != NULL) 5461592Srgrimes removedir($4); 5471592Srgrimes if ($4 != NULL) 5481592Srgrimes free($4); 5491592Srgrimes } 5501592Srgrimes | PWD check_login CRLF 5511592Srgrimes { 5521592Srgrimes if ($2) 5531592Srgrimes pwd(); 5541592Srgrimes } 5551592Srgrimes | CDUP check_login CRLF 5561592Srgrimes { 5571592Srgrimes if ($2) 5581592Srgrimes cwd(".."); 5591592Srgrimes } 5601592Srgrimes | SITE SP HELP CRLF 5611592Srgrimes { 5621592Srgrimes help(sitetab, (char *) 0); 5631592Srgrimes } 5641592Srgrimes | SITE SP HELP SP STRING CRLF 5651592Srgrimes { 5661592Srgrimes help(sitetab, $5); 5671592Srgrimes } 5681592Srgrimes | SITE SP UMASK check_login CRLF 5691592Srgrimes { 5701592Srgrimes int oldmask; 5711592Srgrimes 5721592Srgrimes if ($4) { 5731592Srgrimes oldmask = umask(0); 5741592Srgrimes (void) umask(oldmask); 5751592Srgrimes reply(200, "Current UMASK is %03o", oldmask); 5761592Srgrimes } 5771592Srgrimes } 5781592Srgrimes | SITE SP UMASK check_login SP octal_number CRLF 5791592Srgrimes { 5801592Srgrimes int oldmask; 5811592Srgrimes 5821592Srgrimes if ($4) { 5831592Srgrimes if (($6 == -1) || ($6 > 0777)) { 5841592Srgrimes reply(501, "Bad UMASK value"); 5851592Srgrimes } else { 5861592Srgrimes oldmask = umask($6); 5871592Srgrimes reply(200, 5881592Srgrimes "UMASK set to %03o (was %03o)", 5891592Srgrimes $6, oldmask); 5901592Srgrimes } 5911592Srgrimes } 5921592Srgrimes } 5931592Srgrimes | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 5941592Srgrimes { 5951592Srgrimes if ($4 && ($8 != NULL)) { 5961592Srgrimes if ($6 > 0777) 5971592Srgrimes reply(501, 5981592Srgrimes "CHMOD: Mode value must be between 0 and 0777"); 5991592Srgrimes else if (chmod($8, $6) < 0) 6001592Srgrimes perror_reply(550, $8); 6011592Srgrimes else 6021592Srgrimes reply(200, "CHMOD command successful."); 6031592Srgrimes } 6041592Srgrimes if ($8 != NULL) 6051592Srgrimes free($8); 6061592Srgrimes } 6071592Srgrimes | SITE SP IDLE CRLF 6081592Srgrimes { 6091592Srgrimes reply(200, 6101592Srgrimes "Current IDLE time limit is %d seconds; max %d", 6111592Srgrimes timeout, maxtimeout); 6121592Srgrimes } 6131592Srgrimes | SITE SP IDLE SP NUMBER CRLF 6141592Srgrimes { 6151592Srgrimes if ($5 < 30 || $5 > maxtimeout) { 6161592Srgrimes reply(501, 6171592Srgrimes "Maximum IDLE time must be between 30 and %d seconds", 6181592Srgrimes maxtimeout); 6191592Srgrimes } else { 6201592Srgrimes timeout = $5; 6211592Srgrimes (void) alarm((unsigned) timeout); 6221592Srgrimes reply(200, 6231592Srgrimes "Maximum IDLE time set to %d seconds", 6241592Srgrimes timeout); 6251592Srgrimes } 6261592Srgrimes } 6271592Srgrimes | STOU check_login SP pathname CRLF 6281592Srgrimes { 6291592Srgrimes if ($2 && $4 != NULL) 6301592Srgrimes store($4, "w", 1); 6311592Srgrimes if ($4 != NULL) 6321592Srgrimes free($4); 6331592Srgrimes } 6341592Srgrimes | SYST CRLF 6351592Srgrimes { 6361592Srgrimes#ifdef unix 6371592Srgrimes#ifdef BSD 6381592Srgrimes reply(215, "UNIX Type: L%d Version: BSD-%d", 6391592Srgrimes NBBY, BSD); 6401592Srgrimes#else /* BSD */ 6411592Srgrimes reply(215, "UNIX Type: L%d", NBBY); 6421592Srgrimes#endif /* BSD */ 6431592Srgrimes#else /* unix */ 6441592Srgrimes reply(215, "UNKNOWN Type: L%d", NBBY); 6451592Srgrimes#endif /* unix */ 6461592Srgrimes } 6471592Srgrimes 6481592Srgrimes /* 6491592Srgrimes * SIZE is not in RFC959, but Postel has blessed it and 6501592Srgrimes * it will be in the updated RFC. 6511592Srgrimes * 6521592Srgrimes * Return size of file in a format suitable for 6531592Srgrimes * using with RESTART (we just count bytes). 6541592Srgrimes */ 6551592Srgrimes | SIZE check_login SP pathname CRLF 6561592Srgrimes { 6571592Srgrimes if ($2 && $4 != NULL) 6581592Srgrimes sizecmd($4); 6591592Srgrimes if ($4 != NULL) 6601592Srgrimes free($4); 6611592Srgrimes } 6621592Srgrimes 6631592Srgrimes /* 6641592Srgrimes * MDTM is not in RFC959, but Postel has blessed it and 6651592Srgrimes * it will be in the updated RFC. 6661592Srgrimes * 6671592Srgrimes * Return modification time of file as an ISO 3307 6681592Srgrimes * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 6691592Srgrimes * where xxx is the fractional second (of any precision, 6701592Srgrimes * not necessarily 3 digits) 6711592Srgrimes */ 6721592Srgrimes | MDTM check_login SP pathname CRLF 6731592Srgrimes { 6741592Srgrimes if ($2 && $4 != NULL) { 6751592Srgrimes struct stat stbuf; 6761592Srgrimes if (stat($4, &stbuf) < 0) 6771592Srgrimes reply(550, "%s: %s", 6781592Srgrimes $4, strerror(errno)); 6791592Srgrimes else if (!S_ISREG(stbuf.st_mode)) { 6801592Srgrimes reply(550, "%s: not a plain file.", $4); 6811592Srgrimes } else { 6821592Srgrimes struct tm *t; 6831592Srgrimes t = gmtime(&stbuf.st_mtime); 6841592Srgrimes reply(213, 68517435Spst "%04d%02d%02d%02d%02d%02d", 68617435Spst 1900 + t->tm_year, 68717435Spst t->tm_mon+1, t->tm_mday, 6881592Srgrimes t->tm_hour, t->tm_min, t->tm_sec); 6891592Srgrimes } 6901592Srgrimes } 6911592Srgrimes if ($4 != NULL) 6921592Srgrimes free($4); 6931592Srgrimes } 6941592Srgrimes | QUIT CRLF 6951592Srgrimes { 6961592Srgrimes reply(221, "Goodbye."); 6971592Srgrimes dologout(0); 6981592Srgrimes } 6991592Srgrimes | error CRLF 7001592Srgrimes { 7011592Srgrimes yyerrok; 7021592Srgrimes } 7031592Srgrimes ; 7041592Srgrimesrcmd 7051592Srgrimes : RNFR check_login SP pathname CRLF 7061592Srgrimes { 7071592Srgrimes char *renamefrom(); 7081592Srgrimes 7091592Srgrimes restart_point = (off_t) 0; 7101592Srgrimes if ($2 && $4) { 7111592Srgrimes fromname = renamefrom($4); 7121592Srgrimes if (fromname == (char *) 0 && $4) { 7131592Srgrimes free($4); 7141592Srgrimes } 7151592Srgrimes } 7161592Srgrimes } 7171592Srgrimes | REST SP byte_size CRLF 7181592Srgrimes { 7191592Srgrimes fromname = (char *) 0; 7201592Srgrimes restart_point = $3; /* XXX $3 is only "int" */ 7211592Srgrimes reply(350, "Restarting at %qd. %s", restart_point, 7221592Srgrimes "Send STORE or RETRIEVE to initiate transfer."); 7231592Srgrimes } 7241592Srgrimes ; 7251592Srgrimes 7261592Srgrimesusername 7271592Srgrimes : STRING 7281592Srgrimes ; 7291592Srgrimes 7301592Srgrimespassword 7311592Srgrimes : /* empty */ 7321592Srgrimes { 7331592Srgrimes $$ = (char *)calloc(1, sizeof(char)); 7341592Srgrimes } 7351592Srgrimes | STRING 7361592Srgrimes ; 7371592Srgrimes 7381592Srgrimesbyte_size 7391592Srgrimes : NUMBER 7401592Srgrimes ; 7411592Srgrimes 7421592Srgrimeshost_port 7431592Srgrimes : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 7441592Srgrimes NUMBER COMMA NUMBER 7451592Srgrimes { 7461592Srgrimes char *a, *p; 7471592Srgrimes 74856668Sshin data_dest.su_len = sizeof(struct sockaddr_in); 74956668Sshin data_dest.su_family = AF_INET; 75056668Sshin p = (char *)&data_dest.su_sin.sin_port; 75117435Spst p[0] = $9; p[1] = $11; 75256668Sshin a = (char *)&data_dest.su_sin.sin_addr; 7531592Srgrimes a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 7541592Srgrimes } 7551592Srgrimes ; 7561592Srgrimes 75756668Sshinhost_long_port 75856668Sshin : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 75956668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 76056668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 76156668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 76256668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 76356668Sshin NUMBER 76456668Sshin { 76556668Sshin char *a, *p; 76656668Sshin 76756668Sshin memset(&data_dest, 0, sizeof(data_dest)); 76856668Sshin data_dest.su_len = sizeof(struct sockaddr_in6); 76956668Sshin data_dest.su_family = AF_INET6; 77056668Sshin p = (char *)&data_dest.su_port; 77156668Sshin p[0] = $39; p[1] = $41; 77256668Sshin a = (char *)&data_dest.su_sin6.sin6_addr; 77356668Sshin a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 77456668Sshin a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19; 77556668Sshin a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27; 77656668Sshin a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35; 77756668Sshin if (his_addr.su_family == AF_INET6) { 77856668Sshin /* XXX more sanity checks! */ 77956668Sshin data_dest.su_sin6.sin6_scope_id = 78056668Sshin his_addr.su_sin6.sin6_scope_id; 78156668Sshin } 78256668Sshin if ($1 != 6 || $3 != 16 || $37 != 2) 78356668Sshin memset(&data_dest, 0, sizeof(data_dest)); 78456668Sshin } 78556668Sshin | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 78656668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 78756668Sshin NUMBER 78856668Sshin { 78956668Sshin char *a, *p; 79056668Sshin 79156668Sshin memset(&data_dest, 0, sizeof(data_dest)); 79256668Sshin data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 79356668Sshin data_dest.su_family = AF_INET; 79456668Sshin p = (char *)&data_dest.su_port; 79556668Sshin p[0] = $15; p[1] = $17; 79656668Sshin a = (char *)&data_dest.su_sin.sin_addr; 79756668Sshin a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 79856668Sshin if ($1 != 4 || $3 != 4 || $13 != 2) 79956668Sshin memset(&data_dest, 0, sizeof(data_dest)); 80056668Sshin } 80156668Sshin ; 80256668Sshin 8031592Srgrimesform_code 8041592Srgrimes : N 8051592Srgrimes { 8061592Srgrimes $$ = FORM_N; 8071592Srgrimes } 8081592Srgrimes | T 8091592Srgrimes { 8101592Srgrimes $$ = FORM_T; 8111592Srgrimes } 8121592Srgrimes | C 8131592Srgrimes { 8141592Srgrimes $$ = FORM_C; 8151592Srgrimes } 8161592Srgrimes ; 8171592Srgrimes 8181592Srgrimestype_code 8191592Srgrimes : A 8201592Srgrimes { 8211592Srgrimes cmd_type = TYPE_A; 8221592Srgrimes cmd_form = FORM_N; 8231592Srgrimes } 8241592Srgrimes | A SP form_code 8251592Srgrimes { 8261592Srgrimes cmd_type = TYPE_A; 8271592Srgrimes cmd_form = $3; 8281592Srgrimes } 8291592Srgrimes | E 8301592Srgrimes { 8311592Srgrimes cmd_type = TYPE_E; 8321592Srgrimes cmd_form = FORM_N; 8331592Srgrimes } 8341592Srgrimes | E SP form_code 8351592Srgrimes { 8361592Srgrimes cmd_type = TYPE_E; 8371592Srgrimes cmd_form = $3; 8381592Srgrimes } 8391592Srgrimes | I 8401592Srgrimes { 8411592Srgrimes cmd_type = TYPE_I; 8421592Srgrimes } 8431592Srgrimes | L 8441592Srgrimes { 8451592Srgrimes cmd_type = TYPE_L; 8461592Srgrimes cmd_bytesz = NBBY; 8471592Srgrimes } 8481592Srgrimes | L SP byte_size 8491592Srgrimes { 8501592Srgrimes cmd_type = TYPE_L; 8511592Srgrimes cmd_bytesz = $3; 8521592Srgrimes } 8531592Srgrimes /* this is for a bug in the BBN ftp */ 8541592Srgrimes | L byte_size 8551592Srgrimes { 8561592Srgrimes cmd_type = TYPE_L; 8571592Srgrimes cmd_bytesz = $2; 8581592Srgrimes } 8591592Srgrimes ; 8601592Srgrimes 8611592Srgrimesstruct_code 8621592Srgrimes : F 8631592Srgrimes { 8641592Srgrimes $$ = STRU_F; 8651592Srgrimes } 8661592Srgrimes | R 8671592Srgrimes { 8681592Srgrimes $$ = STRU_R; 8691592Srgrimes } 8701592Srgrimes | P 8711592Srgrimes { 8721592Srgrimes $$ = STRU_P; 8731592Srgrimes } 8741592Srgrimes ; 8751592Srgrimes 8761592Srgrimesmode_code 8771592Srgrimes : S 8781592Srgrimes { 8791592Srgrimes $$ = MODE_S; 8801592Srgrimes } 8811592Srgrimes | B 8821592Srgrimes { 8831592Srgrimes $$ = MODE_B; 8841592Srgrimes } 8851592Srgrimes | C 8861592Srgrimes { 8871592Srgrimes $$ = MODE_C; 8881592Srgrimes } 8891592Srgrimes ; 8901592Srgrimes 8911592Srgrimespathname 8921592Srgrimes : pathstring 8931592Srgrimes { 8941592Srgrimes /* 8951592Srgrimes * Problem: this production is used for all pathname 8961592Srgrimes * processing, but only gives a 550 error reply. 8971592Srgrimes * This is a valid reply in some cases but not in others. 8981592Srgrimes */ 8991592Srgrimes if (logged_in && $1 && *$1 == '~') { 9001592Srgrimes glob_t gl; 9011592Srgrimes int flags = 9021592Srgrimes GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 9031592Srgrimes 9041592Srgrimes memset(&gl, 0, sizeof(gl)); 9051592Srgrimes if (glob($1, flags, NULL, &gl) || 9061592Srgrimes gl.gl_pathc == 0) { 9071592Srgrimes reply(550, "not found"); 9081592Srgrimes $$ = NULL; 9091592Srgrimes } else { 9101592Srgrimes $$ = strdup(gl.gl_pathv[0]); 9111592Srgrimes } 9121592Srgrimes globfree(&gl); 9131592Srgrimes free($1); 9141592Srgrimes } else 9151592Srgrimes $$ = $1; 9161592Srgrimes } 9171592Srgrimes ; 9181592Srgrimes 9191592Srgrimespathstring 9201592Srgrimes : STRING 9211592Srgrimes ; 9221592Srgrimes 9231592Srgrimesoctal_number 9241592Srgrimes : NUMBER 9251592Srgrimes { 9261592Srgrimes int ret, dec, multby, digit; 9271592Srgrimes 9281592Srgrimes /* 9291592Srgrimes * Convert a number that was read as decimal number 9301592Srgrimes * to what it would be if it had been read as octal. 9311592Srgrimes */ 9321592Srgrimes dec = $1; 9331592Srgrimes multby = 1; 9341592Srgrimes ret = 0; 9351592Srgrimes while (dec) { 9361592Srgrimes digit = dec%10; 9371592Srgrimes if (digit > 7) { 9381592Srgrimes ret = -1; 9391592Srgrimes break; 9401592Srgrimes } 9411592Srgrimes ret += digit * multby; 9421592Srgrimes multby *= 8; 9431592Srgrimes dec /= 10; 9441592Srgrimes } 9451592Srgrimes $$ = ret; 9461592Srgrimes } 9471592Srgrimes ; 9481592Srgrimes 9491592Srgrimes 9501592Srgrimescheck_login 9511592Srgrimes : /* empty */ 9521592Srgrimes { 9531592Srgrimes if (logged_in) 9541592Srgrimes $$ = 1; 9551592Srgrimes else { 9561592Srgrimes reply(530, "Please login with USER and PASS."); 9571592Srgrimes $$ = 0; 9581592Srgrimes } 9591592Srgrimes } 9601592Srgrimes ; 9611592Srgrimes 9621592Srgrimes%% 9631592Srgrimes 9641592Srgrimesextern jmp_buf errcatch; 9651592Srgrimes 9661592Srgrimes#define CMD 0 /* beginning of command */ 9671592Srgrimes#define ARGS 1 /* expect miscellaneous arguments */ 9681592Srgrimes#define STR1 2 /* expect SP followed by STRING */ 9691592Srgrimes#define STR2 3 /* expect STRING */ 9701592Srgrimes#define OSTR 4 /* optional SP then STRING */ 9711592Srgrimes#define ZSTR1 5 /* SP then optional STRING */ 9721592Srgrimes#define ZSTR2 6 /* optional STRING after SP */ 9731592Srgrimes#define SITECMD 7 /* SITE command */ 9741592Srgrimes#define NSTR 8 /* Number followed by a string */ 9751592Srgrimes 9761592Srgrimesstruct tab { 9771592Srgrimes char *name; 9781592Srgrimes short token; 9791592Srgrimes short state; 9801592Srgrimes short implemented; /* 1 if command is implemented */ 9811592Srgrimes char *help; 9821592Srgrimes}; 9831592Srgrimes 9841592Srgrimesstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 9851592Srgrimes { "USER", USER, STR1, 1, "<sp> username" }, 9861592Srgrimes { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 9871592Srgrimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 9881592Srgrimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 9891592Srgrimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 9901592Srgrimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 9911592Srgrimes { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 99256668Sshin { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 99356668Sshin { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 9941592Srgrimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 99556668Sshin { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 99656668Sshin { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 9971592Srgrimes { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 9981592Srgrimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 9991592Srgrimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 10001592Srgrimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 10011592Srgrimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 10021592Srgrimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 10031592Srgrimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 10041592Srgrimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 10051592Srgrimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 10061592Srgrimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 10071592Srgrimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 10081592Srgrimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 10091592Srgrimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 10101592Srgrimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 10111592Srgrimes { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 10121592Srgrimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 10131592Srgrimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 10141592Srgrimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 10151592Srgrimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 10161592Srgrimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 10171592Srgrimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 10181592Srgrimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 10191592Srgrimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 10201592Srgrimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 10211592Srgrimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 10221592Srgrimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 10231592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 10241592Srgrimes { "NOOP", NOOP, ARGS, 1, "" }, 10251592Srgrimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 10261592Srgrimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 10271592Srgrimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 10281592Srgrimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 10291592Srgrimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 10301592Srgrimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 10311592Srgrimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 10321592Srgrimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 10331592Srgrimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 10341592Srgrimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 10351592Srgrimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 10361592Srgrimes { NULL, 0, 0, 0, 0 } 10371592Srgrimes}; 10381592Srgrimes 10391592Srgrimesstruct tab sitetab[] = { 10401592Srgrimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 10411592Srgrimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 10421592Srgrimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 10431592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 10441592Srgrimes { NULL, 0, 0, 0, 0 } 10451592Srgrimes}; 10461592Srgrimes 10471592Srgrimesstatic char *copy __P((char *)); 10481592Srgrimesstatic void help __P((struct tab *, char *)); 10491592Srgrimesstatic struct tab * 10501592Srgrimes lookup __P((struct tab *, char *)); 105156668Sshinstatic int port_check __P((const char *)); 105256668Sshinstatic int port_check_v6 __P((const char *)); 10531592Srgrimesstatic void sizecmd __P((char *)); 10541592Srgrimesstatic void toolong __P((int)); 105556668Sshinstatic void v4map_data_dest __P((void)); 10561592Srgrimesstatic int yylex __P((void)); 10571592Srgrimes 10581592Srgrimesstatic struct tab * 10591592Srgrimeslookup(p, cmd) 10601592Srgrimes struct tab *p; 10611592Srgrimes char *cmd; 10621592Srgrimes{ 10631592Srgrimes 10641592Srgrimes for (; p->name != NULL; p++) 10651592Srgrimes if (strcmp(cmd, p->name) == 0) 10661592Srgrimes return (p); 10671592Srgrimes return (0); 10681592Srgrimes} 10691592Srgrimes 10701592Srgrimes#include <arpa/telnet.h> 10711592Srgrimes 10721592Srgrimes/* 10731592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes. 10741592Srgrimes */ 10751592Srgrimeschar * 10761592Srgrimesgetline(s, n, iop) 10771592Srgrimes char *s; 10781592Srgrimes int n; 10791592Srgrimes FILE *iop; 10801592Srgrimes{ 10811592Srgrimes int c; 10821592Srgrimes register char *cs; 10831592Srgrimes 10841592Srgrimes cs = s; 10851592Srgrimes/* tmpline may contain saved command from urgent mode interruption */ 10861592Srgrimes for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 10871592Srgrimes *cs++ = tmpline[c]; 10881592Srgrimes if (tmpline[c] == '\n') { 10891592Srgrimes *cs++ = '\0'; 10901592Srgrimes if (debug) 10911592Srgrimes syslog(LOG_DEBUG, "command: %s", s); 10921592Srgrimes tmpline[0] = '\0'; 10931592Srgrimes return(s); 10941592Srgrimes } 10951592Srgrimes if (c == 0) 10961592Srgrimes tmpline[0] = '\0'; 10971592Srgrimes } 10981592Srgrimes while ((c = getc(iop)) != EOF) { 10991592Srgrimes c &= 0377; 11001592Srgrimes if (c == IAC) { 11011592Srgrimes if ((c = getc(iop)) != EOF) { 11021592Srgrimes c &= 0377; 11031592Srgrimes switch (c) { 11041592Srgrimes case WILL: 11051592Srgrimes case WONT: 11061592Srgrimes c = getc(iop); 11071592Srgrimes printf("%c%c%c", IAC, DONT, 0377&c); 11081592Srgrimes (void) fflush(stdout); 11091592Srgrimes continue; 11101592Srgrimes case DO: 11111592Srgrimes case DONT: 11121592Srgrimes c = getc(iop); 11131592Srgrimes printf("%c%c%c", IAC, WONT, 0377&c); 11141592Srgrimes (void) fflush(stdout); 11151592Srgrimes continue; 11161592Srgrimes case IAC: 11171592Srgrimes break; 11181592Srgrimes default: 11191592Srgrimes continue; /* ignore command */ 11201592Srgrimes } 11211592Srgrimes } 11221592Srgrimes } 11231592Srgrimes *cs++ = c; 11241592Srgrimes if (--n <= 0 || c == '\n') 11251592Srgrimes break; 11261592Srgrimes } 11271592Srgrimes if (c == EOF && cs == s) 11281592Srgrimes return (NULL); 11291592Srgrimes *cs++ = '\0'; 11301592Srgrimes if (debug) { 11311592Srgrimes if (!guest && strncasecmp("pass ", s, 5) == 0) { 11321592Srgrimes /* Don't syslog passwords */ 11331592Srgrimes syslog(LOG_DEBUG, "command: %.5s ???", s); 11341592Srgrimes } else { 11351592Srgrimes register char *cp; 11361592Srgrimes register int len; 11371592Srgrimes 11381592Srgrimes /* Don't syslog trailing CR-LF */ 11391592Srgrimes len = strlen(s); 11401592Srgrimes cp = s + len - 1; 11411592Srgrimes while (cp >= s && (*cp == '\n' || *cp == '\r')) { 11421592Srgrimes --cp; 11431592Srgrimes --len; 11441592Srgrimes } 11451592Srgrimes syslog(LOG_DEBUG, "command: %.*s", len, s); 11461592Srgrimes } 11471592Srgrimes } 11481592Srgrimes return (s); 11491592Srgrimes} 11501592Srgrimes 11511592Srgrimesstatic void 11521592Srgrimestoolong(signo) 11531592Srgrimes int signo; 11541592Srgrimes{ 11551592Srgrimes 11561592Srgrimes reply(421, 11571592Srgrimes "Timeout (%d seconds): closing control connection.", timeout); 11581592Srgrimes if (logging) 11591592Srgrimes syslog(LOG_INFO, "User %s timed out after %d seconds", 11601592Srgrimes (pw ? pw -> pw_name : "unknown"), timeout); 11611592Srgrimes dologout(1); 11621592Srgrimes} 11631592Srgrimes 11641592Srgrimesstatic int 11651592Srgrimesyylex() 11661592Srgrimes{ 11671592Srgrimes static int cpos, state; 11681592Srgrimes char *cp, *cp2; 11691592Srgrimes struct tab *p; 11701592Srgrimes int n; 11711592Srgrimes char c; 11721592Srgrimes 11731592Srgrimes for (;;) { 11741592Srgrimes switch (state) { 11751592Srgrimes 11761592Srgrimes case CMD: 11771592Srgrimes (void) signal(SIGALRM, toolong); 11781592Srgrimes (void) alarm((unsigned) timeout); 11791592Srgrimes if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 11801592Srgrimes reply(221, "You could at least say goodbye."); 11811592Srgrimes dologout(0); 11821592Srgrimes } 11831592Srgrimes (void) alarm(0); 11841592Srgrimes#ifdef SETPROCTITLE 118529574Sphk if (strncasecmp(cbuf, "PASS", 4) != 0) 11861592Srgrimes setproctitle("%s: %s", proctitle, cbuf); 11871592Srgrimes#endif /* SETPROCTITLE */ 11881592Srgrimes if ((cp = strchr(cbuf, '\r'))) { 11891592Srgrimes *cp++ = '\n'; 11901592Srgrimes *cp = '\0'; 11911592Srgrimes } 11921592Srgrimes if ((cp = strpbrk(cbuf, " \n"))) 11931592Srgrimes cpos = cp - cbuf; 11941592Srgrimes if (cpos == 0) 11951592Srgrimes cpos = 4; 11961592Srgrimes c = cbuf[cpos]; 11971592Srgrimes cbuf[cpos] = '\0'; 11981592Srgrimes upper(cbuf); 11991592Srgrimes p = lookup(cmdtab, cbuf); 12001592Srgrimes cbuf[cpos] = c; 12013776Spst if (p != 0) { 12021592Srgrimes if (p->implemented == 0) { 12031592Srgrimes nack(p->name); 12041592Srgrimes longjmp(errcatch,0); 12051592Srgrimes /* NOTREACHED */ 12061592Srgrimes } 12071592Srgrimes state = p->state; 12081592Srgrimes yylval.s = p->name; 12091592Srgrimes return (p->token); 12101592Srgrimes } 12111592Srgrimes break; 12121592Srgrimes 12131592Srgrimes case SITECMD: 12141592Srgrimes if (cbuf[cpos] == ' ') { 12151592Srgrimes cpos++; 12161592Srgrimes return (SP); 12171592Srgrimes } 12181592Srgrimes cp = &cbuf[cpos]; 12191592Srgrimes if ((cp2 = strpbrk(cp, " \n"))) 12201592Srgrimes cpos = cp2 - cbuf; 12211592Srgrimes c = cbuf[cpos]; 12221592Srgrimes cbuf[cpos] = '\0'; 12231592Srgrimes upper(cp); 12241592Srgrimes p = lookup(sitetab, cp); 12251592Srgrimes cbuf[cpos] = c; 12263777Spst if (guest == 0 && p != 0) { 12271592Srgrimes if (p->implemented == 0) { 12281592Srgrimes state = CMD; 12291592Srgrimes nack(p->name); 12301592Srgrimes longjmp(errcatch,0); 12311592Srgrimes /* NOTREACHED */ 12321592Srgrimes } 12331592Srgrimes state = p->state; 12341592Srgrimes yylval.s = p->name; 12351592Srgrimes return (p->token); 12361592Srgrimes } 12371592Srgrimes state = CMD; 12381592Srgrimes break; 12391592Srgrimes 12401592Srgrimes case OSTR: 12411592Srgrimes if (cbuf[cpos] == '\n') { 12421592Srgrimes state = CMD; 12431592Srgrimes return (CRLF); 12441592Srgrimes } 12451592Srgrimes /* FALLTHROUGH */ 12461592Srgrimes 12471592Srgrimes case STR1: 12481592Srgrimes case ZSTR1: 12491592Srgrimes dostr1: 12501592Srgrimes if (cbuf[cpos] == ' ') { 12511592Srgrimes cpos++; 125251979Salfred state = state == OSTR ? STR2 : state+1; 12531592Srgrimes return (SP); 12541592Srgrimes } 12551592Srgrimes break; 12561592Srgrimes 12571592Srgrimes case ZSTR2: 12581592Srgrimes if (cbuf[cpos] == '\n') { 12591592Srgrimes state = CMD; 12601592Srgrimes return (CRLF); 12611592Srgrimes } 12621592Srgrimes /* FALLTHROUGH */ 12631592Srgrimes 12641592Srgrimes case STR2: 12651592Srgrimes cp = &cbuf[cpos]; 12661592Srgrimes n = strlen(cp); 12671592Srgrimes cpos += n - 1; 12681592Srgrimes /* 12691592Srgrimes * Make sure the string is nonempty and \n terminated. 12701592Srgrimes */ 12711592Srgrimes if (n > 1 && cbuf[cpos] == '\n') { 12721592Srgrimes cbuf[cpos] = '\0'; 12731592Srgrimes yylval.s = copy(cp); 12741592Srgrimes cbuf[cpos] = '\n'; 12751592Srgrimes state = ARGS; 12761592Srgrimes return (STRING); 12771592Srgrimes } 12781592Srgrimes break; 12791592Srgrimes 12801592Srgrimes case NSTR: 12811592Srgrimes if (cbuf[cpos] == ' ') { 12821592Srgrimes cpos++; 12831592Srgrimes return (SP); 12841592Srgrimes } 12851592Srgrimes if (isdigit(cbuf[cpos])) { 12861592Srgrimes cp = &cbuf[cpos]; 12871592Srgrimes while (isdigit(cbuf[++cpos])) 12881592Srgrimes ; 12891592Srgrimes c = cbuf[cpos]; 12901592Srgrimes cbuf[cpos] = '\0'; 12911592Srgrimes yylval.i = atoi(cp); 12921592Srgrimes cbuf[cpos] = c; 12931592Srgrimes state = STR1; 12941592Srgrimes return (NUMBER); 12951592Srgrimes } 12961592Srgrimes state = STR1; 12971592Srgrimes goto dostr1; 12981592Srgrimes 12991592Srgrimes case ARGS: 13001592Srgrimes if (isdigit(cbuf[cpos])) { 13011592Srgrimes cp = &cbuf[cpos]; 13021592Srgrimes while (isdigit(cbuf[++cpos])) 13031592Srgrimes ; 13041592Srgrimes c = cbuf[cpos]; 13051592Srgrimes cbuf[cpos] = '\0'; 13061592Srgrimes yylval.i = atoi(cp); 13071592Srgrimes cbuf[cpos] = c; 13081592Srgrimes return (NUMBER); 13091592Srgrimes } 131056668Sshin if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 131156668Sshin && !isalnum(cbuf[cpos + 3])) { 131256668Sshin cpos += 3; 131356668Sshin return ALL; 131456668Sshin } 13151592Srgrimes switch (cbuf[cpos++]) { 13161592Srgrimes 13171592Srgrimes case '\n': 13181592Srgrimes state = CMD; 13191592Srgrimes return (CRLF); 13201592Srgrimes 13211592Srgrimes case ' ': 13221592Srgrimes return (SP); 13231592Srgrimes 13241592Srgrimes case ',': 13251592Srgrimes return (COMMA); 13261592Srgrimes 13271592Srgrimes case 'A': 13281592Srgrimes case 'a': 13291592Srgrimes return (A); 13301592Srgrimes 13311592Srgrimes case 'B': 13321592Srgrimes case 'b': 13331592Srgrimes return (B); 13341592Srgrimes 13351592Srgrimes case 'C': 13361592Srgrimes case 'c': 13371592Srgrimes return (C); 13381592Srgrimes 13391592Srgrimes case 'E': 13401592Srgrimes case 'e': 13411592Srgrimes return (E); 13421592Srgrimes 13431592Srgrimes case 'F': 13441592Srgrimes case 'f': 13451592Srgrimes return (F); 13461592Srgrimes 13471592Srgrimes case 'I': 13481592Srgrimes case 'i': 13491592Srgrimes return (I); 13501592Srgrimes 13511592Srgrimes case 'L': 13521592Srgrimes case 'l': 13531592Srgrimes return (L); 13541592Srgrimes 13551592Srgrimes case 'N': 13561592Srgrimes case 'n': 13571592Srgrimes return (N); 13581592Srgrimes 13591592Srgrimes case 'P': 13601592Srgrimes case 'p': 13611592Srgrimes return (P); 13621592Srgrimes 13631592Srgrimes case 'R': 13641592Srgrimes case 'r': 13651592Srgrimes return (R); 13661592Srgrimes 13671592Srgrimes case 'S': 13681592Srgrimes case 's': 13691592Srgrimes return (S); 13701592Srgrimes 13711592Srgrimes case 'T': 13721592Srgrimes case 't': 13731592Srgrimes return (T); 13741592Srgrimes 13751592Srgrimes } 13761592Srgrimes break; 13771592Srgrimes 13781592Srgrimes default: 13791592Srgrimes fatal("Unknown state in scanner."); 13801592Srgrimes } 13811592Srgrimes yyerror((char *) 0); 13821592Srgrimes state = CMD; 13831592Srgrimes longjmp(errcatch,0); 13841592Srgrimes } 13851592Srgrimes} 13861592Srgrimes 13871592Srgrimesvoid 13881592Srgrimesupper(s) 13891592Srgrimes char *s; 13901592Srgrimes{ 13911592Srgrimes while (*s != '\0') { 13921592Srgrimes if (islower(*s)) 13931592Srgrimes *s = toupper(*s); 13941592Srgrimes s++; 13951592Srgrimes } 13961592Srgrimes} 13971592Srgrimes 13981592Srgrimesstatic char * 13991592Srgrimescopy(s) 14001592Srgrimes char *s; 14011592Srgrimes{ 14021592Srgrimes char *p; 14031592Srgrimes 14041592Srgrimes p = malloc((unsigned) strlen(s) + 1); 14051592Srgrimes if (p == NULL) 14061592Srgrimes fatal("Ran out of memory."); 14071592Srgrimes (void) strcpy(p, s); 14081592Srgrimes return (p); 14091592Srgrimes} 14101592Srgrimes 14111592Srgrimesstatic void 14121592Srgrimeshelp(ctab, s) 14131592Srgrimes struct tab *ctab; 14141592Srgrimes char *s; 14151592Srgrimes{ 14161592Srgrimes struct tab *c; 14171592Srgrimes int width, NCMDS; 14181592Srgrimes char *type; 14191592Srgrimes 14201592Srgrimes if (ctab == sitetab) 14211592Srgrimes type = "SITE "; 14221592Srgrimes else 14231592Srgrimes type = ""; 14241592Srgrimes width = 0, NCMDS = 0; 14251592Srgrimes for (c = ctab; c->name != NULL; c++) { 14261592Srgrimes int len = strlen(c->name); 14271592Srgrimes 14281592Srgrimes if (len > width) 14291592Srgrimes width = len; 14301592Srgrimes NCMDS++; 14311592Srgrimes } 14321592Srgrimes width = (width + 8) &~ 7; 14331592Srgrimes if (s == 0) { 14341592Srgrimes int i, j, w; 14351592Srgrimes int columns, lines; 14361592Srgrimes 14371592Srgrimes lreply(214, "The following %scommands are recognized %s.", 14381592Srgrimes type, "(* =>'s unimplemented)"); 14391592Srgrimes columns = 76 / width; 14401592Srgrimes if (columns == 0) 14411592Srgrimes columns = 1; 14421592Srgrimes lines = (NCMDS + columns - 1) / columns; 14431592Srgrimes for (i = 0; i < lines; i++) { 14441592Srgrimes printf(" "); 14451592Srgrimes for (j = 0; j < columns; j++) { 14461592Srgrimes c = ctab + j * lines + i; 14471592Srgrimes printf("%s%c", c->name, 14481592Srgrimes c->implemented ? ' ' : '*'); 14491592Srgrimes if (c + lines >= &ctab[NCMDS]) 14501592Srgrimes break; 14511592Srgrimes w = strlen(c->name) + 1; 14521592Srgrimes while (w < width) { 14531592Srgrimes putchar(' '); 14541592Srgrimes w++; 14551592Srgrimes } 14561592Srgrimes } 14571592Srgrimes printf("\r\n"); 14581592Srgrimes } 14591592Srgrimes (void) fflush(stdout); 14601592Srgrimes reply(214, "Direct comments to ftp-bugs@%s.", hostname); 14611592Srgrimes return; 14621592Srgrimes } 14631592Srgrimes upper(s); 14641592Srgrimes c = lookup(ctab, s); 14651592Srgrimes if (c == (struct tab *)0) { 14661592Srgrimes reply(502, "Unknown command %s.", s); 14671592Srgrimes return; 14681592Srgrimes } 14691592Srgrimes if (c->implemented) 14701592Srgrimes reply(214, "Syntax: %s%s %s", type, c->name, c->help); 14711592Srgrimes else 14721592Srgrimes reply(214, "%s%-*s\t%s; unimplemented.", type, width, 14731592Srgrimes c->name, c->help); 14741592Srgrimes} 14751592Srgrimes 14761592Srgrimesstatic void 14771592Srgrimessizecmd(filename) 14781592Srgrimes char *filename; 14791592Srgrimes{ 14801592Srgrimes switch (type) { 14811592Srgrimes case TYPE_L: 14821592Srgrimes case TYPE_I: { 14831592Srgrimes struct stat stbuf; 14841592Srgrimes if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) 14851592Srgrimes reply(550, "%s: not a plain file.", filename); 14861592Srgrimes else 14871592Srgrimes reply(213, "%qu", stbuf.st_size); 14881592Srgrimes break; } 14891592Srgrimes case TYPE_A: { 14901592Srgrimes FILE *fin; 14911592Srgrimes int c; 14921592Srgrimes off_t count; 14931592Srgrimes struct stat stbuf; 14941592Srgrimes fin = fopen(filename, "r"); 14951592Srgrimes if (fin == NULL) { 14961592Srgrimes perror_reply(550, filename); 14971592Srgrimes return; 14981592Srgrimes } 14991592Srgrimes if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { 15001592Srgrimes reply(550, "%s: not a plain file.", filename); 15011592Srgrimes (void) fclose(fin); 15021592Srgrimes return; 15031592Srgrimes } 15041592Srgrimes 15051592Srgrimes count = 0; 15061592Srgrimes while((c=getc(fin)) != EOF) { 15071592Srgrimes if (c == '\n') /* will get expanded to \r\n */ 15081592Srgrimes count++; 15091592Srgrimes count++; 15101592Srgrimes } 15111592Srgrimes (void) fclose(fin); 15121592Srgrimes 15131592Srgrimes reply(213, "%qd", count); 15141592Srgrimes break; } 15151592Srgrimes default: 15161592Srgrimes reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 15171592Srgrimes } 15181592Srgrimes} 151956668Sshin 152056668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 152156668Sshinstatic int 152256668Sshinport_check(pcmd) 152356668Sshin const char *pcmd; 152456668Sshin{ 152556668Sshin if (his_addr.su_family == AF_INET) { 152656668Sshin if (data_dest.su_family != AF_INET) { 152756668Sshin usedefault = 1; 152856668Sshin reply(500, "Invalid address rejected."); 152956668Sshin return 1; 153056668Sshin } 153156668Sshin if (paranoid && 153256668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 153356668Sshin memcmp(&data_dest.su_sin.sin_addr, 153456668Sshin &his_addr.su_sin.sin_addr, 153556668Sshin sizeof(data_dest.su_sin.sin_addr)))) { 153656668Sshin usedefault = 1; 153756668Sshin reply(500, "Illegal PORT range rejected."); 153856668Sshin } else { 153956668Sshin usedefault = 0; 154056668Sshin if (pdata >= 0) { 154156668Sshin (void) close(pdata); 154256668Sshin pdata = -1; 154356668Sshin } 154456668Sshin reply(200, "%s command successful.", pcmd); 154556668Sshin } 154656668Sshin return 1; 154756668Sshin } 154856668Sshin return 0; 154956668Sshin} 155056668Sshin 155156668Sshin#ifdef INET6 155256668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 155356668Sshinstatic int 155456668Sshinport_check_v6(pcmd) 155556668Sshin const char *pcmd; 155656668Sshin{ 155756668Sshin if (his_addr.su_family == AF_INET6) { 155856668Sshin if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 155956668Sshin /* Convert data_dest into v4 mapped sockaddr.*/ 156056668Sshin v4map_data_dest(); 156156668Sshin if (data_dest.su_family != AF_INET6) { 156256668Sshin usedefault = 1; 156356668Sshin reply(500, "Invalid address rejected."); 156456668Sshin return 1; 156556668Sshin } 156656668Sshin if (paranoid && 156756668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 156856668Sshin memcmp(&data_dest.su_sin6.sin6_addr, 156956668Sshin &his_addr.su_sin6.sin6_addr, 157056668Sshin sizeof(data_dest.su_sin6.sin6_addr)))) { 157156668Sshin usedefault = 1; 157256668Sshin reply(500, "Illegal PORT range rejected."); 157356668Sshin } else { 157456668Sshin usedefault = 0; 157556668Sshin if (pdata >= 0) { 157656668Sshin (void) close(pdata); 157756668Sshin pdata = -1; 157856668Sshin } 157956668Sshin reply(200, "%s command successful.", pcmd); 158056668Sshin } 158156668Sshin return 1; 158256668Sshin } 158356668Sshin return 0; 158456668Sshin} 158556668Sshin 158656668Sshinstatic void 158756668Sshinv4map_data_dest() 158856668Sshin{ 158956668Sshin struct in_addr savedaddr; 159056668Sshin int savedport; 159156668Sshin 159256668Sshin if (data_dest.su_family != AF_INET) { 159356668Sshin usedefault = 1; 159456668Sshin reply(500, "Invalid address rejected."); 159556668Sshin return; 159656668Sshin } 159756668Sshin 159856668Sshin savedaddr = data_dest.su_sin.sin_addr; 159956668Sshin savedport = data_dest.su_port; 160056668Sshin 160156668Sshin memset(&data_dest, 0, sizeof(data_dest)); 160256668Sshin data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 160356668Sshin data_dest.su_sin6.sin6_family = AF_INET6; 160456668Sshin data_dest.su_sin6.sin6_port = savedport; 160556668Sshin memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 160656668Sshin memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 160756668Sshin (caddr_t)&savedaddr, sizeof(savedaddr)); 160856668Sshin} 160956668Sshin#endif 1610