ftpcmd.y revision 69234
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 69234 2000-11-26 23:33:36Z danny $"; 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 { 50369234Sdanny if ($2) { 50469234Sdanny if (guest) 50569234Sdanny cwd("/"); 50669234Sdanny else 50769234Sdanny cwd(pw->pw_dir); 50869234Sdanny } 5091592Srgrimes } 5101592Srgrimes | CWD check_login SP pathname CRLF 5111592Srgrimes { 5121592Srgrimes if ($2 && $4 != NULL) 5131592Srgrimes cwd($4); 5141592Srgrimes if ($4 != NULL) 5151592Srgrimes free($4); 5161592Srgrimes } 5171592Srgrimes | HELP CRLF 5181592Srgrimes { 5191592Srgrimes help(cmdtab, (char *) 0); 5201592Srgrimes } 5211592Srgrimes | HELP SP STRING CRLF 5221592Srgrimes { 5231592Srgrimes char *cp = $3; 5241592Srgrimes 5251592Srgrimes if (strncasecmp(cp, "SITE", 4) == 0) { 5261592Srgrimes cp = $3 + 4; 5271592Srgrimes if (*cp == ' ') 5281592Srgrimes cp++; 5291592Srgrimes if (*cp) 5301592Srgrimes help(sitetab, cp); 5311592Srgrimes else 5321592Srgrimes help(sitetab, (char *) 0); 5331592Srgrimes } else 5341592Srgrimes help(cmdtab, $3); 5351592Srgrimes } 5361592Srgrimes | NOOP CRLF 5371592Srgrimes { 5381592Srgrimes reply(200, "NOOP command successful."); 5391592Srgrimes } 5401592Srgrimes | MKD check_login SP pathname CRLF 5411592Srgrimes { 5421592Srgrimes if ($2 && $4 != NULL) 5431592Srgrimes makedir($4); 5441592Srgrimes if ($4 != NULL) 5451592Srgrimes free($4); 5461592Srgrimes } 5471592Srgrimes | RMD check_login SP pathname CRLF 5481592Srgrimes { 5491592Srgrimes if ($2 && $4 != NULL) 5501592Srgrimes removedir($4); 5511592Srgrimes if ($4 != NULL) 5521592Srgrimes free($4); 5531592Srgrimes } 5541592Srgrimes | PWD check_login CRLF 5551592Srgrimes { 5561592Srgrimes if ($2) 5571592Srgrimes pwd(); 5581592Srgrimes } 5591592Srgrimes | CDUP check_login CRLF 5601592Srgrimes { 5611592Srgrimes if ($2) 5621592Srgrimes cwd(".."); 5631592Srgrimes } 5641592Srgrimes | SITE SP HELP CRLF 5651592Srgrimes { 5661592Srgrimes help(sitetab, (char *) 0); 5671592Srgrimes } 5681592Srgrimes | SITE SP HELP SP STRING CRLF 5691592Srgrimes { 5701592Srgrimes help(sitetab, $5); 5711592Srgrimes } 5721592Srgrimes | SITE SP UMASK check_login CRLF 5731592Srgrimes { 5741592Srgrimes int oldmask; 5751592Srgrimes 5761592Srgrimes if ($4) { 5771592Srgrimes oldmask = umask(0); 5781592Srgrimes (void) umask(oldmask); 5791592Srgrimes reply(200, "Current UMASK is %03o", oldmask); 5801592Srgrimes } 5811592Srgrimes } 5821592Srgrimes | SITE SP UMASK check_login SP octal_number CRLF 5831592Srgrimes { 5841592Srgrimes int oldmask; 5851592Srgrimes 5861592Srgrimes if ($4) { 5871592Srgrimes if (($6 == -1) || ($6 > 0777)) { 5881592Srgrimes reply(501, "Bad UMASK value"); 5891592Srgrimes } else { 5901592Srgrimes oldmask = umask($6); 5911592Srgrimes reply(200, 5921592Srgrimes "UMASK set to %03o (was %03o)", 5931592Srgrimes $6, oldmask); 5941592Srgrimes } 5951592Srgrimes } 5961592Srgrimes } 5971592Srgrimes | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 5981592Srgrimes { 5991592Srgrimes if ($4 && ($8 != NULL)) { 6001592Srgrimes if ($6 > 0777) 6011592Srgrimes reply(501, 6021592Srgrimes "CHMOD: Mode value must be between 0 and 0777"); 6031592Srgrimes else if (chmod($8, $6) < 0) 6041592Srgrimes perror_reply(550, $8); 6051592Srgrimes else 6061592Srgrimes reply(200, "CHMOD command successful."); 6071592Srgrimes } 6081592Srgrimes if ($8 != NULL) 6091592Srgrimes free($8); 6101592Srgrimes } 6111592Srgrimes | SITE SP IDLE CRLF 6121592Srgrimes { 6131592Srgrimes reply(200, 6141592Srgrimes "Current IDLE time limit is %d seconds; max %d", 6151592Srgrimes timeout, maxtimeout); 6161592Srgrimes } 6171592Srgrimes | SITE SP IDLE SP NUMBER CRLF 6181592Srgrimes { 6191592Srgrimes if ($5 < 30 || $5 > maxtimeout) { 6201592Srgrimes reply(501, 6211592Srgrimes "Maximum IDLE time must be between 30 and %d seconds", 6221592Srgrimes maxtimeout); 6231592Srgrimes } else { 6241592Srgrimes timeout = $5; 6251592Srgrimes (void) alarm((unsigned) timeout); 6261592Srgrimes reply(200, 6271592Srgrimes "Maximum IDLE time set to %d seconds", 6281592Srgrimes timeout); 6291592Srgrimes } 6301592Srgrimes } 6311592Srgrimes | STOU check_login SP pathname CRLF 6321592Srgrimes { 6331592Srgrimes if ($2 && $4 != NULL) 6341592Srgrimes store($4, "w", 1); 6351592Srgrimes if ($4 != NULL) 6361592Srgrimes free($4); 6371592Srgrimes } 6381592Srgrimes | SYST CRLF 6391592Srgrimes { 6401592Srgrimes#ifdef unix 6411592Srgrimes#ifdef BSD 6421592Srgrimes reply(215, "UNIX Type: L%d Version: BSD-%d", 6431592Srgrimes NBBY, BSD); 6441592Srgrimes#else /* BSD */ 6451592Srgrimes reply(215, "UNIX Type: L%d", NBBY); 6461592Srgrimes#endif /* BSD */ 6471592Srgrimes#else /* unix */ 6481592Srgrimes reply(215, "UNKNOWN Type: L%d", NBBY); 6491592Srgrimes#endif /* unix */ 6501592Srgrimes } 6511592Srgrimes 6521592Srgrimes /* 6531592Srgrimes * SIZE is not in RFC959, but Postel has blessed it and 6541592Srgrimes * it will be in the updated RFC. 6551592Srgrimes * 6561592Srgrimes * Return size of file in a format suitable for 6571592Srgrimes * using with RESTART (we just count bytes). 6581592Srgrimes */ 6591592Srgrimes | SIZE check_login SP pathname CRLF 6601592Srgrimes { 6611592Srgrimes if ($2 && $4 != NULL) 6621592Srgrimes sizecmd($4); 6631592Srgrimes if ($4 != NULL) 6641592Srgrimes free($4); 6651592Srgrimes } 6661592Srgrimes 6671592Srgrimes /* 6681592Srgrimes * MDTM is not in RFC959, but Postel has blessed it and 6691592Srgrimes * it will be in the updated RFC. 6701592Srgrimes * 6711592Srgrimes * Return modification time of file as an ISO 3307 6721592Srgrimes * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 6731592Srgrimes * where xxx is the fractional second (of any precision, 6741592Srgrimes * not necessarily 3 digits) 6751592Srgrimes */ 6761592Srgrimes | MDTM check_login SP pathname CRLF 6771592Srgrimes { 6781592Srgrimes if ($2 && $4 != NULL) { 6791592Srgrimes struct stat stbuf; 6801592Srgrimes if (stat($4, &stbuf) < 0) 6811592Srgrimes reply(550, "%s: %s", 6821592Srgrimes $4, strerror(errno)); 6831592Srgrimes else if (!S_ISREG(stbuf.st_mode)) { 6841592Srgrimes reply(550, "%s: not a plain file.", $4); 6851592Srgrimes } else { 6861592Srgrimes struct tm *t; 6871592Srgrimes t = gmtime(&stbuf.st_mtime); 6881592Srgrimes reply(213, 68917435Spst "%04d%02d%02d%02d%02d%02d", 69017435Spst 1900 + t->tm_year, 69117435Spst t->tm_mon+1, t->tm_mday, 6921592Srgrimes t->tm_hour, t->tm_min, t->tm_sec); 6931592Srgrimes } 6941592Srgrimes } 6951592Srgrimes if ($4 != NULL) 6961592Srgrimes free($4); 6971592Srgrimes } 6981592Srgrimes | QUIT CRLF 6991592Srgrimes { 7001592Srgrimes reply(221, "Goodbye."); 7011592Srgrimes dologout(0); 7021592Srgrimes } 7031592Srgrimes | error CRLF 7041592Srgrimes { 7051592Srgrimes yyerrok; 7061592Srgrimes } 7071592Srgrimes ; 7081592Srgrimesrcmd 7091592Srgrimes : RNFR check_login SP pathname CRLF 7101592Srgrimes { 7111592Srgrimes char *renamefrom(); 7121592Srgrimes 7131592Srgrimes restart_point = (off_t) 0; 7141592Srgrimes if ($2 && $4) { 7151592Srgrimes fromname = renamefrom($4); 7161592Srgrimes if (fromname == (char *) 0 && $4) { 7171592Srgrimes free($4); 7181592Srgrimes } 7191592Srgrimes } 7201592Srgrimes } 7211592Srgrimes | REST SP byte_size CRLF 7221592Srgrimes { 7231592Srgrimes fromname = (char *) 0; 7241592Srgrimes restart_point = $3; /* XXX $3 is only "int" */ 7251592Srgrimes reply(350, "Restarting at %qd. %s", restart_point, 7261592Srgrimes "Send STORE or RETRIEVE to initiate transfer."); 7271592Srgrimes } 7281592Srgrimes ; 7291592Srgrimes 7301592Srgrimesusername 7311592Srgrimes : STRING 7321592Srgrimes ; 7331592Srgrimes 7341592Srgrimespassword 7351592Srgrimes : /* empty */ 7361592Srgrimes { 7371592Srgrimes $$ = (char *)calloc(1, sizeof(char)); 7381592Srgrimes } 7391592Srgrimes | STRING 7401592Srgrimes ; 7411592Srgrimes 7421592Srgrimesbyte_size 7431592Srgrimes : NUMBER 7441592Srgrimes ; 7451592Srgrimes 7461592Srgrimeshost_port 7471592Srgrimes : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 7481592Srgrimes NUMBER COMMA NUMBER 7491592Srgrimes { 7501592Srgrimes char *a, *p; 7511592Srgrimes 75256668Sshin data_dest.su_len = sizeof(struct sockaddr_in); 75356668Sshin data_dest.su_family = AF_INET; 75456668Sshin p = (char *)&data_dest.su_sin.sin_port; 75517435Spst p[0] = $9; p[1] = $11; 75656668Sshin a = (char *)&data_dest.su_sin.sin_addr; 7571592Srgrimes a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 7581592Srgrimes } 7591592Srgrimes ; 7601592Srgrimes 76156668Sshinhost_long_port 76256668Sshin : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 76356668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 76456668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 76556668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 76656668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 76756668Sshin NUMBER 76856668Sshin { 76956668Sshin char *a, *p; 77056668Sshin 77156668Sshin memset(&data_dest, 0, sizeof(data_dest)); 77256668Sshin data_dest.su_len = sizeof(struct sockaddr_in6); 77356668Sshin data_dest.su_family = AF_INET6; 77456668Sshin p = (char *)&data_dest.su_port; 77556668Sshin p[0] = $39; p[1] = $41; 77656668Sshin a = (char *)&data_dest.su_sin6.sin6_addr; 77756668Sshin a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 77856668Sshin a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19; 77956668Sshin a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27; 78056668Sshin a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35; 78156668Sshin if (his_addr.su_family == AF_INET6) { 78256668Sshin /* XXX more sanity checks! */ 78356668Sshin data_dest.su_sin6.sin6_scope_id = 78456668Sshin his_addr.su_sin6.sin6_scope_id; 78556668Sshin } 78656668Sshin if ($1 != 6 || $3 != 16 || $37 != 2) 78756668Sshin memset(&data_dest, 0, sizeof(data_dest)); 78856668Sshin } 78956668Sshin | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 79056668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 79156668Sshin NUMBER 79256668Sshin { 79356668Sshin char *a, *p; 79456668Sshin 79556668Sshin memset(&data_dest, 0, sizeof(data_dest)); 79656668Sshin data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 79756668Sshin data_dest.su_family = AF_INET; 79856668Sshin p = (char *)&data_dest.su_port; 79956668Sshin p[0] = $15; p[1] = $17; 80056668Sshin a = (char *)&data_dest.su_sin.sin_addr; 80156668Sshin a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 80256668Sshin if ($1 != 4 || $3 != 4 || $13 != 2) 80356668Sshin memset(&data_dest, 0, sizeof(data_dest)); 80456668Sshin } 80556668Sshin ; 80656668Sshin 8071592Srgrimesform_code 8081592Srgrimes : N 8091592Srgrimes { 8101592Srgrimes $$ = FORM_N; 8111592Srgrimes } 8121592Srgrimes | T 8131592Srgrimes { 8141592Srgrimes $$ = FORM_T; 8151592Srgrimes } 8161592Srgrimes | C 8171592Srgrimes { 8181592Srgrimes $$ = FORM_C; 8191592Srgrimes } 8201592Srgrimes ; 8211592Srgrimes 8221592Srgrimestype_code 8231592Srgrimes : A 8241592Srgrimes { 8251592Srgrimes cmd_type = TYPE_A; 8261592Srgrimes cmd_form = FORM_N; 8271592Srgrimes } 8281592Srgrimes | A SP form_code 8291592Srgrimes { 8301592Srgrimes cmd_type = TYPE_A; 8311592Srgrimes cmd_form = $3; 8321592Srgrimes } 8331592Srgrimes | E 8341592Srgrimes { 8351592Srgrimes cmd_type = TYPE_E; 8361592Srgrimes cmd_form = FORM_N; 8371592Srgrimes } 8381592Srgrimes | E SP form_code 8391592Srgrimes { 8401592Srgrimes cmd_type = TYPE_E; 8411592Srgrimes cmd_form = $3; 8421592Srgrimes } 8431592Srgrimes | I 8441592Srgrimes { 8451592Srgrimes cmd_type = TYPE_I; 8461592Srgrimes } 8471592Srgrimes | L 8481592Srgrimes { 8491592Srgrimes cmd_type = TYPE_L; 8501592Srgrimes cmd_bytesz = NBBY; 8511592Srgrimes } 8521592Srgrimes | L SP byte_size 8531592Srgrimes { 8541592Srgrimes cmd_type = TYPE_L; 8551592Srgrimes cmd_bytesz = $3; 8561592Srgrimes } 8571592Srgrimes /* this is for a bug in the BBN ftp */ 8581592Srgrimes | L byte_size 8591592Srgrimes { 8601592Srgrimes cmd_type = TYPE_L; 8611592Srgrimes cmd_bytesz = $2; 8621592Srgrimes } 8631592Srgrimes ; 8641592Srgrimes 8651592Srgrimesstruct_code 8661592Srgrimes : F 8671592Srgrimes { 8681592Srgrimes $$ = STRU_F; 8691592Srgrimes } 8701592Srgrimes | R 8711592Srgrimes { 8721592Srgrimes $$ = STRU_R; 8731592Srgrimes } 8741592Srgrimes | P 8751592Srgrimes { 8761592Srgrimes $$ = STRU_P; 8771592Srgrimes } 8781592Srgrimes ; 8791592Srgrimes 8801592Srgrimesmode_code 8811592Srgrimes : S 8821592Srgrimes { 8831592Srgrimes $$ = MODE_S; 8841592Srgrimes } 8851592Srgrimes | B 8861592Srgrimes { 8871592Srgrimes $$ = MODE_B; 8881592Srgrimes } 8891592Srgrimes | C 8901592Srgrimes { 8911592Srgrimes $$ = MODE_C; 8921592Srgrimes } 8931592Srgrimes ; 8941592Srgrimes 8951592Srgrimespathname 8961592Srgrimes : pathstring 8971592Srgrimes { 8981592Srgrimes /* 8991592Srgrimes * Problem: this production is used for all pathname 9001592Srgrimes * processing, but only gives a 550 error reply. 9011592Srgrimes * This is a valid reply in some cases but not in others. 9021592Srgrimes */ 9031592Srgrimes if (logged_in && $1 && *$1 == '~') { 9041592Srgrimes glob_t gl; 9051592Srgrimes int flags = 9061592Srgrimes GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 9071592Srgrimes 9081592Srgrimes memset(&gl, 0, sizeof(gl)); 9091592Srgrimes if (glob($1, flags, NULL, &gl) || 9101592Srgrimes gl.gl_pathc == 0) { 9111592Srgrimes reply(550, "not found"); 9121592Srgrimes $$ = NULL; 9131592Srgrimes } else { 9141592Srgrimes $$ = strdup(gl.gl_pathv[0]); 9151592Srgrimes } 9161592Srgrimes globfree(&gl); 9171592Srgrimes free($1); 9181592Srgrimes } else 9191592Srgrimes $$ = $1; 9201592Srgrimes } 9211592Srgrimes ; 9221592Srgrimes 9231592Srgrimespathstring 9241592Srgrimes : STRING 9251592Srgrimes ; 9261592Srgrimes 9271592Srgrimesoctal_number 9281592Srgrimes : NUMBER 9291592Srgrimes { 9301592Srgrimes int ret, dec, multby, digit; 9311592Srgrimes 9321592Srgrimes /* 9331592Srgrimes * Convert a number that was read as decimal number 9341592Srgrimes * to what it would be if it had been read as octal. 9351592Srgrimes */ 9361592Srgrimes dec = $1; 9371592Srgrimes multby = 1; 9381592Srgrimes ret = 0; 9391592Srgrimes while (dec) { 9401592Srgrimes digit = dec%10; 9411592Srgrimes if (digit > 7) { 9421592Srgrimes ret = -1; 9431592Srgrimes break; 9441592Srgrimes } 9451592Srgrimes ret += digit * multby; 9461592Srgrimes multby *= 8; 9471592Srgrimes dec /= 10; 9481592Srgrimes } 9491592Srgrimes $$ = ret; 9501592Srgrimes } 9511592Srgrimes ; 9521592Srgrimes 9531592Srgrimes 9541592Srgrimescheck_login 9551592Srgrimes : /* empty */ 9561592Srgrimes { 9571592Srgrimes if (logged_in) 9581592Srgrimes $$ = 1; 9591592Srgrimes else { 9601592Srgrimes reply(530, "Please login with USER and PASS."); 9611592Srgrimes $$ = 0; 9621592Srgrimes } 9631592Srgrimes } 9641592Srgrimes ; 9651592Srgrimes 9661592Srgrimes%% 9671592Srgrimes 9681592Srgrimesextern jmp_buf errcatch; 9691592Srgrimes 9701592Srgrimes#define CMD 0 /* beginning of command */ 9711592Srgrimes#define ARGS 1 /* expect miscellaneous arguments */ 9721592Srgrimes#define STR1 2 /* expect SP followed by STRING */ 9731592Srgrimes#define STR2 3 /* expect STRING */ 9741592Srgrimes#define OSTR 4 /* optional SP then STRING */ 9751592Srgrimes#define ZSTR1 5 /* SP then optional STRING */ 9761592Srgrimes#define ZSTR2 6 /* optional STRING after SP */ 9771592Srgrimes#define SITECMD 7 /* SITE command */ 9781592Srgrimes#define NSTR 8 /* Number followed by a string */ 9791592Srgrimes 9801592Srgrimesstruct tab { 9811592Srgrimes char *name; 9821592Srgrimes short token; 9831592Srgrimes short state; 9841592Srgrimes short implemented; /* 1 if command is implemented */ 9851592Srgrimes char *help; 9861592Srgrimes}; 9871592Srgrimes 9881592Srgrimesstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 9891592Srgrimes { "USER", USER, STR1, 1, "<sp> username" }, 9901592Srgrimes { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 9911592Srgrimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 9921592Srgrimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 9931592Srgrimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 9941592Srgrimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 9951592Srgrimes { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 99656668Sshin { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 99756668Sshin { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 9981592Srgrimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 99956668Sshin { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 100056668Sshin { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 10011592Srgrimes { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 10021592Srgrimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 10031592Srgrimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 10041592Srgrimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 10051592Srgrimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 10061592Srgrimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 10071592Srgrimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 10081592Srgrimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 10091592Srgrimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 10101592Srgrimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 10111592Srgrimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 10121592Srgrimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 10131592Srgrimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 10141592Srgrimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 10151592Srgrimes { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 10161592Srgrimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 10171592Srgrimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 10181592Srgrimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 10191592Srgrimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 10201592Srgrimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 10211592Srgrimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 10221592Srgrimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 10231592Srgrimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 10241592Srgrimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 10251592Srgrimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 10261592Srgrimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 10271592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 10281592Srgrimes { "NOOP", NOOP, ARGS, 1, "" }, 10291592Srgrimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 10301592Srgrimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 10311592Srgrimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 10321592Srgrimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 10331592Srgrimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 10341592Srgrimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 10351592Srgrimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 10361592Srgrimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 10371592Srgrimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 10381592Srgrimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 10391592Srgrimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 10401592Srgrimes { NULL, 0, 0, 0, 0 } 10411592Srgrimes}; 10421592Srgrimes 10431592Srgrimesstruct tab sitetab[] = { 10441592Srgrimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 10451592Srgrimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 10461592Srgrimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 10471592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 10481592Srgrimes { NULL, 0, 0, 0, 0 } 10491592Srgrimes}; 10501592Srgrimes 10511592Srgrimesstatic char *copy __P((char *)); 10521592Srgrimesstatic void help __P((struct tab *, char *)); 10531592Srgrimesstatic struct tab * 10541592Srgrimes lookup __P((struct tab *, char *)); 105556668Sshinstatic int port_check __P((const char *)); 105656668Sshinstatic int port_check_v6 __P((const char *)); 10571592Srgrimesstatic void sizecmd __P((char *)); 10581592Srgrimesstatic void toolong __P((int)); 105956668Sshinstatic void v4map_data_dest __P((void)); 10601592Srgrimesstatic int yylex __P((void)); 10611592Srgrimes 10621592Srgrimesstatic struct tab * 10631592Srgrimeslookup(p, cmd) 10641592Srgrimes struct tab *p; 10651592Srgrimes char *cmd; 10661592Srgrimes{ 10671592Srgrimes 10681592Srgrimes for (; p->name != NULL; p++) 10691592Srgrimes if (strcmp(cmd, p->name) == 0) 10701592Srgrimes return (p); 10711592Srgrimes return (0); 10721592Srgrimes} 10731592Srgrimes 10741592Srgrimes#include <arpa/telnet.h> 10751592Srgrimes 10761592Srgrimes/* 10771592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes. 10781592Srgrimes */ 10791592Srgrimeschar * 10801592Srgrimesgetline(s, n, iop) 10811592Srgrimes char *s; 10821592Srgrimes int n; 10831592Srgrimes FILE *iop; 10841592Srgrimes{ 10851592Srgrimes int c; 10861592Srgrimes register char *cs; 10871592Srgrimes 10881592Srgrimes cs = s; 10891592Srgrimes/* tmpline may contain saved command from urgent mode interruption */ 10901592Srgrimes for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 10911592Srgrimes *cs++ = tmpline[c]; 10921592Srgrimes if (tmpline[c] == '\n') { 10931592Srgrimes *cs++ = '\0'; 10941592Srgrimes if (debug) 10951592Srgrimes syslog(LOG_DEBUG, "command: %s", s); 10961592Srgrimes tmpline[0] = '\0'; 10971592Srgrimes return(s); 10981592Srgrimes } 10991592Srgrimes if (c == 0) 11001592Srgrimes tmpline[0] = '\0'; 11011592Srgrimes } 11021592Srgrimes while ((c = getc(iop)) != EOF) { 11031592Srgrimes c &= 0377; 11041592Srgrimes if (c == IAC) { 11051592Srgrimes if ((c = getc(iop)) != EOF) { 11061592Srgrimes c &= 0377; 11071592Srgrimes switch (c) { 11081592Srgrimes case WILL: 11091592Srgrimes case WONT: 11101592Srgrimes c = getc(iop); 11111592Srgrimes printf("%c%c%c", IAC, DONT, 0377&c); 11121592Srgrimes (void) fflush(stdout); 11131592Srgrimes continue; 11141592Srgrimes case DO: 11151592Srgrimes case DONT: 11161592Srgrimes c = getc(iop); 11171592Srgrimes printf("%c%c%c", IAC, WONT, 0377&c); 11181592Srgrimes (void) fflush(stdout); 11191592Srgrimes continue; 11201592Srgrimes case IAC: 11211592Srgrimes break; 11221592Srgrimes default: 11231592Srgrimes continue; /* ignore command */ 11241592Srgrimes } 11251592Srgrimes } 11261592Srgrimes } 11271592Srgrimes *cs++ = c; 11281592Srgrimes if (--n <= 0 || c == '\n') 11291592Srgrimes break; 11301592Srgrimes } 11311592Srgrimes if (c == EOF && cs == s) 11321592Srgrimes return (NULL); 11331592Srgrimes *cs++ = '\0'; 11341592Srgrimes if (debug) { 11351592Srgrimes if (!guest && strncasecmp("pass ", s, 5) == 0) { 11361592Srgrimes /* Don't syslog passwords */ 11371592Srgrimes syslog(LOG_DEBUG, "command: %.5s ???", s); 11381592Srgrimes } else { 11391592Srgrimes register char *cp; 11401592Srgrimes register int len; 11411592Srgrimes 11421592Srgrimes /* Don't syslog trailing CR-LF */ 11431592Srgrimes len = strlen(s); 11441592Srgrimes cp = s + len - 1; 11451592Srgrimes while (cp >= s && (*cp == '\n' || *cp == '\r')) { 11461592Srgrimes --cp; 11471592Srgrimes --len; 11481592Srgrimes } 11491592Srgrimes syslog(LOG_DEBUG, "command: %.*s", len, s); 11501592Srgrimes } 11511592Srgrimes } 11521592Srgrimes return (s); 11531592Srgrimes} 11541592Srgrimes 11551592Srgrimesstatic void 11561592Srgrimestoolong(signo) 11571592Srgrimes int signo; 11581592Srgrimes{ 11591592Srgrimes 11601592Srgrimes reply(421, 11611592Srgrimes "Timeout (%d seconds): closing control connection.", timeout); 11621592Srgrimes if (logging) 11631592Srgrimes syslog(LOG_INFO, "User %s timed out after %d seconds", 11641592Srgrimes (pw ? pw -> pw_name : "unknown"), timeout); 11651592Srgrimes dologout(1); 11661592Srgrimes} 11671592Srgrimes 11681592Srgrimesstatic int 11691592Srgrimesyylex() 11701592Srgrimes{ 11711592Srgrimes static int cpos, state; 11721592Srgrimes char *cp, *cp2; 11731592Srgrimes struct tab *p; 11741592Srgrimes int n; 11751592Srgrimes char c; 11761592Srgrimes 11771592Srgrimes for (;;) { 11781592Srgrimes switch (state) { 11791592Srgrimes 11801592Srgrimes case CMD: 11811592Srgrimes (void) signal(SIGALRM, toolong); 11821592Srgrimes (void) alarm((unsigned) timeout); 11831592Srgrimes if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 11841592Srgrimes reply(221, "You could at least say goodbye."); 11851592Srgrimes dologout(0); 11861592Srgrimes } 11871592Srgrimes (void) alarm(0); 11881592Srgrimes#ifdef SETPROCTITLE 118929574Sphk if (strncasecmp(cbuf, "PASS", 4) != 0) 11901592Srgrimes setproctitle("%s: %s", proctitle, cbuf); 11911592Srgrimes#endif /* SETPROCTITLE */ 11921592Srgrimes if ((cp = strchr(cbuf, '\r'))) { 11931592Srgrimes *cp++ = '\n'; 11941592Srgrimes *cp = '\0'; 11951592Srgrimes } 11961592Srgrimes if ((cp = strpbrk(cbuf, " \n"))) 11971592Srgrimes cpos = cp - cbuf; 11981592Srgrimes if (cpos == 0) 11991592Srgrimes cpos = 4; 12001592Srgrimes c = cbuf[cpos]; 12011592Srgrimes cbuf[cpos] = '\0'; 12021592Srgrimes upper(cbuf); 12031592Srgrimes p = lookup(cmdtab, cbuf); 12041592Srgrimes cbuf[cpos] = c; 12053776Spst if (p != 0) { 12061592Srgrimes if (p->implemented == 0) { 12071592Srgrimes nack(p->name); 12081592Srgrimes longjmp(errcatch,0); 12091592Srgrimes /* NOTREACHED */ 12101592Srgrimes } 12111592Srgrimes state = p->state; 12121592Srgrimes yylval.s = p->name; 12131592Srgrimes return (p->token); 12141592Srgrimes } 12151592Srgrimes break; 12161592Srgrimes 12171592Srgrimes case SITECMD: 12181592Srgrimes if (cbuf[cpos] == ' ') { 12191592Srgrimes cpos++; 12201592Srgrimes return (SP); 12211592Srgrimes } 12221592Srgrimes cp = &cbuf[cpos]; 12231592Srgrimes if ((cp2 = strpbrk(cp, " \n"))) 12241592Srgrimes cpos = cp2 - cbuf; 12251592Srgrimes c = cbuf[cpos]; 12261592Srgrimes cbuf[cpos] = '\0'; 12271592Srgrimes upper(cp); 12281592Srgrimes p = lookup(sitetab, cp); 12291592Srgrimes cbuf[cpos] = c; 12303777Spst if (guest == 0 && p != 0) { 12311592Srgrimes if (p->implemented == 0) { 12321592Srgrimes state = CMD; 12331592Srgrimes nack(p->name); 12341592Srgrimes longjmp(errcatch,0); 12351592Srgrimes /* NOTREACHED */ 12361592Srgrimes } 12371592Srgrimes state = p->state; 12381592Srgrimes yylval.s = p->name; 12391592Srgrimes return (p->token); 12401592Srgrimes } 12411592Srgrimes state = CMD; 12421592Srgrimes break; 12431592Srgrimes 12441592Srgrimes case OSTR: 12451592Srgrimes if (cbuf[cpos] == '\n') { 12461592Srgrimes state = CMD; 12471592Srgrimes return (CRLF); 12481592Srgrimes } 12491592Srgrimes /* FALLTHROUGH */ 12501592Srgrimes 12511592Srgrimes case STR1: 12521592Srgrimes case ZSTR1: 12531592Srgrimes dostr1: 12541592Srgrimes if (cbuf[cpos] == ' ') { 12551592Srgrimes cpos++; 125651979Salfred state = state == OSTR ? STR2 : state+1; 12571592Srgrimes return (SP); 12581592Srgrimes } 12591592Srgrimes break; 12601592Srgrimes 12611592Srgrimes case ZSTR2: 12621592Srgrimes if (cbuf[cpos] == '\n') { 12631592Srgrimes state = CMD; 12641592Srgrimes return (CRLF); 12651592Srgrimes } 12661592Srgrimes /* FALLTHROUGH */ 12671592Srgrimes 12681592Srgrimes case STR2: 12691592Srgrimes cp = &cbuf[cpos]; 12701592Srgrimes n = strlen(cp); 12711592Srgrimes cpos += n - 1; 12721592Srgrimes /* 12731592Srgrimes * Make sure the string is nonempty and \n terminated. 12741592Srgrimes */ 12751592Srgrimes if (n > 1 && cbuf[cpos] == '\n') { 12761592Srgrimes cbuf[cpos] = '\0'; 12771592Srgrimes yylval.s = copy(cp); 12781592Srgrimes cbuf[cpos] = '\n'; 12791592Srgrimes state = ARGS; 12801592Srgrimes return (STRING); 12811592Srgrimes } 12821592Srgrimes break; 12831592Srgrimes 12841592Srgrimes case NSTR: 12851592Srgrimes if (cbuf[cpos] == ' ') { 12861592Srgrimes cpos++; 12871592Srgrimes return (SP); 12881592Srgrimes } 12891592Srgrimes if (isdigit(cbuf[cpos])) { 12901592Srgrimes cp = &cbuf[cpos]; 12911592Srgrimes while (isdigit(cbuf[++cpos])) 12921592Srgrimes ; 12931592Srgrimes c = cbuf[cpos]; 12941592Srgrimes cbuf[cpos] = '\0'; 12951592Srgrimes yylval.i = atoi(cp); 12961592Srgrimes cbuf[cpos] = c; 12971592Srgrimes state = STR1; 12981592Srgrimes return (NUMBER); 12991592Srgrimes } 13001592Srgrimes state = STR1; 13011592Srgrimes goto dostr1; 13021592Srgrimes 13031592Srgrimes case ARGS: 13041592Srgrimes if (isdigit(cbuf[cpos])) { 13051592Srgrimes cp = &cbuf[cpos]; 13061592Srgrimes while (isdigit(cbuf[++cpos])) 13071592Srgrimes ; 13081592Srgrimes c = cbuf[cpos]; 13091592Srgrimes cbuf[cpos] = '\0'; 13101592Srgrimes yylval.i = atoi(cp); 13111592Srgrimes cbuf[cpos] = c; 13121592Srgrimes return (NUMBER); 13131592Srgrimes } 131456668Sshin if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 131556668Sshin && !isalnum(cbuf[cpos + 3])) { 131656668Sshin cpos += 3; 131756668Sshin return ALL; 131856668Sshin } 13191592Srgrimes switch (cbuf[cpos++]) { 13201592Srgrimes 13211592Srgrimes case '\n': 13221592Srgrimes state = CMD; 13231592Srgrimes return (CRLF); 13241592Srgrimes 13251592Srgrimes case ' ': 13261592Srgrimes return (SP); 13271592Srgrimes 13281592Srgrimes case ',': 13291592Srgrimes return (COMMA); 13301592Srgrimes 13311592Srgrimes case 'A': 13321592Srgrimes case 'a': 13331592Srgrimes return (A); 13341592Srgrimes 13351592Srgrimes case 'B': 13361592Srgrimes case 'b': 13371592Srgrimes return (B); 13381592Srgrimes 13391592Srgrimes case 'C': 13401592Srgrimes case 'c': 13411592Srgrimes return (C); 13421592Srgrimes 13431592Srgrimes case 'E': 13441592Srgrimes case 'e': 13451592Srgrimes return (E); 13461592Srgrimes 13471592Srgrimes case 'F': 13481592Srgrimes case 'f': 13491592Srgrimes return (F); 13501592Srgrimes 13511592Srgrimes case 'I': 13521592Srgrimes case 'i': 13531592Srgrimes return (I); 13541592Srgrimes 13551592Srgrimes case 'L': 13561592Srgrimes case 'l': 13571592Srgrimes return (L); 13581592Srgrimes 13591592Srgrimes case 'N': 13601592Srgrimes case 'n': 13611592Srgrimes return (N); 13621592Srgrimes 13631592Srgrimes case 'P': 13641592Srgrimes case 'p': 13651592Srgrimes return (P); 13661592Srgrimes 13671592Srgrimes case 'R': 13681592Srgrimes case 'r': 13691592Srgrimes return (R); 13701592Srgrimes 13711592Srgrimes case 'S': 13721592Srgrimes case 's': 13731592Srgrimes return (S); 13741592Srgrimes 13751592Srgrimes case 'T': 13761592Srgrimes case 't': 13771592Srgrimes return (T); 13781592Srgrimes 13791592Srgrimes } 13801592Srgrimes break; 13811592Srgrimes 13821592Srgrimes default: 13831592Srgrimes fatal("Unknown state in scanner."); 13841592Srgrimes } 13851592Srgrimes yyerror((char *) 0); 13861592Srgrimes state = CMD; 13871592Srgrimes longjmp(errcatch,0); 13881592Srgrimes } 13891592Srgrimes} 13901592Srgrimes 13911592Srgrimesvoid 13921592Srgrimesupper(s) 13931592Srgrimes char *s; 13941592Srgrimes{ 13951592Srgrimes while (*s != '\0') { 13961592Srgrimes if (islower(*s)) 13971592Srgrimes *s = toupper(*s); 13981592Srgrimes s++; 13991592Srgrimes } 14001592Srgrimes} 14011592Srgrimes 14021592Srgrimesstatic char * 14031592Srgrimescopy(s) 14041592Srgrimes char *s; 14051592Srgrimes{ 14061592Srgrimes char *p; 14071592Srgrimes 14081592Srgrimes p = malloc((unsigned) strlen(s) + 1); 14091592Srgrimes if (p == NULL) 14101592Srgrimes fatal("Ran out of memory."); 14111592Srgrimes (void) strcpy(p, s); 14121592Srgrimes return (p); 14131592Srgrimes} 14141592Srgrimes 14151592Srgrimesstatic void 14161592Srgrimeshelp(ctab, s) 14171592Srgrimes struct tab *ctab; 14181592Srgrimes char *s; 14191592Srgrimes{ 14201592Srgrimes struct tab *c; 14211592Srgrimes int width, NCMDS; 14221592Srgrimes char *type; 14231592Srgrimes 14241592Srgrimes if (ctab == sitetab) 14251592Srgrimes type = "SITE "; 14261592Srgrimes else 14271592Srgrimes type = ""; 14281592Srgrimes width = 0, NCMDS = 0; 14291592Srgrimes for (c = ctab; c->name != NULL; c++) { 14301592Srgrimes int len = strlen(c->name); 14311592Srgrimes 14321592Srgrimes if (len > width) 14331592Srgrimes width = len; 14341592Srgrimes NCMDS++; 14351592Srgrimes } 14361592Srgrimes width = (width + 8) &~ 7; 14371592Srgrimes if (s == 0) { 14381592Srgrimes int i, j, w; 14391592Srgrimes int columns, lines; 14401592Srgrimes 14411592Srgrimes lreply(214, "The following %scommands are recognized %s.", 14421592Srgrimes type, "(* =>'s unimplemented)"); 14431592Srgrimes columns = 76 / width; 14441592Srgrimes if (columns == 0) 14451592Srgrimes columns = 1; 14461592Srgrimes lines = (NCMDS + columns - 1) / columns; 14471592Srgrimes for (i = 0; i < lines; i++) { 14481592Srgrimes printf(" "); 14491592Srgrimes for (j = 0; j < columns; j++) { 14501592Srgrimes c = ctab + j * lines + i; 14511592Srgrimes printf("%s%c", c->name, 14521592Srgrimes c->implemented ? ' ' : '*'); 14531592Srgrimes if (c + lines >= &ctab[NCMDS]) 14541592Srgrimes break; 14551592Srgrimes w = strlen(c->name) + 1; 14561592Srgrimes while (w < width) { 14571592Srgrimes putchar(' '); 14581592Srgrimes w++; 14591592Srgrimes } 14601592Srgrimes } 14611592Srgrimes printf("\r\n"); 14621592Srgrimes } 14631592Srgrimes (void) fflush(stdout); 14641592Srgrimes reply(214, "Direct comments to ftp-bugs@%s.", hostname); 14651592Srgrimes return; 14661592Srgrimes } 14671592Srgrimes upper(s); 14681592Srgrimes c = lookup(ctab, s); 14691592Srgrimes if (c == (struct tab *)0) { 14701592Srgrimes reply(502, "Unknown command %s.", s); 14711592Srgrimes return; 14721592Srgrimes } 14731592Srgrimes if (c->implemented) 14741592Srgrimes reply(214, "Syntax: %s%s %s", type, c->name, c->help); 14751592Srgrimes else 14761592Srgrimes reply(214, "%s%-*s\t%s; unimplemented.", type, width, 14771592Srgrimes c->name, c->help); 14781592Srgrimes} 14791592Srgrimes 14801592Srgrimesstatic void 14811592Srgrimessizecmd(filename) 14821592Srgrimes char *filename; 14831592Srgrimes{ 14841592Srgrimes switch (type) { 14851592Srgrimes case TYPE_L: 14861592Srgrimes case TYPE_I: { 14871592Srgrimes struct stat stbuf; 148863350Sdes if (stat(filename, &stbuf) < 0) 148963350Sdes perror_reply(550, filename); 149063350Sdes else if (!S_ISREG(stbuf.st_mode)) 14911592Srgrimes reply(550, "%s: not a plain file.", filename); 14921592Srgrimes else 14931592Srgrimes reply(213, "%qu", stbuf.st_size); 14941592Srgrimes break; } 14951592Srgrimes case TYPE_A: { 14961592Srgrimes FILE *fin; 14971592Srgrimes int c; 14981592Srgrimes off_t count; 14991592Srgrimes struct stat stbuf; 15001592Srgrimes fin = fopen(filename, "r"); 15011592Srgrimes if (fin == NULL) { 15021592Srgrimes perror_reply(550, filename); 15031592Srgrimes return; 15041592Srgrimes } 150563350Sdes if (fstat(fileno(fin), &stbuf) < 0) { 150663350Sdes perror_reply(550, filename); 150763350Sdes (void) fclose(fin); 150863350Sdes return; 150963350Sdes } else if (!S_ISREG(stbuf.st_mode)) { 15101592Srgrimes reply(550, "%s: not a plain file.", filename); 15111592Srgrimes (void) fclose(fin); 15121592Srgrimes return; 15131592Srgrimes } 15141592Srgrimes 15151592Srgrimes count = 0; 15161592Srgrimes while((c=getc(fin)) != EOF) { 15171592Srgrimes if (c == '\n') /* will get expanded to \r\n */ 15181592Srgrimes count++; 15191592Srgrimes count++; 15201592Srgrimes } 15211592Srgrimes (void) fclose(fin); 15221592Srgrimes 15231592Srgrimes reply(213, "%qd", count); 15241592Srgrimes break; } 15251592Srgrimes default: 15261592Srgrimes reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 15271592Srgrimes } 15281592Srgrimes} 152956668Sshin 153056668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 153156668Sshinstatic int 153256668Sshinport_check(pcmd) 153356668Sshin const char *pcmd; 153456668Sshin{ 153556668Sshin if (his_addr.su_family == AF_INET) { 153656668Sshin if (data_dest.su_family != AF_INET) { 153756668Sshin usedefault = 1; 153856668Sshin reply(500, "Invalid address rejected."); 153956668Sshin return 1; 154056668Sshin } 154156668Sshin if (paranoid && 154256668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 154356668Sshin memcmp(&data_dest.su_sin.sin_addr, 154456668Sshin &his_addr.su_sin.sin_addr, 154556668Sshin sizeof(data_dest.su_sin.sin_addr)))) { 154656668Sshin usedefault = 1; 154756668Sshin reply(500, "Illegal PORT range rejected."); 154856668Sshin } else { 154956668Sshin usedefault = 0; 155056668Sshin if (pdata >= 0) { 155156668Sshin (void) close(pdata); 155256668Sshin pdata = -1; 155356668Sshin } 155456668Sshin reply(200, "%s command successful.", pcmd); 155556668Sshin } 155656668Sshin return 1; 155756668Sshin } 155856668Sshin return 0; 155956668Sshin} 156056668Sshin 156156668Sshin#ifdef INET6 156256668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 156356668Sshinstatic int 156456668Sshinport_check_v6(pcmd) 156556668Sshin const char *pcmd; 156656668Sshin{ 156756668Sshin if (his_addr.su_family == AF_INET6) { 156856668Sshin if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 156956668Sshin /* Convert data_dest into v4 mapped sockaddr.*/ 157056668Sshin v4map_data_dest(); 157156668Sshin if (data_dest.su_family != AF_INET6) { 157256668Sshin usedefault = 1; 157356668Sshin reply(500, "Invalid address rejected."); 157456668Sshin return 1; 157556668Sshin } 157656668Sshin if (paranoid && 157756668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 157856668Sshin memcmp(&data_dest.su_sin6.sin6_addr, 157956668Sshin &his_addr.su_sin6.sin6_addr, 158056668Sshin sizeof(data_dest.su_sin6.sin6_addr)))) { 158156668Sshin usedefault = 1; 158256668Sshin reply(500, "Illegal PORT range rejected."); 158356668Sshin } else { 158456668Sshin usedefault = 0; 158556668Sshin if (pdata >= 0) { 158656668Sshin (void) close(pdata); 158756668Sshin pdata = -1; 158856668Sshin } 158956668Sshin reply(200, "%s command successful.", pcmd); 159056668Sshin } 159156668Sshin return 1; 159256668Sshin } 159356668Sshin return 0; 159456668Sshin} 159556668Sshin 159656668Sshinstatic void 159756668Sshinv4map_data_dest() 159856668Sshin{ 159956668Sshin struct in_addr savedaddr; 160056668Sshin int savedport; 160156668Sshin 160256668Sshin if (data_dest.su_family != AF_INET) { 160356668Sshin usedefault = 1; 160456668Sshin reply(500, "Invalid address rejected."); 160556668Sshin return; 160656668Sshin } 160756668Sshin 160856668Sshin savedaddr = data_dest.su_sin.sin_addr; 160956668Sshin savedport = data_dest.su_port; 161056668Sshin 161156668Sshin memset(&data_dest, 0, sizeof(data_dest)); 161256668Sshin data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 161356668Sshin data_dest.su_sin6.sin6_family = AF_INET6; 161456668Sshin data_dest.su_sin6.sin6_port = savedport; 161556668Sshin memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 161656668Sshin memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 161756668Sshin (caddr_t)&savedaddr, sizeof(savedaddr)); 161856668Sshin} 161956668Sshin#endif 1620