ftpcmd.y revision 100684
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 100684 2002-07-25 17:41:47Z yar $"; 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> 6192090Smaxim#include <libutil.h> 6292272Smaxim#include <limits.h> 6392090Smaxim#include <md5.h> 6456668Sshin#include <netdb.h> 651592Srgrimes#include <pwd.h> 661592Srgrimes#include <signal.h> 671592Srgrimes#include <stdio.h> 681592Srgrimes#include <stdlib.h> 691592Srgrimes#include <string.h> 701592Srgrimes#include <syslog.h> 711592Srgrimes#include <time.h> 721592Srgrimes#include <unistd.h> 731592Srgrimes 741592Srgrimes#include "extern.h" 751592Srgrimes 7656668Sshinextern union sockunion data_dest, his_addr; 771592Srgrimesextern int logged_in; 781592Srgrimesextern struct passwd *pw; 791592Srgrimesextern int guest; 8017435Spstextern int paranoid; 811592Srgrimesextern int logging; 821592Srgrimesextern int type; 831592Srgrimesextern int form; 8476096Smarkmextern int ftpdebug; 851592Srgrimesextern int timeout; 861592Srgrimesextern int maxtimeout; 871592Srgrimesextern int pdata; 8827650Sdavidnextern char *hostname; 8927650Sdavidnextern char remotehost[]; 901592Srgrimesextern char proctitle[]; 911592Srgrimesextern int usedefault; 921592Srgrimesextern int transflag; 931592Srgrimesextern char tmpline[]; 9470102Sphkextern int readonly; 9570102Sphkextern int noepsv; 9682460Snikextern int noretr; 9782796Ssheldonhextern int noguestretr; 98100684Syarextern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */ 991592Srgrimes 1001592Srgrimesoff_t restart_point; 1011592Srgrimes 1021592Srgrimesstatic int cmd_type; 1031592Srgrimesstatic int cmd_form; 1041592Srgrimesstatic int cmd_bytesz; 10589935Syarstatic int state; 1061592Srgrimeschar cbuf[512]; 10788935Sdwmalonechar *fromname = (char *) 0; 1081592Srgrimes 10956668Sshinextern int epsvall; 11056668Sshin 1111592Srgrimes%} 1121592Srgrimes 1131592Srgrimes%union { 11492272Smaxim struct { 11592272Smaxim off_t o; 11692272Smaxim int i; 11792272Smaxim } u; 1181592Srgrimes char *s; 1191592Srgrimes} 1201592Srgrimes 1211592Srgrimes%token 1221592Srgrimes A B C E F I 1231592Srgrimes L N P R S T 12456668Sshin ALL 1251592Srgrimes 1261592Srgrimes SP CRLF COMMA 1271592Srgrimes 1281592Srgrimes USER PASS ACCT REIN QUIT PORT 1291592Srgrimes PASV TYPE STRU MODE RETR STOR 1301592Srgrimes APPE MLFL MAIL MSND MSOM MSAM 1311592Srgrimes MRSQ MRCP ALLO REST RNFR RNTO 1321592Srgrimes ABOR DELE CWD LIST NLST SITE 1331592Srgrimes STAT HELP NOOP MKD RMD PWD 1341592Srgrimes CDUP STOU SMNT SYST SIZE MDTM 13556668Sshin LPRT LPSV EPRT EPSV 1361592Srgrimes 13775535Sphk UMASK IDLE CHMOD MDFIVE 1381592Srgrimes 1391592Srgrimes LEXERR 1401592Srgrimes 1411592Srgrimes%token <s> STRING 14292272Smaxim%token <u> NUMBER 1431592Srgrimes 14492272Smaxim%type <u.i> check_login octal_number byte_size 14592272Smaxim%type <u.i> check_login_ro check_login_epsv 14692272Smaxim%type <u.i> struct_code mode_code type_code form_code 14775567Speter%type <s> pathstring pathname password username 14856668Sshin%type <s> ALL 1491592Srgrimes 1501592Srgrimes%start cmd_list 1511592Srgrimes 1521592Srgrimes%% 1531592Srgrimes 1541592Srgrimescmd_list 1551592Srgrimes : /* empty */ 1561592Srgrimes | cmd_list cmd 1571592Srgrimes { 15888935Sdwmalone if (fromname) 15988935Sdwmalone free(fromname); 1601592Srgrimes fromname = (char *) 0; 1611592Srgrimes restart_point = (off_t) 0; 1621592Srgrimes } 1631592Srgrimes | cmd_list rcmd 1641592Srgrimes ; 1651592Srgrimes 1661592Srgrimescmd 1671592Srgrimes : USER SP username CRLF 1681592Srgrimes { 1691592Srgrimes user($3); 1701592Srgrimes free($3); 1711592Srgrimes } 1721592Srgrimes | PASS SP password CRLF 1731592Srgrimes { 1741592Srgrimes pass($3); 1751592Srgrimes free($3); 1761592Srgrimes } 17775556Sgreen | PASS CRLF 17875556Sgreen { 17975556Sgreen pass(""); 18075556Sgreen } 18117433Spst | PORT check_login SP host_port CRLF 1821592Srgrimes { 18356668Sshin if (epsvall) { 18456668Sshin reply(501, "no PORT allowed after EPSV ALL"); 18556668Sshin goto port_done; 18656668Sshin } 18756668Sshin if (!$2) 18856668Sshin goto port_done; 18956668Sshin if (port_check("PORT") == 1) 19056668Sshin goto port_done; 19156668Sshin#ifdef INET6 19256668Sshin if ((his_addr.su_family != AF_INET6 || 19356668Sshin !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { 19456668Sshin /* shoud never happen */ 19556668Sshin usedefault = 1; 19656668Sshin reply(500, "Invalid address rejected."); 19756668Sshin goto port_done; 19856668Sshin } 19956668Sshin port_check_v6("pcmd"); 20056668Sshin#endif 20156668Sshin port_done: 20256668Sshin } 20356668Sshin | LPRT check_login SP host_long_port CRLF 20456668Sshin { 20556668Sshin if (epsvall) { 20656668Sshin reply(501, "no LPRT allowed after EPSV ALL"); 20756668Sshin goto lprt_done; 20856668Sshin } 20956668Sshin if (!$2) 21056668Sshin goto lprt_done; 21156668Sshin if (port_check("LPRT") == 1) 21256668Sshin goto lprt_done; 21356668Sshin#ifdef INET6 21456668Sshin if (his_addr.su_family != AF_INET6) { 21556668Sshin usedefault = 1; 21656668Sshin reply(500, "Invalid address rejected."); 21756668Sshin goto lprt_done; 21856668Sshin } 21956668Sshin if (port_check_v6("LPRT") == 1) 22056668Sshin goto lprt_done; 22156668Sshin#endif 22256668Sshin lprt_done: 22356668Sshin } 22456668Sshin | EPRT check_login SP STRING CRLF 22556668Sshin { 22656668Sshin char delim; 22756668Sshin char *tmp = NULL; 22856668Sshin char *p, *q; 22956668Sshin char *result[3]; 23056668Sshin struct addrinfo hints; 23156668Sshin struct addrinfo *res; 23256668Sshin int i; 23356668Sshin 23456668Sshin if (epsvall) { 23556668Sshin reply(501, "no EPRT allowed after EPSV ALL"); 23656668Sshin goto eprt_done; 23756668Sshin } 23856668Sshin if (!$2) 23956668Sshin goto eprt_done; 24056668Sshin 24156668Sshin memset(&data_dest, 0, sizeof(data_dest)); 24256668Sshin tmp = strdup($4); 24376096Smarkm if (ftpdebug) 24456668Sshin syslog(LOG_DEBUG, "%s", tmp); 24556668Sshin if (!tmp) { 24676096Smarkm fatalerror("not enough core"); 24756668Sshin /*NOTREACHED*/ 24856668Sshin } 24956668Sshin p = tmp; 25056668Sshin delim = p[0]; 25156668Sshin p++; 25256668Sshin memset(result, 0, sizeof(result)); 25356668Sshin for (i = 0; i < 3; i++) { 25456668Sshin q = strchr(p, delim); 25556668Sshin if (!q || *q != delim) { 25656668Sshin parsefail: 25756668Sshin reply(500, 25856668Sshin "Invalid argument, rejected."); 25956668Sshin if (tmp) 26056668Sshin free(tmp); 26117433Spst usedefault = 1; 26256668Sshin goto eprt_done; 26317433Spst } 26456668Sshin *q++ = '\0'; 26556668Sshin result[i] = p; 26676096Smarkm if (ftpdebug) 26756668Sshin syslog(LOG_DEBUG, "%d: %s", i, p); 26856668Sshin p = q; 2691592Srgrimes } 27056668Sshin 27156668Sshin /* some more sanity check */ 27256668Sshin p = result[0]; 27356668Sshin while (*p) { 27456668Sshin if (!isdigit(*p)) 27556668Sshin goto parsefail; 27656668Sshin p++; 27756668Sshin } 27856668Sshin p = result[2]; 27956668Sshin while (*p) { 28056668Sshin if (!isdigit(*p)) 28156668Sshin goto parsefail; 28256668Sshin p++; 28356668Sshin } 28456668Sshin 28556668Sshin /* grab address */ 28656668Sshin memset(&hints, 0, sizeof(hints)); 28756668Sshin if (atoi(result[0]) == 1) 28856668Sshin hints.ai_family = PF_INET; 28956668Sshin#ifdef INET6 29056668Sshin else if (atoi(result[0]) == 2) 29156668Sshin hints.ai_family = PF_INET6; 29256668Sshin#endif 29356668Sshin else 29456668Sshin hints.ai_family = PF_UNSPEC; /*XXX*/ 29556668Sshin hints.ai_socktype = SOCK_STREAM; 29656668Sshin i = getaddrinfo(result[1], result[2], &hints, &res); 29756668Sshin if (i) 29856668Sshin goto parsefail; 29956668Sshin memcpy(&data_dest, res->ai_addr, res->ai_addrlen); 30056668Sshin#ifdef INET6 30156668Sshin if (his_addr.su_family == AF_INET6 30256668Sshin && data_dest.su_family == AF_INET6) { 30356668Sshin /* XXX more sanity checks! */ 30456668Sshin data_dest.su_sin6.sin6_scope_id = 30556668Sshin his_addr.su_sin6.sin6_scope_id; 30656668Sshin } 30756668Sshin#endif 30856668Sshin free(tmp); 30956668Sshin tmp = NULL; 31056668Sshin 31156668Sshin if (port_check("EPRT") == 1) 31256668Sshin goto eprt_done; 31356668Sshin#ifdef INET6 31456668Sshin if (his_addr.su_family != AF_INET6) { 31556668Sshin usedefault = 1; 31656668Sshin reply(500, "Invalid address rejected."); 31756668Sshin goto eprt_done; 31856668Sshin } 31956668Sshin if (port_check_v6("EPRT") == 1) 32056668Sshin goto eprt_done; 32156668Sshin#endif 32288935Sdwmalone eprt_done: 32388935Sdwmalone free($4); 3241592Srgrimes } 32517433Spst | PASV check_login CRLF 3261592Srgrimes { 32756668Sshin if (epsvall) 32856668Sshin reply(501, "no PASV allowed after EPSV ALL"); 32956668Sshin else if ($2) 33017433Spst passive(); 3311592Srgrimes } 33256668Sshin | LPSV check_login CRLF 33356668Sshin { 33456668Sshin if (epsvall) 33556668Sshin reply(501, "no LPSV allowed after EPSV ALL"); 33656668Sshin else if ($2) 33756668Sshin long_passive("LPSV", PF_UNSPEC); 33856668Sshin } 33970102Sphk | EPSV check_login_epsv SP NUMBER CRLF 34056668Sshin { 34156668Sshin if ($2) { 34256668Sshin int pf; 34392272Smaxim switch ($4.i) { 34456668Sshin case 1: 34556668Sshin pf = PF_INET; 34656668Sshin break; 34756668Sshin#ifdef INET6 34856668Sshin case 2: 34956668Sshin pf = PF_INET6; 35056668Sshin break; 35156668Sshin#endif 35256668Sshin default: 35356668Sshin pf = -1; /*junk value*/ 35456668Sshin break; 35556668Sshin } 35656668Sshin long_passive("EPSV", pf); 35756668Sshin } 35856668Sshin } 35970102Sphk | EPSV check_login_epsv SP ALL CRLF 36056668Sshin { 36156668Sshin if ($2) { 36256668Sshin reply(200, 36356668Sshin "EPSV ALL command successful."); 36456668Sshin epsvall++; 36556668Sshin } 36656668Sshin } 36770102Sphk | EPSV check_login_epsv CRLF 36856668Sshin { 36956668Sshin if ($2) 37056668Sshin long_passive("EPSV", PF_UNSPEC); 37156668Sshin } 37271278Sjedgar | TYPE check_login SP type_code CRLF 3731592Srgrimes { 37471278Sjedgar if ($2) { 37571278Sjedgar switch (cmd_type) { 3761592Srgrimes 37771278Sjedgar case TYPE_A: 37871278Sjedgar if (cmd_form == FORM_N) { 37971278Sjedgar reply(200, "Type set to A."); 38071278Sjedgar type = cmd_type; 38171278Sjedgar form = cmd_form; 38271278Sjedgar } else 38371278Sjedgar reply(504, "Form must be N."); 38471278Sjedgar break; 3851592Srgrimes 38671278Sjedgar case TYPE_E: 38771278Sjedgar reply(504, "Type E not implemented."); 38871278Sjedgar break; 3891592Srgrimes 39071278Sjedgar case TYPE_I: 39171278Sjedgar reply(200, "Type set to I."); 39271278Sjedgar type = cmd_type; 39371278Sjedgar break; 3941592Srgrimes 39571278Sjedgar case TYPE_L: 3961592Srgrimes#if NBBY == 8 39771278Sjedgar if (cmd_bytesz == 8) { 39871278Sjedgar reply(200, 39971278Sjedgar "Type set to L (byte size 8)."); 40071278Sjedgar type = cmd_type; 40171278Sjedgar } else 40271278Sjedgar reply(504, "Byte size must be 8."); 4031592Srgrimes#else /* NBBY == 8 */ 40471278Sjedgar UNIMPLEMENTED for NBBY != 8 4051592Srgrimes#endif /* NBBY == 8 */ 40671278Sjedgar } 4071592Srgrimes } 4081592Srgrimes } 40971278Sjedgar | STRU check_login SP struct_code CRLF 4101592Srgrimes { 41171278Sjedgar if ($2) { 41271278Sjedgar switch ($4) { 4131592Srgrimes 41471278Sjedgar case STRU_F: 41571278Sjedgar reply(200, "STRU F ok."); 41671278Sjedgar break; 4171592Srgrimes 41871278Sjedgar default: 41971278Sjedgar reply(504, "Unimplemented STRU type."); 42071278Sjedgar } 4211592Srgrimes } 4221592Srgrimes } 42371278Sjedgar | MODE check_login SP mode_code CRLF 4241592Srgrimes { 42571278Sjedgar if ($2) { 42671278Sjedgar switch ($4) { 4271592Srgrimes 42871278Sjedgar case MODE_S: 42971278Sjedgar reply(200, "MODE S ok."); 43071278Sjedgar break; 43171278Sjedgar 43271278Sjedgar default: 43371278Sjedgar reply(502, "Unimplemented MODE type."); 43471278Sjedgar } 4351592Srgrimes } 4361592Srgrimes } 43771278Sjedgar | ALLO check_login SP NUMBER CRLF 4381592Srgrimes { 43971278Sjedgar if ($2) { 44071278Sjedgar reply(202, "ALLO command ignored."); 44171278Sjedgar } 4421592Srgrimes } 44371278Sjedgar | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 4441592Srgrimes { 44571278Sjedgar if ($2) { 44671278Sjedgar reply(202, "ALLO command ignored."); 44771278Sjedgar } 4481592Srgrimes } 4491592Srgrimes | RETR check_login SP pathname CRLF 4501592Srgrimes { 45182796Ssheldonh if (noretr || (guest && noguestretr)) 45282460Snik reply(500, "RETR command is disabled"); 45382460Snik else if ($2 && $4 != NULL) 4541592Srgrimes retrieve((char *) 0, $4); 45582460Snik 4561592Srgrimes if ($4 != NULL) 4571592Srgrimes free($4); 4581592Srgrimes } 45970102Sphk | STOR check_login_ro SP pathname CRLF 4601592Srgrimes { 4611592Srgrimes if ($2 && $4 != NULL) 4621592Srgrimes store($4, "w", 0); 4631592Srgrimes if ($4 != NULL) 4641592Srgrimes free($4); 4651592Srgrimes } 46670102Sphk | APPE check_login_ro SP pathname CRLF 4671592Srgrimes { 4681592Srgrimes if ($2 && $4 != NULL) 4691592Srgrimes store($4, "a", 0); 4701592Srgrimes if ($4 != NULL) 4711592Srgrimes free($4); 4721592Srgrimes } 4731592Srgrimes | NLST check_login CRLF 4741592Srgrimes { 4751592Srgrimes if ($2) 4761592Srgrimes send_file_list("."); 4771592Srgrimes } 4781592Srgrimes | NLST check_login SP STRING CRLF 4791592Srgrimes { 4801592Srgrimes if ($2 && $4 != NULL) 4811592Srgrimes send_file_list($4); 4821592Srgrimes if ($4 != NULL) 4831592Srgrimes free($4); 4841592Srgrimes } 4851592Srgrimes | LIST check_login CRLF 4861592Srgrimes { 4871592Srgrimes if ($2) 4881592Srgrimes retrieve("/bin/ls -lgA", ""); 4891592Srgrimes } 49075567Speter | LIST check_login SP pathstring CRLF 4911592Srgrimes { 4921592Srgrimes if ($2 && $4 != NULL) 4931592Srgrimes retrieve("/bin/ls -lgA %s", $4); 4941592Srgrimes if ($4 != NULL) 4951592Srgrimes free($4); 4961592Srgrimes } 4971592Srgrimes | STAT check_login SP pathname CRLF 4981592Srgrimes { 4991592Srgrimes if ($2 && $4 != NULL) 5001592Srgrimes statfilecmd($4); 5011592Srgrimes if ($4 != NULL) 5021592Srgrimes free($4); 5031592Srgrimes } 50471278Sjedgar | STAT check_login CRLF 5051592Srgrimes { 50671278Sjedgar if ($2) { 50771278Sjedgar statcmd(); 50871278Sjedgar } 5091592Srgrimes } 51070102Sphk | DELE check_login_ro SP pathname CRLF 5111592Srgrimes { 5121592Srgrimes if ($2 && $4 != NULL) 5131592Srgrimes delete($4); 5141592Srgrimes if ($4 != NULL) 5151592Srgrimes free($4); 5161592Srgrimes } 51770102Sphk | RNTO check_login_ro SP pathname CRLF 5181592Srgrimes { 51917433Spst if ($2) { 52017433Spst if (fromname) { 52117433Spst renamecmd(fromname, $4); 52217433Spst free(fromname); 52317433Spst fromname = (char *) 0; 52417433Spst } else { 52517433Spst reply(503, "Bad sequence of commands."); 52617433Spst } 5271592Srgrimes } 52817433Spst free($4); 5291592Srgrimes } 53071278Sjedgar | ABOR check_login CRLF 5311592Srgrimes { 53271278Sjedgar if ($2) 53371278Sjedgar reply(225, "ABOR command successful."); 5341592Srgrimes } 5351592Srgrimes | CWD check_login CRLF 5361592Srgrimes { 53769234Sdanny if ($2) { 53869234Sdanny if (guest) 53969234Sdanny cwd("/"); 54069234Sdanny else 54169234Sdanny cwd(pw->pw_dir); 54269234Sdanny } 5431592Srgrimes } 5441592Srgrimes | CWD check_login SP pathname CRLF 5451592Srgrimes { 5461592Srgrimes if ($2 && $4 != NULL) 5471592Srgrimes cwd($4); 5481592Srgrimes if ($4 != NULL) 5491592Srgrimes free($4); 5501592Srgrimes } 5511592Srgrimes | HELP CRLF 5521592Srgrimes { 5531592Srgrimes help(cmdtab, (char *) 0); 5541592Srgrimes } 5551592Srgrimes | HELP SP STRING CRLF 5561592Srgrimes { 5571592Srgrimes char *cp = $3; 5581592Srgrimes 5591592Srgrimes if (strncasecmp(cp, "SITE", 4) == 0) { 5601592Srgrimes cp = $3 + 4; 5611592Srgrimes if (*cp == ' ') 5621592Srgrimes cp++; 5631592Srgrimes if (*cp) 5641592Srgrimes help(sitetab, cp); 5651592Srgrimes else 5661592Srgrimes help(sitetab, (char *) 0); 5671592Srgrimes } else 5681592Srgrimes help(cmdtab, $3); 56988935Sdwmalone free($3); 5701592Srgrimes } 5711592Srgrimes | NOOP CRLF 5721592Srgrimes { 5731592Srgrimes reply(200, "NOOP command successful."); 5741592Srgrimes } 57570102Sphk | MKD check_login_ro SP pathname CRLF 5761592Srgrimes { 5771592Srgrimes if ($2 && $4 != NULL) 5781592Srgrimes makedir($4); 5791592Srgrimes if ($4 != NULL) 5801592Srgrimes free($4); 5811592Srgrimes } 58270102Sphk | RMD check_login_ro SP pathname CRLF 5831592Srgrimes { 5841592Srgrimes if ($2 && $4 != NULL) 5851592Srgrimes removedir($4); 5861592Srgrimes if ($4 != NULL) 5871592Srgrimes free($4); 5881592Srgrimes } 5891592Srgrimes | PWD check_login CRLF 5901592Srgrimes { 5911592Srgrimes if ($2) 5921592Srgrimes pwd(); 5931592Srgrimes } 5941592Srgrimes | CDUP check_login CRLF 5951592Srgrimes { 5961592Srgrimes if ($2) 5971592Srgrimes cwd(".."); 5981592Srgrimes } 5991592Srgrimes | SITE SP HELP CRLF 6001592Srgrimes { 6011592Srgrimes help(sitetab, (char *) 0); 6021592Srgrimes } 6031592Srgrimes | SITE SP HELP SP STRING CRLF 6041592Srgrimes { 6051592Srgrimes help(sitetab, $5); 60688935Sdwmalone free($5); 6071592Srgrimes } 60875535Sphk | SITE SP MDFIVE check_login SP pathname CRLF 60975535Sphk { 61075535Sphk char p[64], *q; 61175535Sphk 61275535Sphk if ($4) { 61375535Sphk q = MD5File($6, p); 61475535Sphk if (q != NULL) 61575535Sphk reply(200, "MD5(%s) = %s", $6, p); 61675535Sphk else 61775535Sphk perror_reply(550, $6); 61875535Sphk } 61988935Sdwmalone if ($6) 62088935Sdwmalone free($6); 62175535Sphk } 6221592Srgrimes | SITE SP UMASK check_login CRLF 6231592Srgrimes { 6241592Srgrimes int oldmask; 6251592Srgrimes 6261592Srgrimes if ($4) { 6271592Srgrimes oldmask = umask(0); 6281592Srgrimes (void) umask(oldmask); 6291592Srgrimes reply(200, "Current UMASK is %03o", oldmask); 6301592Srgrimes } 6311592Srgrimes } 6321592Srgrimes | SITE SP UMASK check_login SP octal_number CRLF 6331592Srgrimes { 6341592Srgrimes int oldmask; 6351592Srgrimes 6361592Srgrimes if ($4) { 6371592Srgrimes if (($6 == -1) || ($6 > 0777)) { 6381592Srgrimes reply(501, "Bad UMASK value"); 6391592Srgrimes } else { 6401592Srgrimes oldmask = umask($6); 6411592Srgrimes reply(200, 6421592Srgrimes "UMASK set to %03o (was %03o)", 6431592Srgrimes $6, oldmask); 6441592Srgrimes } 6451592Srgrimes } 6461592Srgrimes } 64770102Sphk | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF 6481592Srgrimes { 6491592Srgrimes if ($4 && ($8 != NULL)) { 6501592Srgrimes if ($6 > 0777) 6511592Srgrimes reply(501, 6521592Srgrimes "CHMOD: Mode value must be between 0 and 0777"); 6531592Srgrimes else if (chmod($8, $6) < 0) 6541592Srgrimes perror_reply(550, $8); 6551592Srgrimes else 6561592Srgrimes reply(200, "CHMOD command successful."); 6571592Srgrimes } 6581592Srgrimes if ($8 != NULL) 6591592Srgrimes free($8); 6601592Srgrimes } 66171278Sjedgar | SITE SP check_login IDLE CRLF 6621592Srgrimes { 66371278Sjedgar if ($3) 66471278Sjedgar reply(200, 66571278Sjedgar "Current IDLE time limit is %d seconds; max %d", 66671278Sjedgar timeout, maxtimeout); 6671592Srgrimes } 66871278Sjedgar | SITE SP check_login IDLE SP NUMBER CRLF 6691592Srgrimes { 67071278Sjedgar if ($3) { 67192272Smaxim if ($6.i < 30 || $6.i > maxtimeout) { 67271278Sjedgar reply(501, 67371278Sjedgar "Maximum IDLE time must be between 30 and %d seconds", 67471278Sjedgar maxtimeout); 67571278Sjedgar } else { 67692272Smaxim timeout = $6.i; 67771278Sjedgar (void) alarm((unsigned) timeout); 67871278Sjedgar reply(200, 67971278Sjedgar "Maximum IDLE time set to %d seconds", 68071278Sjedgar timeout); 68171278Sjedgar } 6821592Srgrimes } 6831592Srgrimes } 68470102Sphk | STOU check_login_ro SP pathname CRLF 6851592Srgrimes { 6861592Srgrimes if ($2 && $4 != NULL) 6871592Srgrimes store($4, "w", 1); 6881592Srgrimes if ($4 != NULL) 6891592Srgrimes free($4); 6901592Srgrimes } 69171278Sjedgar | SYST check_login CRLF 6921592Srgrimes { 69371278Sjedgar if ($2) 6941592Srgrimes#ifdef unix 6951592Srgrimes#ifdef BSD 6961592Srgrimes reply(215, "UNIX Type: L%d Version: BSD-%d", 6971592Srgrimes NBBY, BSD); 6981592Srgrimes#else /* BSD */ 6991592Srgrimes reply(215, "UNIX Type: L%d", NBBY); 7001592Srgrimes#endif /* BSD */ 7011592Srgrimes#else /* unix */ 7021592Srgrimes reply(215, "UNKNOWN Type: L%d", NBBY); 7031592Srgrimes#endif /* unix */ 7041592Srgrimes } 7051592Srgrimes 7061592Srgrimes /* 7071592Srgrimes * SIZE is not in RFC959, but Postel has blessed it and 7081592Srgrimes * it will be in the updated RFC. 7091592Srgrimes * 7101592Srgrimes * Return size of file in a format suitable for 7111592Srgrimes * using with RESTART (we just count bytes). 7121592Srgrimes */ 7131592Srgrimes | SIZE check_login SP pathname CRLF 7141592Srgrimes { 7151592Srgrimes if ($2 && $4 != NULL) 7161592Srgrimes sizecmd($4); 7171592Srgrimes if ($4 != NULL) 7181592Srgrimes free($4); 7191592Srgrimes } 7201592Srgrimes 7211592Srgrimes /* 7221592Srgrimes * MDTM is not in RFC959, but Postel has blessed it and 7231592Srgrimes * it will be in the updated RFC. 7241592Srgrimes * 7251592Srgrimes * Return modification time of file as an ISO 3307 7261592Srgrimes * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 7271592Srgrimes * where xxx is the fractional second (of any precision, 7281592Srgrimes * not necessarily 3 digits) 7291592Srgrimes */ 7301592Srgrimes | MDTM check_login SP pathname CRLF 7311592Srgrimes { 7321592Srgrimes if ($2 && $4 != NULL) { 7331592Srgrimes struct stat stbuf; 7341592Srgrimes if (stat($4, &stbuf) < 0) 7351592Srgrimes reply(550, "%s: %s", 7361592Srgrimes $4, strerror(errno)); 7371592Srgrimes else if (!S_ISREG(stbuf.st_mode)) { 7381592Srgrimes reply(550, "%s: not a plain file.", $4); 7391592Srgrimes } else { 7401592Srgrimes struct tm *t; 7411592Srgrimes t = gmtime(&stbuf.st_mtime); 7421592Srgrimes reply(213, 74317435Spst "%04d%02d%02d%02d%02d%02d", 74417435Spst 1900 + t->tm_year, 74517435Spst t->tm_mon+1, t->tm_mday, 7461592Srgrimes t->tm_hour, t->tm_min, t->tm_sec); 7471592Srgrimes } 7481592Srgrimes } 7491592Srgrimes if ($4 != NULL) 7501592Srgrimes free($4); 7511592Srgrimes } 7521592Srgrimes | QUIT CRLF 7531592Srgrimes { 7541592Srgrimes reply(221, "Goodbye."); 7551592Srgrimes dologout(0); 7561592Srgrimes } 75789935Syar | error 7581592Srgrimes { 75989935Syar yyclearin; /* discard lookahead data */ 76089935Syar yyerrok; /* clear error condition */ 76189935Syar state = 0; /* reset lexer state */ 7621592Srgrimes } 7631592Srgrimes ; 7641592Srgrimesrcmd 76570102Sphk : RNFR check_login_ro SP pathname CRLF 7661592Srgrimes { 7671592Srgrimes restart_point = (off_t) 0; 7681592Srgrimes if ($2 && $4) { 76988935Sdwmalone if (fromname) 77088935Sdwmalone free(fromname); 77188935Sdwmalone fromname = (char *) 0; 77288935Sdwmalone if (renamefrom($4)) 77388935Sdwmalone fromname = $4; 77488935Sdwmalone else 7751592Srgrimes free($4); 77688935Sdwmalone } else if ($4) { 77788935Sdwmalone free($4); 7781592Srgrimes } 7791592Srgrimes } 78092272Smaxim | REST check_login SP NUMBER CRLF 7811592Srgrimes { 78271278Sjedgar if ($2) { 78388935Sdwmalone if (fromname) 78488935Sdwmalone free(fromname); 78571278Sjedgar fromname = (char *) 0; 78692272Smaxim restart_point = $4.o; 78792272Smaxim reply(350, "Restarting at %llu. %s", 78871278Sjedgar restart_point, 78971278Sjedgar "Send STORE or RETRIEVE to initiate transfer."); 79071278Sjedgar } 7911592Srgrimes } 7921592Srgrimes ; 7931592Srgrimes 7941592Srgrimesusername 7951592Srgrimes : STRING 7961592Srgrimes ; 7971592Srgrimes 7981592Srgrimespassword 7991592Srgrimes : /* empty */ 8001592Srgrimes { 8011592Srgrimes $$ = (char *)calloc(1, sizeof(char)); 8021592Srgrimes } 8031592Srgrimes | STRING 8041592Srgrimes ; 8051592Srgrimes 8061592Srgrimesbyte_size 8071592Srgrimes : NUMBER 80892272Smaxim { 80992272Smaxim $$ = $1.i; 81092272Smaxim } 8111592Srgrimes ; 8121592Srgrimes 8131592Srgrimeshost_port 8141592Srgrimes : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 8151592Srgrimes NUMBER COMMA NUMBER 8161592Srgrimes { 8171592Srgrimes char *a, *p; 8181592Srgrimes 81956668Sshin data_dest.su_len = sizeof(struct sockaddr_in); 82056668Sshin data_dest.su_family = AF_INET; 82156668Sshin p = (char *)&data_dest.su_sin.sin_port; 82292272Smaxim p[0] = $9.i; p[1] = $11.i; 82356668Sshin a = (char *)&data_dest.su_sin.sin_addr; 82492272Smaxim a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; 8251592Srgrimes } 8261592Srgrimes ; 8271592Srgrimes 82856668Sshinhost_long_port 82956668Sshin : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83056668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83156668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83256668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83356668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83456668Sshin NUMBER 83556668Sshin { 83656668Sshin char *a, *p; 83756668Sshin 83856668Sshin memset(&data_dest, 0, sizeof(data_dest)); 83956668Sshin data_dest.su_len = sizeof(struct sockaddr_in6); 84056668Sshin data_dest.su_family = AF_INET6; 84156668Sshin p = (char *)&data_dest.su_port; 84292272Smaxim p[0] = $39.i; p[1] = $41.i; 84356668Sshin a = (char *)&data_dest.su_sin6.sin6_addr; 84492272Smaxim a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 84592272Smaxim a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i; 84692272Smaxim a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i; 84792272Smaxim a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i; 84856668Sshin if (his_addr.su_family == AF_INET6) { 84956668Sshin /* XXX more sanity checks! */ 85056668Sshin data_dest.su_sin6.sin6_scope_id = 85156668Sshin his_addr.su_sin6.sin6_scope_id; 85256668Sshin } 85392272Smaxim if ($1.i != 6 || $3.i != 16 || $37.i != 2) 85456668Sshin memset(&data_dest, 0, sizeof(data_dest)); 85556668Sshin } 85656668Sshin | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 85756668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 85856668Sshin NUMBER 85956668Sshin { 86056668Sshin char *a, *p; 86156668Sshin 86256668Sshin memset(&data_dest, 0, sizeof(data_dest)); 86356668Sshin data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 86456668Sshin data_dest.su_family = AF_INET; 86556668Sshin p = (char *)&data_dest.su_port; 86692272Smaxim p[0] = $15.i; p[1] = $17.i; 86756668Sshin a = (char *)&data_dest.su_sin.sin_addr; 86892272Smaxim a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 86992272Smaxim if ($1.i != 4 || $3.i != 4 || $13.i != 2) 87056668Sshin memset(&data_dest, 0, sizeof(data_dest)); 87156668Sshin } 87256668Sshin ; 87356668Sshin 8741592Srgrimesform_code 8751592Srgrimes : N 8761592Srgrimes { 8771592Srgrimes $$ = FORM_N; 8781592Srgrimes } 8791592Srgrimes | T 8801592Srgrimes { 8811592Srgrimes $$ = FORM_T; 8821592Srgrimes } 8831592Srgrimes | C 8841592Srgrimes { 8851592Srgrimes $$ = FORM_C; 8861592Srgrimes } 8871592Srgrimes ; 8881592Srgrimes 8891592Srgrimestype_code 8901592Srgrimes : A 8911592Srgrimes { 8921592Srgrimes cmd_type = TYPE_A; 8931592Srgrimes cmd_form = FORM_N; 8941592Srgrimes } 8951592Srgrimes | A SP form_code 8961592Srgrimes { 8971592Srgrimes cmd_type = TYPE_A; 8981592Srgrimes cmd_form = $3; 8991592Srgrimes } 9001592Srgrimes | E 9011592Srgrimes { 9021592Srgrimes cmd_type = TYPE_E; 9031592Srgrimes cmd_form = FORM_N; 9041592Srgrimes } 9051592Srgrimes | E SP form_code 9061592Srgrimes { 9071592Srgrimes cmd_type = TYPE_E; 9081592Srgrimes cmd_form = $3; 9091592Srgrimes } 9101592Srgrimes | I 9111592Srgrimes { 9121592Srgrimes cmd_type = TYPE_I; 9131592Srgrimes } 9141592Srgrimes | L 9151592Srgrimes { 9161592Srgrimes cmd_type = TYPE_L; 9171592Srgrimes cmd_bytesz = NBBY; 9181592Srgrimes } 9191592Srgrimes | L SP byte_size 9201592Srgrimes { 9211592Srgrimes cmd_type = TYPE_L; 9221592Srgrimes cmd_bytesz = $3; 9231592Srgrimes } 9241592Srgrimes /* this is for a bug in the BBN ftp */ 9251592Srgrimes | L byte_size 9261592Srgrimes { 9271592Srgrimes cmd_type = TYPE_L; 9281592Srgrimes cmd_bytesz = $2; 9291592Srgrimes } 9301592Srgrimes ; 9311592Srgrimes 9321592Srgrimesstruct_code 9331592Srgrimes : F 9341592Srgrimes { 9351592Srgrimes $$ = STRU_F; 9361592Srgrimes } 9371592Srgrimes | R 9381592Srgrimes { 9391592Srgrimes $$ = STRU_R; 9401592Srgrimes } 9411592Srgrimes | P 9421592Srgrimes { 9431592Srgrimes $$ = STRU_P; 9441592Srgrimes } 9451592Srgrimes ; 9461592Srgrimes 9471592Srgrimesmode_code 9481592Srgrimes : S 9491592Srgrimes { 9501592Srgrimes $$ = MODE_S; 9511592Srgrimes } 9521592Srgrimes | B 9531592Srgrimes { 9541592Srgrimes $$ = MODE_B; 9551592Srgrimes } 9561592Srgrimes | C 9571592Srgrimes { 9581592Srgrimes $$ = MODE_C; 9591592Srgrimes } 9601592Srgrimes ; 9611592Srgrimes 9621592Srgrimespathname 9631592Srgrimes : pathstring 9641592Srgrimes { 9651592Srgrimes /* 9661592Srgrimes * Problem: this production is used for all pathname 9671592Srgrimes * processing, but only gives a 550 error reply. 9681592Srgrimes * This is a valid reply in some cases but not in others. 9691592Srgrimes */ 97075567Speter if (logged_in && $1) { 9711592Srgrimes glob_t gl; 9721592Srgrimes int flags = 973100222Smikeh GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; 9741592Srgrimes 9751592Srgrimes memset(&gl, 0, sizeof(gl)); 97675560Sjedgar flags |= GLOB_MAXPATH; 97775560Sjedgar gl.gl_matchc = MAXGLOBARGS; 9781592Srgrimes if (glob($1, flags, NULL, &gl) || 9791592Srgrimes gl.gl_pathc == 0) { 9801592Srgrimes reply(550, "not found"); 9811592Srgrimes $$ = NULL; 98275567Speter } else if (gl.gl_pathc > 1) { 98375567Speter reply(550, "ambiguous"); 98475567Speter $$ = NULL; 9851592Srgrimes } else { 9861592Srgrimes $$ = strdup(gl.gl_pathv[0]); 9871592Srgrimes } 9881592Srgrimes globfree(&gl); 9891592Srgrimes free($1); 9901592Srgrimes } else 9911592Srgrimes $$ = $1; 9921592Srgrimes } 9931592Srgrimes ; 9941592Srgrimes 9951592Srgrimespathstring 9961592Srgrimes : STRING 9971592Srgrimes ; 9981592Srgrimes 9991592Srgrimesoctal_number 10001592Srgrimes : NUMBER 10011592Srgrimes { 10021592Srgrimes int ret, dec, multby, digit; 10031592Srgrimes 10041592Srgrimes /* 10051592Srgrimes * Convert a number that was read as decimal number 10061592Srgrimes * to what it would be if it had been read as octal. 10071592Srgrimes */ 100892272Smaxim dec = $1.i; 10091592Srgrimes multby = 1; 10101592Srgrimes ret = 0; 10111592Srgrimes while (dec) { 10121592Srgrimes digit = dec%10; 10131592Srgrimes if (digit > 7) { 10141592Srgrimes ret = -1; 10151592Srgrimes break; 10161592Srgrimes } 10171592Srgrimes ret += digit * multby; 10181592Srgrimes multby *= 8; 10191592Srgrimes dec /= 10; 10201592Srgrimes } 10211592Srgrimes $$ = ret; 10221592Srgrimes } 10231592Srgrimes ; 10241592Srgrimes 10251592Srgrimes 10261592Srgrimescheck_login 10271592Srgrimes : /* empty */ 10281592Srgrimes { 102970102Sphk $$ = check_login1(); 10301592Srgrimes } 10311592Srgrimes ; 10321592Srgrimes 103370102Sphkcheck_login_epsv 103470102Sphk : /* empty */ 103570102Sphk { 103670102Sphk if (noepsv) { 103770102Sphk reply(500, "EPSV command disabled"); 103870102Sphk $$ = 0; 103970102Sphk } 104070102Sphk else 104170102Sphk $$ = check_login1(); 104270102Sphk } 104370102Sphk ; 104470102Sphk 104570102Sphkcheck_login_ro 104670102Sphk : /* empty */ 104770102Sphk { 104870102Sphk if (readonly) { 104972710Sdes reply(550, "Permission denied."); 105070102Sphk $$ = 0; 105170102Sphk } 105270102Sphk else 105370102Sphk $$ = check_login1(); 105470102Sphk } 105570102Sphk ; 105670102Sphk 10571592Srgrimes%% 10581592Srgrimes 10591592Srgrimes#define CMD 0 /* beginning of command */ 10601592Srgrimes#define ARGS 1 /* expect miscellaneous arguments */ 10611592Srgrimes#define STR1 2 /* expect SP followed by STRING */ 10621592Srgrimes#define STR2 3 /* expect STRING */ 10631592Srgrimes#define OSTR 4 /* optional SP then STRING */ 106475556Sgreen#define ZSTR1 5 /* optional SP then optional STRING */ 10651592Srgrimes#define ZSTR2 6 /* optional STRING after SP */ 10661592Srgrimes#define SITECMD 7 /* SITE command */ 10671592Srgrimes#define NSTR 8 /* Number followed by a string */ 10681592Srgrimes 106975560Sjedgar#define MAXGLOBARGS 1000 107075560Sjedgar 10711592Srgrimesstruct tab { 10721592Srgrimes char *name; 10731592Srgrimes short token; 10741592Srgrimes short state; 10751592Srgrimes short implemented; /* 1 if command is implemented */ 10761592Srgrimes char *help; 10771592Srgrimes}; 10781592Srgrimes 10791592Srgrimesstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 10801592Srgrimes { "USER", USER, STR1, 1, "<sp> username" }, 108175556Sgreen { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" }, 10821592Srgrimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 10831592Srgrimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 10841592Srgrimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 10851592Srgrimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 10861592Srgrimes { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 108756668Sshin { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 108856668Sshin { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 10891592Srgrimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 109056668Sshin { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 109156668Sshin { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 10921592Srgrimes { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 10931592Srgrimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 10941592Srgrimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 10951592Srgrimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 10961592Srgrimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 10971592Srgrimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 10981592Srgrimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 10991592Srgrimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 11001592Srgrimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 11011592Srgrimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 11021592Srgrimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 11031592Srgrimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 11041592Srgrimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 11051592Srgrimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 11061592Srgrimes { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 11071592Srgrimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 11081592Srgrimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 11091592Srgrimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 11101592Srgrimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 11111592Srgrimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 11121592Srgrimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 11131592Srgrimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 11141592Srgrimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 11151592Srgrimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 11161592Srgrimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 11171592Srgrimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 11181592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11191592Srgrimes { "NOOP", NOOP, ARGS, 1, "" }, 11201592Srgrimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 11211592Srgrimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 11221592Srgrimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 11231592Srgrimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 11241592Srgrimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 11251592Srgrimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 11261592Srgrimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11271592Srgrimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11281592Srgrimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 11291592Srgrimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 11301592Srgrimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 11311592Srgrimes { NULL, 0, 0, 0, 0 } 11321592Srgrimes}; 11331592Srgrimes 11341592Srgrimesstruct tab sitetab[] = { 113575535Sphk { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" }, 11361592Srgrimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 11371592Srgrimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 11381592Srgrimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 11391592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11401592Srgrimes { NULL, 0, 0, 0, 0 } 11411592Srgrimes}; 11421592Srgrimes 114390148Simpstatic char *copy(char *); 114490148Simpstatic void help(struct tab *, char *); 11451592Srgrimesstatic struct tab * 114690148Simp lookup(struct tab *, char *); 114790148Simpstatic int port_check(const char *); 114890148Simpstatic int port_check_v6(const char *); 114990148Simpstatic void sizecmd(char *); 115090148Simpstatic void toolong(int); 115190148Simpstatic void v4map_data_dest(void); 115290148Simpstatic int yylex(void); 11531592Srgrimes 11541592Srgrimesstatic struct tab * 115590148Simplookup(struct tab *p, char *cmd) 11561592Srgrimes{ 11571592Srgrimes 11581592Srgrimes for (; p->name != NULL; p++) 11591592Srgrimes if (strcmp(cmd, p->name) == 0) 11601592Srgrimes return (p); 11611592Srgrimes return (0); 11621592Srgrimes} 11631592Srgrimes 11641592Srgrimes#include <arpa/telnet.h> 11651592Srgrimes 11661592Srgrimes/* 11671592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes. 11681592Srgrimes */ 11691592Srgrimeschar * 117090148Simpgetline(char *s, int n, FILE *iop) 11711592Srgrimes{ 11721592Srgrimes int c; 11731592Srgrimes register char *cs; 11741592Srgrimes 11751592Srgrimes cs = s; 11761592Srgrimes/* tmpline may contain saved command from urgent mode interruption */ 11771592Srgrimes for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 11781592Srgrimes *cs++ = tmpline[c]; 11791592Srgrimes if (tmpline[c] == '\n') { 11801592Srgrimes *cs++ = '\0'; 118176096Smarkm if (ftpdebug) 11821592Srgrimes syslog(LOG_DEBUG, "command: %s", s); 11831592Srgrimes tmpline[0] = '\0'; 11841592Srgrimes return(s); 11851592Srgrimes } 11861592Srgrimes if (c == 0) 11871592Srgrimes tmpline[0] = '\0'; 11881592Srgrimes } 11891592Srgrimes while ((c = getc(iop)) != EOF) { 11901592Srgrimes c &= 0377; 11911592Srgrimes if (c == IAC) { 11921592Srgrimes if ((c = getc(iop)) != EOF) { 11931592Srgrimes c &= 0377; 11941592Srgrimes switch (c) { 11951592Srgrimes case WILL: 11961592Srgrimes case WONT: 11971592Srgrimes c = getc(iop); 11981592Srgrimes printf("%c%c%c", IAC, DONT, 0377&c); 11991592Srgrimes (void) fflush(stdout); 12001592Srgrimes continue; 12011592Srgrimes case DO: 12021592Srgrimes case DONT: 12031592Srgrimes c = getc(iop); 12041592Srgrimes printf("%c%c%c", IAC, WONT, 0377&c); 12051592Srgrimes (void) fflush(stdout); 12061592Srgrimes continue; 12071592Srgrimes case IAC: 12081592Srgrimes break; 12091592Srgrimes default: 12101592Srgrimes continue; /* ignore command */ 12111592Srgrimes } 12121592Srgrimes } 12131592Srgrimes } 12141592Srgrimes *cs++ = c; 12151592Srgrimes if (--n <= 0 || c == '\n') 12161592Srgrimes break; 12171592Srgrimes } 12181592Srgrimes if (c == EOF && cs == s) 12191592Srgrimes return (NULL); 12201592Srgrimes *cs++ = '\0'; 122176096Smarkm if (ftpdebug) { 12221592Srgrimes if (!guest && strncasecmp("pass ", s, 5) == 0) { 12231592Srgrimes /* Don't syslog passwords */ 12241592Srgrimes syslog(LOG_DEBUG, "command: %.5s ???", s); 12251592Srgrimes } else { 12261592Srgrimes register char *cp; 12271592Srgrimes register int len; 12281592Srgrimes 12291592Srgrimes /* Don't syslog trailing CR-LF */ 12301592Srgrimes len = strlen(s); 12311592Srgrimes cp = s + len - 1; 12321592Srgrimes while (cp >= s && (*cp == '\n' || *cp == '\r')) { 12331592Srgrimes --cp; 12341592Srgrimes --len; 12351592Srgrimes } 12361592Srgrimes syslog(LOG_DEBUG, "command: %.*s", len, s); 12371592Srgrimes } 12381592Srgrimes } 12391592Srgrimes return (s); 12401592Srgrimes} 12411592Srgrimes 12421592Srgrimesstatic void 124390148Simptoolong(int signo) 12441592Srgrimes{ 12451592Srgrimes 12461592Srgrimes reply(421, 12471592Srgrimes "Timeout (%d seconds): closing control connection.", timeout); 12481592Srgrimes if (logging) 12491592Srgrimes syslog(LOG_INFO, "User %s timed out after %d seconds", 12501592Srgrimes (pw ? pw -> pw_name : "unknown"), timeout); 12511592Srgrimes dologout(1); 12521592Srgrimes} 12531592Srgrimes 12541592Srgrimesstatic int 125590148Simpyylex(void) 12561592Srgrimes{ 125789935Syar static int cpos; 12581592Srgrimes char *cp, *cp2; 12591592Srgrimes struct tab *p; 12601592Srgrimes int n; 12611592Srgrimes char c; 12621592Srgrimes 12631592Srgrimes for (;;) { 12641592Srgrimes switch (state) { 12651592Srgrimes 12661592Srgrimes case CMD: 12671592Srgrimes (void) signal(SIGALRM, toolong); 12681592Srgrimes (void) alarm((unsigned) timeout); 12691592Srgrimes if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 12701592Srgrimes reply(221, "You could at least say goodbye."); 12711592Srgrimes dologout(0); 12721592Srgrimes } 12731592Srgrimes (void) alarm(0); 12741592Srgrimes#ifdef SETPROCTITLE 127529574Sphk if (strncasecmp(cbuf, "PASS", 4) != 0) 12761592Srgrimes setproctitle("%s: %s", proctitle, cbuf); 12771592Srgrimes#endif /* SETPROCTITLE */ 12781592Srgrimes if ((cp = strchr(cbuf, '\r'))) { 12791592Srgrimes *cp++ = '\n'; 12801592Srgrimes *cp = '\0'; 12811592Srgrimes } 12821592Srgrimes if ((cp = strpbrk(cbuf, " \n"))) 12831592Srgrimes cpos = cp - cbuf; 12841592Srgrimes if (cpos == 0) 12851592Srgrimes cpos = 4; 12861592Srgrimes c = cbuf[cpos]; 12871592Srgrimes cbuf[cpos] = '\0'; 12881592Srgrimes upper(cbuf); 12891592Srgrimes p = lookup(cmdtab, cbuf); 12901592Srgrimes cbuf[cpos] = c; 12913776Spst if (p != 0) { 12921592Srgrimes if (p->implemented == 0) { 12931592Srgrimes nack(p->name); 129489935Syar return (LEXERR); 12951592Srgrimes } 12961592Srgrimes state = p->state; 12971592Srgrimes yylval.s = p->name; 12981592Srgrimes return (p->token); 12991592Srgrimes } 13001592Srgrimes break; 13011592Srgrimes 13021592Srgrimes case SITECMD: 13031592Srgrimes if (cbuf[cpos] == ' ') { 13041592Srgrimes cpos++; 13051592Srgrimes return (SP); 13061592Srgrimes } 13071592Srgrimes cp = &cbuf[cpos]; 13081592Srgrimes if ((cp2 = strpbrk(cp, " \n"))) 13091592Srgrimes cpos = cp2 - cbuf; 13101592Srgrimes c = cbuf[cpos]; 13111592Srgrimes cbuf[cpos] = '\0'; 13121592Srgrimes upper(cp); 13131592Srgrimes p = lookup(sitetab, cp); 13141592Srgrimes cbuf[cpos] = c; 13153777Spst if (guest == 0 && p != 0) { 13161592Srgrimes if (p->implemented == 0) { 13171592Srgrimes state = CMD; 13181592Srgrimes nack(p->name); 131989935Syar return (LEXERR); 13201592Srgrimes } 13211592Srgrimes state = p->state; 13221592Srgrimes yylval.s = p->name; 13231592Srgrimes return (p->token); 13241592Srgrimes } 13251592Srgrimes state = CMD; 13261592Srgrimes break; 13271592Srgrimes 132875556Sgreen case ZSTR1: 13291592Srgrimes case OSTR: 13301592Srgrimes if (cbuf[cpos] == '\n') { 13311592Srgrimes state = CMD; 13321592Srgrimes return (CRLF); 13331592Srgrimes } 13341592Srgrimes /* FALLTHROUGH */ 13351592Srgrimes 13361592Srgrimes case STR1: 13371592Srgrimes dostr1: 13381592Srgrimes if (cbuf[cpos] == ' ') { 13391592Srgrimes cpos++; 134051979Salfred state = state == OSTR ? STR2 : state+1; 13411592Srgrimes return (SP); 13421592Srgrimes } 13431592Srgrimes break; 13441592Srgrimes 13451592Srgrimes case ZSTR2: 13461592Srgrimes if (cbuf[cpos] == '\n') { 13471592Srgrimes state = CMD; 13481592Srgrimes return (CRLF); 13491592Srgrimes } 13501592Srgrimes /* FALLTHROUGH */ 13511592Srgrimes 13521592Srgrimes case STR2: 13531592Srgrimes cp = &cbuf[cpos]; 13541592Srgrimes n = strlen(cp); 13551592Srgrimes cpos += n - 1; 13561592Srgrimes /* 13571592Srgrimes * Make sure the string is nonempty and \n terminated. 13581592Srgrimes */ 13591592Srgrimes if (n > 1 && cbuf[cpos] == '\n') { 13601592Srgrimes cbuf[cpos] = '\0'; 13611592Srgrimes yylval.s = copy(cp); 13621592Srgrimes cbuf[cpos] = '\n'; 13631592Srgrimes state = ARGS; 13641592Srgrimes return (STRING); 13651592Srgrimes } 13661592Srgrimes break; 13671592Srgrimes 13681592Srgrimes case NSTR: 13691592Srgrimes if (cbuf[cpos] == ' ') { 13701592Srgrimes cpos++; 13711592Srgrimes return (SP); 13721592Srgrimes } 13731592Srgrimes if (isdigit(cbuf[cpos])) { 13741592Srgrimes cp = &cbuf[cpos]; 13751592Srgrimes while (isdigit(cbuf[++cpos])) 13761592Srgrimes ; 13771592Srgrimes c = cbuf[cpos]; 13781592Srgrimes cbuf[cpos] = '\0'; 137992272Smaxim yylval.u.i = atoi(cp); 13801592Srgrimes cbuf[cpos] = c; 13811592Srgrimes state = STR1; 13821592Srgrimes return (NUMBER); 13831592Srgrimes } 13841592Srgrimes state = STR1; 13851592Srgrimes goto dostr1; 13861592Srgrimes 13871592Srgrimes case ARGS: 13881592Srgrimes if (isdigit(cbuf[cpos])) { 13891592Srgrimes cp = &cbuf[cpos]; 13901592Srgrimes while (isdigit(cbuf[++cpos])) 13911592Srgrimes ; 13921592Srgrimes c = cbuf[cpos]; 13931592Srgrimes cbuf[cpos] = '\0'; 139492272Smaxim yylval.u.i = atoi(cp); 139592272Smaxim yylval.u.o = strtoull(cp, (char **)NULL, 10); 13961592Srgrimes cbuf[cpos] = c; 13971592Srgrimes return (NUMBER); 13981592Srgrimes } 139956668Sshin if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 140056668Sshin && !isalnum(cbuf[cpos + 3])) { 140156668Sshin cpos += 3; 140256668Sshin return ALL; 140356668Sshin } 14041592Srgrimes switch (cbuf[cpos++]) { 14051592Srgrimes 14061592Srgrimes case '\n': 14071592Srgrimes state = CMD; 14081592Srgrimes return (CRLF); 14091592Srgrimes 14101592Srgrimes case ' ': 14111592Srgrimes return (SP); 14121592Srgrimes 14131592Srgrimes case ',': 14141592Srgrimes return (COMMA); 14151592Srgrimes 14161592Srgrimes case 'A': 14171592Srgrimes case 'a': 14181592Srgrimes return (A); 14191592Srgrimes 14201592Srgrimes case 'B': 14211592Srgrimes case 'b': 14221592Srgrimes return (B); 14231592Srgrimes 14241592Srgrimes case 'C': 14251592Srgrimes case 'c': 14261592Srgrimes return (C); 14271592Srgrimes 14281592Srgrimes case 'E': 14291592Srgrimes case 'e': 14301592Srgrimes return (E); 14311592Srgrimes 14321592Srgrimes case 'F': 14331592Srgrimes case 'f': 14341592Srgrimes return (F); 14351592Srgrimes 14361592Srgrimes case 'I': 14371592Srgrimes case 'i': 14381592Srgrimes return (I); 14391592Srgrimes 14401592Srgrimes case 'L': 14411592Srgrimes case 'l': 14421592Srgrimes return (L); 14431592Srgrimes 14441592Srgrimes case 'N': 14451592Srgrimes case 'n': 14461592Srgrimes return (N); 14471592Srgrimes 14481592Srgrimes case 'P': 14491592Srgrimes case 'p': 14501592Srgrimes return (P); 14511592Srgrimes 14521592Srgrimes case 'R': 14531592Srgrimes case 'r': 14541592Srgrimes return (R); 14551592Srgrimes 14561592Srgrimes case 'S': 14571592Srgrimes case 's': 14581592Srgrimes return (S); 14591592Srgrimes 14601592Srgrimes case 'T': 14611592Srgrimes case 't': 14621592Srgrimes return (T); 14631592Srgrimes 14641592Srgrimes } 14651592Srgrimes break; 14661592Srgrimes 14671592Srgrimes default: 146876096Smarkm fatalerror("Unknown state in scanner."); 14691592Srgrimes } 14701592Srgrimes state = CMD; 147189935Syar return (LEXERR); 14721592Srgrimes } 14731592Srgrimes} 14741592Srgrimes 14751592Srgrimesvoid 147690148Simpupper(char *s) 14771592Srgrimes{ 14781592Srgrimes while (*s != '\0') { 14791592Srgrimes if (islower(*s)) 14801592Srgrimes *s = toupper(*s); 14811592Srgrimes s++; 14821592Srgrimes } 14831592Srgrimes} 14841592Srgrimes 14851592Srgrimesstatic char * 148690148Simpcopy(char *s) 14871592Srgrimes{ 14881592Srgrimes char *p; 14891592Srgrimes 14901592Srgrimes p = malloc((unsigned) strlen(s) + 1); 14911592Srgrimes if (p == NULL) 149276096Smarkm fatalerror("Ran out of memory."); 14931592Srgrimes (void) strcpy(p, s); 14941592Srgrimes return (p); 14951592Srgrimes} 14961592Srgrimes 14971592Srgrimesstatic void 149890148Simphelp(struct tab *ctab, char *s) 14991592Srgrimes{ 15001592Srgrimes struct tab *c; 15011592Srgrimes int width, NCMDS; 15021592Srgrimes char *type; 15031592Srgrimes 15041592Srgrimes if (ctab == sitetab) 15051592Srgrimes type = "SITE "; 15061592Srgrimes else 15071592Srgrimes type = ""; 15081592Srgrimes width = 0, NCMDS = 0; 15091592Srgrimes for (c = ctab; c->name != NULL; c++) { 15101592Srgrimes int len = strlen(c->name); 15111592Srgrimes 15121592Srgrimes if (len > width) 15131592Srgrimes width = len; 15141592Srgrimes NCMDS++; 15151592Srgrimes } 15161592Srgrimes width = (width + 8) &~ 7; 15171592Srgrimes if (s == 0) { 15181592Srgrimes int i, j, w; 15191592Srgrimes int columns, lines; 15201592Srgrimes 15211592Srgrimes lreply(214, "The following %scommands are recognized %s.", 15221592Srgrimes type, "(* =>'s unimplemented)"); 15231592Srgrimes columns = 76 / width; 15241592Srgrimes if (columns == 0) 15251592Srgrimes columns = 1; 15261592Srgrimes lines = (NCMDS + columns - 1) / columns; 15271592Srgrimes for (i = 0; i < lines; i++) { 15281592Srgrimes printf(" "); 15291592Srgrimes for (j = 0; j < columns; j++) { 15301592Srgrimes c = ctab + j * lines + i; 15311592Srgrimes printf("%s%c", c->name, 15321592Srgrimes c->implemented ? ' ' : '*'); 15331592Srgrimes if (c + lines >= &ctab[NCMDS]) 15341592Srgrimes break; 15351592Srgrimes w = strlen(c->name) + 1; 15361592Srgrimes while (w < width) { 15371592Srgrimes putchar(' '); 15381592Srgrimes w++; 15391592Srgrimes } 15401592Srgrimes } 15411592Srgrimes printf("\r\n"); 15421592Srgrimes } 15431592Srgrimes (void) fflush(stdout); 15441592Srgrimes reply(214, "Direct comments to ftp-bugs@%s.", hostname); 15451592Srgrimes return; 15461592Srgrimes } 15471592Srgrimes upper(s); 15481592Srgrimes c = lookup(ctab, s); 15491592Srgrimes if (c == (struct tab *)0) { 15501592Srgrimes reply(502, "Unknown command %s.", s); 15511592Srgrimes return; 15521592Srgrimes } 15531592Srgrimes if (c->implemented) 15541592Srgrimes reply(214, "Syntax: %s%s %s", type, c->name, c->help); 15551592Srgrimes else 15561592Srgrimes reply(214, "%s%-*s\t%s; unimplemented.", type, width, 15571592Srgrimes c->name, c->help); 15581592Srgrimes} 15591592Srgrimes 15601592Srgrimesstatic void 156190148Simpsizecmd(char *filename) 15621592Srgrimes{ 15631592Srgrimes switch (type) { 15641592Srgrimes case TYPE_L: 15651592Srgrimes case TYPE_I: { 15661592Srgrimes struct stat stbuf; 156763350Sdes if (stat(filename, &stbuf) < 0) 156863350Sdes perror_reply(550, filename); 156963350Sdes else if (!S_ISREG(stbuf.st_mode)) 15701592Srgrimes reply(550, "%s: not a plain file.", filename); 15711592Srgrimes else 15721592Srgrimes reply(213, "%qu", stbuf.st_size); 15731592Srgrimes break; } 15741592Srgrimes case TYPE_A: { 15751592Srgrimes FILE *fin; 15761592Srgrimes int c; 15771592Srgrimes off_t count; 15781592Srgrimes struct stat stbuf; 15791592Srgrimes fin = fopen(filename, "r"); 15801592Srgrimes if (fin == NULL) { 15811592Srgrimes perror_reply(550, filename); 15821592Srgrimes return; 15831592Srgrimes } 158463350Sdes if (fstat(fileno(fin), &stbuf) < 0) { 158563350Sdes perror_reply(550, filename); 158663350Sdes (void) fclose(fin); 158763350Sdes return; 158863350Sdes } else if (!S_ISREG(stbuf.st_mode)) { 15891592Srgrimes reply(550, "%s: not a plain file.", filename); 15901592Srgrimes (void) fclose(fin); 15911592Srgrimes return; 15921592Srgrimes } 15931592Srgrimes 15941592Srgrimes count = 0; 15951592Srgrimes while((c=getc(fin)) != EOF) { 15961592Srgrimes if (c == '\n') /* will get expanded to \r\n */ 15971592Srgrimes count++; 15981592Srgrimes count++; 15991592Srgrimes } 16001592Srgrimes (void) fclose(fin); 16011592Srgrimes 16021592Srgrimes reply(213, "%qd", count); 16031592Srgrimes break; } 16041592Srgrimes default: 1605100684Syar reply(504, "SIZE not implemented for type %s.", 1606100684Syar typenames[type]); 16071592Srgrimes } 16081592Srgrimes} 160956668Sshin 161056668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 161156668Sshinstatic int 161290148Simpport_check(const char *pcmd) 161356668Sshin{ 161456668Sshin if (his_addr.su_family == AF_INET) { 161556668Sshin if (data_dest.su_family != AF_INET) { 161656668Sshin usedefault = 1; 161756668Sshin reply(500, "Invalid address rejected."); 161856668Sshin return 1; 161956668Sshin } 162056668Sshin if (paranoid && 162156668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 162256668Sshin memcmp(&data_dest.su_sin.sin_addr, 162356668Sshin &his_addr.su_sin.sin_addr, 162456668Sshin sizeof(data_dest.su_sin.sin_addr)))) { 162556668Sshin usedefault = 1; 162656668Sshin reply(500, "Illegal PORT range rejected."); 162756668Sshin } else { 162856668Sshin usedefault = 0; 162956668Sshin if (pdata >= 0) { 163056668Sshin (void) close(pdata); 163156668Sshin pdata = -1; 163256668Sshin } 163356668Sshin reply(200, "%s command successful.", pcmd); 163456668Sshin } 163556668Sshin return 1; 163656668Sshin } 163756668Sshin return 0; 163856668Sshin} 163956668Sshin 164070102Sphkstatic int 164190148Simpcheck_login1(void) 164270102Sphk{ 164370102Sphk if (logged_in) 164470102Sphk return 1; 164570102Sphk else { 164670102Sphk reply(530, "Please login with USER and PASS."); 164770102Sphk return 0; 164870102Sphk } 164970102Sphk} 165070102Sphk 165156668Sshin#ifdef INET6 165256668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 165356668Sshinstatic int 165490148Simpport_check_v6(const char *pcmd) 165556668Sshin{ 165656668Sshin if (his_addr.su_family == AF_INET6) { 165756668Sshin if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 165856668Sshin /* Convert data_dest into v4 mapped sockaddr.*/ 165956668Sshin v4map_data_dest(); 166056668Sshin if (data_dest.su_family != AF_INET6) { 166156668Sshin usedefault = 1; 166256668Sshin reply(500, "Invalid address rejected."); 166356668Sshin return 1; 166456668Sshin } 166556668Sshin if (paranoid && 166656668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 166756668Sshin memcmp(&data_dest.su_sin6.sin6_addr, 166856668Sshin &his_addr.su_sin6.sin6_addr, 166956668Sshin sizeof(data_dest.su_sin6.sin6_addr)))) { 167056668Sshin usedefault = 1; 167156668Sshin reply(500, "Illegal PORT range rejected."); 167256668Sshin } else { 167356668Sshin usedefault = 0; 167456668Sshin if (pdata >= 0) { 167556668Sshin (void) close(pdata); 167656668Sshin pdata = -1; 167756668Sshin } 167856668Sshin reply(200, "%s command successful.", pcmd); 167956668Sshin } 168056668Sshin return 1; 168156668Sshin } 168256668Sshin return 0; 168356668Sshin} 168456668Sshin 168556668Sshinstatic void 168690148Simpv4map_data_dest(void) 168756668Sshin{ 168856668Sshin struct in_addr savedaddr; 168956668Sshin int savedport; 169056668Sshin 169156668Sshin if (data_dest.su_family != AF_INET) { 169256668Sshin usedefault = 1; 169356668Sshin reply(500, "Invalid address rejected."); 169456668Sshin return; 169556668Sshin } 169656668Sshin 169756668Sshin savedaddr = data_dest.su_sin.sin_addr; 169856668Sshin savedport = data_dest.su_port; 169956668Sshin 170056668Sshin memset(&data_dest, 0, sizeof(data_dest)); 170156668Sshin data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 170256668Sshin data_dest.su_sin6.sin6_family = AF_INET6; 170356668Sshin data_dest.su_sin6.sin6_port = savedport; 170456668Sshin memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 170556668Sshin memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 170656668Sshin (caddr_t)&savedaddr, sizeof(savedaddr)); 170756668Sshin} 170856668Sshin#endif 1709