ftpcmd.y revision 110378
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 110378 2003-02-05 11:11:32Z 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" 75109380Syar#include "pathnames.h" 761592Srgrimes 7756668Sshinextern union sockunion data_dest, his_addr; 78110037Syarextern int hostinfo; 791592Srgrimesextern int logged_in; 801592Srgrimesextern struct passwd *pw; 811592Srgrimesextern int guest; 82110036Syarextern char *homedir; 8317435Spstextern int paranoid; 841592Srgrimesextern int logging; 851592Srgrimesextern int type; 861592Srgrimesextern int form; 8776096Smarkmextern int ftpdebug; 881592Srgrimesextern int timeout; 891592Srgrimesextern int maxtimeout; 901592Srgrimesextern int pdata; 9127650Sdavidnextern char *hostname; 9227650Sdavidnextern char remotehost[]; 931592Srgrimesextern char proctitle[]; 941592Srgrimesextern int usedefault; 951592Srgrimesextern int transflag; 961592Srgrimesextern char tmpline[]; 9770102Sphkextern int readonly; 9870102Sphkextern int noepsv; 9982460Snikextern int noretr; 10082796Ssheldonhextern int noguestretr; 101100684Syarextern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */ 1021592Srgrimes 1031592Srgrimesoff_t restart_point; 1041592Srgrimes 1051592Srgrimesstatic int cmd_type; 1061592Srgrimesstatic int cmd_form; 1071592Srgrimesstatic int cmd_bytesz; 10889935Syarstatic int state; 1091592Srgrimeschar cbuf[512]; 11088935Sdwmalonechar *fromname = (char *) 0; 1111592Srgrimes 11256668Sshinextern int epsvall; 11356668Sshin 1141592Srgrimes%} 1151592Srgrimes 1161592Srgrimes%union { 11792272Smaxim struct { 11892272Smaxim off_t o; 11992272Smaxim int i; 12092272Smaxim } u; 1211592Srgrimes char *s; 1221592Srgrimes} 1231592Srgrimes 1241592Srgrimes%token 1251592Srgrimes A B C E F I 1261592Srgrimes L N P R S T 12756668Sshin ALL 1281592Srgrimes 1291592Srgrimes SP CRLF COMMA 1301592Srgrimes 1311592Srgrimes USER PASS ACCT REIN QUIT PORT 1321592Srgrimes PASV TYPE STRU MODE RETR STOR 1331592Srgrimes APPE MLFL MAIL MSND MSOM MSAM 1341592Srgrimes MRSQ MRCP ALLO REST RNFR RNTO 1351592Srgrimes ABOR DELE CWD LIST NLST SITE 1361592Srgrimes STAT HELP NOOP MKD RMD PWD 1371592Srgrimes CDUP STOU SMNT SYST SIZE MDTM 13856668Sshin LPRT LPSV EPRT EPSV 1391592Srgrimes 14075535Sphk UMASK IDLE CHMOD MDFIVE 1411592Srgrimes 142102565Syar LEXERR NOTIMPL 1431592Srgrimes 1441592Srgrimes%token <s> STRING 14592272Smaxim%token <u> NUMBER 1461592Srgrimes 14792272Smaxim%type <u.i> check_login octal_number byte_size 14892272Smaxim%type <u.i> check_login_ro check_login_epsv 14992272Smaxim%type <u.i> struct_code mode_code type_code form_code 15075567Speter%type <s> pathstring pathname password username 151102565Syar%type <s> ALL NOTIMPL 1521592Srgrimes 1531592Srgrimes%start cmd_list 1541592Srgrimes 1551592Srgrimes%% 1561592Srgrimes 1571592Srgrimescmd_list 1581592Srgrimes : /* empty */ 1591592Srgrimes | cmd_list cmd 1601592Srgrimes { 16188935Sdwmalone if (fromname) 16288935Sdwmalone free(fromname); 1631592Srgrimes fromname = (char *) 0; 1641592Srgrimes restart_point = (off_t) 0; 1651592Srgrimes } 1661592Srgrimes | cmd_list rcmd 1671592Srgrimes ; 1681592Srgrimes 1691592Srgrimescmd 1701592Srgrimes : USER SP username CRLF 1711592Srgrimes { 1721592Srgrimes user($3); 1731592Srgrimes free($3); 1741592Srgrimes } 1751592Srgrimes | PASS SP password CRLF 1761592Srgrimes { 1771592Srgrimes pass($3); 1781592Srgrimes free($3); 1791592Srgrimes } 18075556Sgreen | PASS CRLF 18175556Sgreen { 18275556Sgreen pass(""); 18375556Sgreen } 18417433Spst | PORT check_login SP host_port CRLF 1851592Srgrimes { 18656668Sshin if (epsvall) { 18756668Sshin reply(501, "no PORT allowed after EPSV ALL"); 18856668Sshin goto port_done; 18956668Sshin } 19056668Sshin if (!$2) 19156668Sshin goto port_done; 19256668Sshin if (port_check("PORT") == 1) 19356668Sshin goto port_done; 19456668Sshin#ifdef INET6 19556668Sshin if ((his_addr.su_family != AF_INET6 || 19656668Sshin !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { 19756668Sshin /* shoud never happen */ 19856668Sshin usedefault = 1; 19956668Sshin reply(500, "Invalid address rejected."); 20056668Sshin goto port_done; 20156668Sshin } 20256668Sshin port_check_v6("pcmd"); 20356668Sshin#endif 20456668Sshin port_done: 20556668Sshin } 20656668Sshin | LPRT check_login SP host_long_port CRLF 20756668Sshin { 20856668Sshin if (epsvall) { 20956668Sshin reply(501, "no LPRT allowed after EPSV ALL"); 21056668Sshin goto lprt_done; 21156668Sshin } 21256668Sshin if (!$2) 21356668Sshin goto lprt_done; 21456668Sshin if (port_check("LPRT") == 1) 21556668Sshin goto lprt_done; 21656668Sshin#ifdef INET6 21756668Sshin if (his_addr.su_family != AF_INET6) { 21856668Sshin usedefault = 1; 21956668Sshin reply(500, "Invalid address rejected."); 22056668Sshin goto lprt_done; 22156668Sshin } 22256668Sshin if (port_check_v6("LPRT") == 1) 22356668Sshin goto lprt_done; 22456668Sshin#endif 22556668Sshin lprt_done: 22656668Sshin } 22756668Sshin | EPRT check_login SP STRING CRLF 22856668Sshin { 22956668Sshin char delim; 23056668Sshin char *tmp = NULL; 23156668Sshin char *p, *q; 23256668Sshin char *result[3]; 23356668Sshin struct addrinfo hints; 23456668Sshin struct addrinfo *res; 23556668Sshin int i; 23656668Sshin 23756668Sshin if (epsvall) { 23856668Sshin reply(501, "no EPRT allowed after EPSV ALL"); 23956668Sshin goto eprt_done; 24056668Sshin } 24156668Sshin if (!$2) 24256668Sshin goto eprt_done; 24356668Sshin 24456668Sshin memset(&data_dest, 0, sizeof(data_dest)); 24556668Sshin tmp = strdup($4); 24676096Smarkm if (ftpdebug) 24756668Sshin syslog(LOG_DEBUG, "%s", tmp); 24856668Sshin if (!tmp) { 24976096Smarkm fatalerror("not enough core"); 25056668Sshin /*NOTREACHED*/ 25156668Sshin } 25256668Sshin p = tmp; 25356668Sshin delim = p[0]; 25456668Sshin p++; 25556668Sshin memset(result, 0, sizeof(result)); 25656668Sshin for (i = 0; i < 3; i++) { 25756668Sshin q = strchr(p, delim); 25856668Sshin if (!q || *q != delim) { 25956668Sshin parsefail: 26056668Sshin reply(500, 26156668Sshin "Invalid argument, rejected."); 26256668Sshin if (tmp) 26356668Sshin free(tmp); 26417433Spst usedefault = 1; 26556668Sshin goto eprt_done; 26617433Spst } 26756668Sshin *q++ = '\0'; 26856668Sshin result[i] = p; 26976096Smarkm if (ftpdebug) 27056668Sshin syslog(LOG_DEBUG, "%d: %s", i, p); 27156668Sshin p = q; 2721592Srgrimes } 27356668Sshin 27456668Sshin /* some more sanity check */ 27556668Sshin p = result[0]; 27656668Sshin while (*p) { 27756668Sshin if (!isdigit(*p)) 27856668Sshin goto parsefail; 27956668Sshin p++; 28056668Sshin } 28156668Sshin p = result[2]; 28256668Sshin while (*p) { 28356668Sshin if (!isdigit(*p)) 28456668Sshin goto parsefail; 28556668Sshin p++; 28656668Sshin } 28756668Sshin 28856668Sshin /* grab address */ 28956668Sshin memset(&hints, 0, sizeof(hints)); 29056668Sshin if (atoi(result[0]) == 1) 29156668Sshin hints.ai_family = PF_INET; 29256668Sshin#ifdef INET6 29356668Sshin else if (atoi(result[0]) == 2) 29456668Sshin hints.ai_family = PF_INET6; 29556668Sshin#endif 29656668Sshin else 29756668Sshin hints.ai_family = PF_UNSPEC; /*XXX*/ 29856668Sshin hints.ai_socktype = SOCK_STREAM; 29956668Sshin i = getaddrinfo(result[1], result[2], &hints, &res); 30056668Sshin if (i) 30156668Sshin goto parsefail; 30256668Sshin memcpy(&data_dest, res->ai_addr, res->ai_addrlen); 30356668Sshin#ifdef INET6 30456668Sshin if (his_addr.su_family == AF_INET6 30556668Sshin && data_dest.su_family == AF_INET6) { 30656668Sshin /* XXX more sanity checks! */ 30756668Sshin data_dest.su_sin6.sin6_scope_id = 30856668Sshin his_addr.su_sin6.sin6_scope_id; 30956668Sshin } 31056668Sshin#endif 31156668Sshin free(tmp); 31256668Sshin tmp = NULL; 31356668Sshin 31456668Sshin if (port_check("EPRT") == 1) 31556668Sshin goto eprt_done; 31656668Sshin#ifdef INET6 31756668Sshin if (his_addr.su_family != AF_INET6) { 31856668Sshin usedefault = 1; 31956668Sshin reply(500, "Invalid address rejected."); 32056668Sshin goto eprt_done; 32156668Sshin } 32256668Sshin if (port_check_v6("EPRT") == 1) 32356668Sshin goto eprt_done; 32456668Sshin#endif 32588935Sdwmalone eprt_done: 32688935Sdwmalone free($4); 3271592Srgrimes } 32817433Spst | PASV check_login CRLF 3291592Srgrimes { 33056668Sshin if (epsvall) 33156668Sshin reply(501, "no PASV allowed after EPSV ALL"); 33256668Sshin else if ($2) 33317433Spst passive(); 3341592Srgrimes } 33556668Sshin | LPSV check_login CRLF 33656668Sshin { 33756668Sshin if (epsvall) 33856668Sshin reply(501, "no LPSV allowed after EPSV ALL"); 33956668Sshin else if ($2) 34056668Sshin long_passive("LPSV", PF_UNSPEC); 34156668Sshin } 34270102Sphk | EPSV check_login_epsv SP NUMBER CRLF 34356668Sshin { 34456668Sshin if ($2) { 34556668Sshin int pf; 34692272Smaxim switch ($4.i) { 34756668Sshin case 1: 34856668Sshin pf = PF_INET; 34956668Sshin break; 35056668Sshin#ifdef INET6 35156668Sshin case 2: 35256668Sshin pf = PF_INET6; 35356668Sshin break; 35456668Sshin#endif 35556668Sshin default: 35656668Sshin pf = -1; /*junk value*/ 35756668Sshin break; 35856668Sshin } 35956668Sshin long_passive("EPSV", pf); 36056668Sshin } 36156668Sshin } 36270102Sphk | EPSV check_login_epsv SP ALL CRLF 36356668Sshin { 36456668Sshin if ($2) { 36556668Sshin reply(200, 36656668Sshin "EPSV ALL command successful."); 36756668Sshin epsvall++; 36856668Sshin } 36956668Sshin } 37070102Sphk | EPSV check_login_epsv CRLF 37156668Sshin { 37256668Sshin if ($2) 37356668Sshin long_passive("EPSV", PF_UNSPEC); 37456668Sshin } 37571278Sjedgar | TYPE check_login SP type_code CRLF 3761592Srgrimes { 37771278Sjedgar if ($2) { 37871278Sjedgar switch (cmd_type) { 3791592Srgrimes 38071278Sjedgar case TYPE_A: 38171278Sjedgar if (cmd_form == FORM_N) { 38271278Sjedgar reply(200, "Type set to A."); 38371278Sjedgar type = cmd_type; 38471278Sjedgar form = cmd_form; 38571278Sjedgar } else 38671278Sjedgar reply(504, "Form must be N."); 38771278Sjedgar break; 3881592Srgrimes 38971278Sjedgar case TYPE_E: 39071278Sjedgar reply(504, "Type E not implemented."); 39171278Sjedgar break; 3921592Srgrimes 39371278Sjedgar case TYPE_I: 39471278Sjedgar reply(200, "Type set to I."); 39571278Sjedgar type = cmd_type; 39671278Sjedgar break; 3971592Srgrimes 39871278Sjedgar case TYPE_L: 399103949Smike#if CHAR_BIT == 8 40071278Sjedgar if (cmd_bytesz == 8) { 40171278Sjedgar reply(200, 40271278Sjedgar "Type set to L (byte size 8)."); 40371278Sjedgar type = cmd_type; 40471278Sjedgar } else 40571278Sjedgar reply(504, "Byte size must be 8."); 406103949Smike#else /* CHAR_BIT == 8 */ 407103949Smike UNIMPLEMENTED for CHAR_BIT != 8 408103949Smike#endif /* CHAR_BIT == 8 */ 40971278Sjedgar } 4101592Srgrimes } 4111592Srgrimes } 41271278Sjedgar | STRU check_login SP struct_code CRLF 4131592Srgrimes { 41471278Sjedgar if ($2) { 41571278Sjedgar switch ($4) { 4161592Srgrimes 41771278Sjedgar case STRU_F: 41871278Sjedgar reply(200, "STRU F ok."); 41971278Sjedgar break; 4201592Srgrimes 42171278Sjedgar default: 42271278Sjedgar reply(504, "Unimplemented STRU type."); 42371278Sjedgar } 4241592Srgrimes } 4251592Srgrimes } 42671278Sjedgar | MODE check_login SP mode_code CRLF 4271592Srgrimes { 42871278Sjedgar if ($2) { 42971278Sjedgar switch ($4) { 4301592Srgrimes 43171278Sjedgar case MODE_S: 43271278Sjedgar reply(200, "MODE S ok."); 43371278Sjedgar break; 43471278Sjedgar 43571278Sjedgar default: 43671278Sjedgar reply(502, "Unimplemented MODE type."); 43771278Sjedgar } 4381592Srgrimes } 4391592Srgrimes } 44071278Sjedgar | ALLO check_login SP NUMBER CRLF 4411592Srgrimes { 44271278Sjedgar if ($2) { 44371278Sjedgar reply(202, "ALLO command ignored."); 44471278Sjedgar } 4451592Srgrimes } 44671278Sjedgar | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 4471592Srgrimes { 44871278Sjedgar if ($2) { 44971278Sjedgar reply(202, "ALLO command ignored."); 45071278Sjedgar } 4511592Srgrimes } 4521592Srgrimes | RETR check_login SP pathname CRLF 4531592Srgrimes { 45482796Ssheldonh if (noretr || (guest && noguestretr)) 45582460Snik reply(500, "RETR command is disabled"); 45682460Snik else if ($2 && $4 != NULL) 4571592Srgrimes retrieve((char *) 0, $4); 45882460Snik 4591592Srgrimes if ($4 != NULL) 4601592Srgrimes free($4); 4611592Srgrimes } 46270102Sphk | STOR check_login_ro SP pathname CRLF 4631592Srgrimes { 4641592Srgrimes if ($2 && $4 != NULL) 4651592Srgrimes store($4, "w", 0); 4661592Srgrimes if ($4 != NULL) 4671592Srgrimes free($4); 4681592Srgrimes } 46970102Sphk | APPE check_login_ro SP pathname CRLF 4701592Srgrimes { 4711592Srgrimes if ($2 && $4 != NULL) 4721592Srgrimes store($4, "a", 0); 4731592Srgrimes if ($4 != NULL) 4741592Srgrimes free($4); 4751592Srgrimes } 4761592Srgrimes | NLST check_login CRLF 4771592Srgrimes { 4781592Srgrimes if ($2) 4791592Srgrimes send_file_list("."); 4801592Srgrimes } 481101395Syar | NLST check_login SP pathstring CRLF 4821592Srgrimes { 483101395Syar if ($2) 4841592Srgrimes send_file_list($4); 485101395Syar free($4); 4861592Srgrimes } 4871592Srgrimes | LIST check_login CRLF 4881592Srgrimes { 4891592Srgrimes if ($2) 490109380Syar retrieve(_PATH_LS " -lgA", ""); 4911592Srgrimes } 49275567Speter | LIST check_login SP pathstring CRLF 4931592Srgrimes { 494101395Syar if ($2) 495109380Syar retrieve(_PATH_LS " -lgA %s", $4); 496101395Syar free($4); 4971592Srgrimes } 4981592Srgrimes | STAT check_login SP pathname CRLF 4991592Srgrimes { 5001592Srgrimes if ($2 && $4 != NULL) 5011592Srgrimes statfilecmd($4); 5021592Srgrimes if ($4 != NULL) 5031592Srgrimes free($4); 5041592Srgrimes } 50571278Sjedgar | STAT check_login CRLF 5061592Srgrimes { 50771278Sjedgar if ($2) { 50871278Sjedgar statcmd(); 50971278Sjedgar } 5101592Srgrimes } 51170102Sphk | DELE check_login_ro SP pathname CRLF 5121592Srgrimes { 5131592Srgrimes if ($2 && $4 != NULL) 5141592Srgrimes delete($4); 5151592Srgrimes if ($4 != NULL) 5161592Srgrimes free($4); 5171592Srgrimes } 51870102Sphk | RNTO check_login_ro SP pathname CRLF 5191592Srgrimes { 520101379Syar if ($2 && $4 != NULL) { 52117433Spst if (fromname) { 52217433Spst renamecmd(fromname, $4); 52317433Spst free(fromname); 52417433Spst fromname = (char *) 0; 52517433Spst } else { 52617433Spst reply(503, "Bad sequence of commands."); 52717433Spst } 5281592Srgrimes } 529101379Syar if ($4 != NULL) 530101379Syar free($4); 5311592Srgrimes } 53271278Sjedgar | ABOR check_login CRLF 5331592Srgrimes { 53471278Sjedgar if ($2) 53571278Sjedgar reply(225, "ABOR command successful."); 5361592Srgrimes } 5371592Srgrimes | CWD check_login CRLF 5381592Srgrimes { 53969234Sdanny if ($2) { 540110036Syar cwd(homedir); 54169234Sdanny } 5421592Srgrimes } 5431592Srgrimes | CWD check_login SP pathname CRLF 5441592Srgrimes { 5451592Srgrimes if ($2 && $4 != NULL) 5461592Srgrimes cwd($4); 5471592Srgrimes if ($4 != NULL) 5481592Srgrimes free($4); 5491592Srgrimes } 5501592Srgrimes | HELP CRLF 5511592Srgrimes { 5521592Srgrimes help(cmdtab, (char *) 0); 5531592Srgrimes } 5541592Srgrimes | HELP SP STRING CRLF 5551592Srgrimes { 5561592Srgrimes char *cp = $3; 5571592Srgrimes 5581592Srgrimes if (strncasecmp(cp, "SITE", 4) == 0) { 5591592Srgrimes cp = $3 + 4; 5601592Srgrimes if (*cp == ' ') 5611592Srgrimes cp++; 5621592Srgrimes if (*cp) 5631592Srgrimes help(sitetab, cp); 5641592Srgrimes else 5651592Srgrimes help(sitetab, (char *) 0); 5661592Srgrimes } else 5671592Srgrimes help(cmdtab, $3); 56888935Sdwmalone free($3); 5691592Srgrimes } 5701592Srgrimes | NOOP CRLF 5711592Srgrimes { 5721592Srgrimes reply(200, "NOOP command successful."); 5731592Srgrimes } 57470102Sphk | MKD check_login_ro SP pathname CRLF 5751592Srgrimes { 5761592Srgrimes if ($2 && $4 != NULL) 5771592Srgrimes makedir($4); 5781592Srgrimes if ($4 != NULL) 5791592Srgrimes free($4); 5801592Srgrimes } 58170102Sphk | RMD check_login_ro SP pathname CRLF 5821592Srgrimes { 5831592Srgrimes if ($2 && $4 != NULL) 5841592Srgrimes removedir($4); 5851592Srgrimes if ($4 != NULL) 5861592Srgrimes free($4); 5871592Srgrimes } 5881592Srgrimes | PWD check_login CRLF 5891592Srgrimes { 5901592Srgrimes if ($2) 5911592Srgrimes pwd(); 5921592Srgrimes } 5931592Srgrimes | CDUP check_login CRLF 5941592Srgrimes { 5951592Srgrimes if ($2) 5961592Srgrimes cwd(".."); 5971592Srgrimes } 5981592Srgrimes | SITE SP HELP CRLF 5991592Srgrimes { 6001592Srgrimes help(sitetab, (char *) 0); 6011592Srgrimes } 6021592Srgrimes | SITE SP HELP SP STRING CRLF 6031592Srgrimes { 6041592Srgrimes help(sitetab, $5); 60588935Sdwmalone free($5); 6061592Srgrimes } 60775535Sphk | SITE SP MDFIVE check_login SP pathname CRLF 60875535Sphk { 60975535Sphk char p[64], *q; 61075535Sphk 611101379Syar if ($4 && $6) { 61275535Sphk q = MD5File($6, p); 61375535Sphk if (q != NULL) 61475535Sphk reply(200, "MD5(%s) = %s", $6, p); 61575535Sphk else 61675535Sphk perror_reply(550, $6); 61775535Sphk } 61888935Sdwmalone if ($6) 61988935Sdwmalone free($6); 62075535Sphk } 6211592Srgrimes | SITE SP UMASK check_login CRLF 6221592Srgrimes { 6231592Srgrimes int oldmask; 6241592Srgrimes 6251592Srgrimes if ($4) { 6261592Srgrimes oldmask = umask(0); 6271592Srgrimes (void) umask(oldmask); 6281592Srgrimes reply(200, "Current UMASK is %03o", oldmask); 6291592Srgrimes } 6301592Srgrimes } 6311592Srgrimes | SITE SP UMASK check_login SP octal_number CRLF 6321592Srgrimes { 6331592Srgrimes int oldmask; 6341592Srgrimes 6351592Srgrimes if ($4) { 6361592Srgrimes if (($6 == -1) || ($6 > 0777)) { 6371592Srgrimes reply(501, "Bad UMASK value"); 6381592Srgrimes } else { 6391592Srgrimes oldmask = umask($6); 6401592Srgrimes reply(200, 6411592Srgrimes "UMASK set to %03o (was %03o)", 6421592Srgrimes $6, oldmask); 6431592Srgrimes } 6441592Srgrimes } 6451592Srgrimes } 64670102Sphk | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF 6471592Srgrimes { 6481592Srgrimes if ($4 && ($8 != NULL)) { 649101378Syar if (($6 == -1 ) || ($6 > 0777)) 650101378Syar reply(501, "Bad mode value"); 6511592Srgrimes else if (chmod($8, $6) < 0) 6521592Srgrimes perror_reply(550, $8); 6531592Srgrimes else 6541592Srgrimes reply(200, "CHMOD command successful."); 6551592Srgrimes } 6561592Srgrimes if ($8 != NULL) 6571592Srgrimes free($8); 6581592Srgrimes } 65971278Sjedgar | SITE SP check_login IDLE CRLF 6601592Srgrimes { 66171278Sjedgar if ($3) 66271278Sjedgar reply(200, 66371278Sjedgar "Current IDLE time limit is %d seconds; max %d", 66471278Sjedgar timeout, maxtimeout); 6651592Srgrimes } 66671278Sjedgar | SITE SP check_login IDLE SP NUMBER CRLF 6671592Srgrimes { 66871278Sjedgar if ($3) { 66992272Smaxim if ($6.i < 30 || $6.i > maxtimeout) { 67071278Sjedgar reply(501, 67171278Sjedgar "Maximum IDLE time must be between 30 and %d seconds", 67271278Sjedgar maxtimeout); 67371278Sjedgar } else { 67492272Smaxim timeout = $6.i; 67571278Sjedgar (void) alarm((unsigned) timeout); 67671278Sjedgar reply(200, 67771278Sjedgar "Maximum IDLE time set to %d seconds", 67871278Sjedgar timeout); 67971278Sjedgar } 6801592Srgrimes } 6811592Srgrimes } 68270102Sphk | STOU check_login_ro SP pathname CRLF 6831592Srgrimes { 6841592Srgrimes if ($2 && $4 != NULL) 6851592Srgrimes store($4, "w", 1); 6861592Srgrimes if ($4 != NULL) 6871592Srgrimes free($4); 6881592Srgrimes } 68971278Sjedgar | SYST check_login CRLF 6901592Srgrimes { 69171278Sjedgar if ($2) 6921592Srgrimes#ifdef unix 6931592Srgrimes#ifdef BSD 6941592Srgrimes reply(215, "UNIX Type: L%d Version: BSD-%d", 695103949Smike CHAR_BIT, BSD); 6961592Srgrimes#else /* BSD */ 697103949Smike reply(215, "UNIX Type: L%d", CHAR_BIT); 6981592Srgrimes#endif /* BSD */ 6991592Srgrimes#else /* unix */ 700103949Smike reply(215, "UNKNOWN Type: L%d", CHAR_BIT); 7011592Srgrimes#endif /* unix */ 7021592Srgrimes } 7031592Srgrimes 7041592Srgrimes /* 7051592Srgrimes * SIZE is not in RFC959, but Postel has blessed it and 7061592Srgrimes * it will be in the updated RFC. 7071592Srgrimes * 7081592Srgrimes * Return size of file in a format suitable for 7091592Srgrimes * using with RESTART (we just count bytes). 7101592Srgrimes */ 7111592Srgrimes | SIZE check_login SP pathname CRLF 7121592Srgrimes { 7131592Srgrimes if ($2 && $4 != NULL) 7141592Srgrimes sizecmd($4); 7151592Srgrimes if ($4 != NULL) 7161592Srgrimes free($4); 7171592Srgrimes } 7181592Srgrimes 7191592Srgrimes /* 7201592Srgrimes * MDTM is not in RFC959, but Postel has blessed it and 7211592Srgrimes * it will be in the updated RFC. 7221592Srgrimes * 7231592Srgrimes * Return modification time of file as an ISO 3307 7241592Srgrimes * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 7251592Srgrimes * where xxx is the fractional second (of any precision, 7261592Srgrimes * not necessarily 3 digits) 7271592Srgrimes */ 7281592Srgrimes | MDTM check_login SP pathname CRLF 7291592Srgrimes { 7301592Srgrimes if ($2 && $4 != NULL) { 7311592Srgrimes struct stat stbuf; 7321592Srgrimes if (stat($4, &stbuf) < 0) 7331592Srgrimes reply(550, "%s: %s", 7341592Srgrimes $4, strerror(errno)); 7351592Srgrimes else if (!S_ISREG(stbuf.st_mode)) { 7361592Srgrimes reply(550, "%s: not a plain file.", $4); 7371592Srgrimes } else { 7381592Srgrimes struct tm *t; 7391592Srgrimes t = gmtime(&stbuf.st_mtime); 7401592Srgrimes reply(213, 74117435Spst "%04d%02d%02d%02d%02d%02d", 74217435Spst 1900 + t->tm_year, 74317435Spst t->tm_mon+1, t->tm_mday, 7441592Srgrimes t->tm_hour, t->tm_min, t->tm_sec); 7451592Srgrimes } 7461592Srgrimes } 7471592Srgrimes if ($4 != NULL) 7481592Srgrimes free($4); 7491592Srgrimes } 7501592Srgrimes | QUIT CRLF 7511592Srgrimes { 7521592Srgrimes reply(221, "Goodbye."); 7531592Srgrimes dologout(0); 7541592Srgrimes } 755102565Syar | NOTIMPL 756102565Syar { 757102565Syar nack($1); 758102565Syar } 75989935Syar | error 7601592Srgrimes { 76189935Syar yyclearin; /* discard lookahead data */ 76289935Syar yyerrok; /* clear error condition */ 763102565Syar state = CMD; /* reset lexer state */ 7641592Srgrimes } 7651592Srgrimes ; 7661592Srgrimesrcmd 76770102Sphk : RNFR check_login_ro SP pathname CRLF 7681592Srgrimes { 7691592Srgrimes restart_point = (off_t) 0; 7701592Srgrimes if ($2 && $4) { 77188935Sdwmalone if (fromname) 77288935Sdwmalone free(fromname); 77388935Sdwmalone fromname = (char *) 0; 77488935Sdwmalone if (renamefrom($4)) 77588935Sdwmalone fromname = $4; 77688935Sdwmalone else 7771592Srgrimes free($4); 77888935Sdwmalone } else if ($4) { 77988935Sdwmalone free($4); 7801592Srgrimes } 7811592Srgrimes } 78292272Smaxim | REST check_login SP NUMBER CRLF 7831592Srgrimes { 78471278Sjedgar if ($2) { 78588935Sdwmalone if (fromname) 78688935Sdwmalone free(fromname); 78771278Sjedgar fromname = (char *) 0; 78892272Smaxim restart_point = $4.o; 78992272Smaxim reply(350, "Restarting at %llu. %s", 79071278Sjedgar restart_point, 79171278Sjedgar "Send STORE or RETRIEVE to initiate transfer."); 79271278Sjedgar } 7931592Srgrimes } 7941592Srgrimes ; 7951592Srgrimes 7961592Srgrimesusername 7971592Srgrimes : STRING 7981592Srgrimes ; 7991592Srgrimes 8001592Srgrimespassword 8011592Srgrimes : /* empty */ 8021592Srgrimes { 8031592Srgrimes $$ = (char *)calloc(1, sizeof(char)); 8041592Srgrimes } 8051592Srgrimes | STRING 8061592Srgrimes ; 8071592Srgrimes 8081592Srgrimesbyte_size 8091592Srgrimes : NUMBER 81092272Smaxim { 81192272Smaxim $$ = $1.i; 81292272Smaxim } 8131592Srgrimes ; 8141592Srgrimes 8151592Srgrimeshost_port 8161592Srgrimes : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 8171592Srgrimes NUMBER COMMA NUMBER 8181592Srgrimes { 8191592Srgrimes char *a, *p; 8201592Srgrimes 82156668Sshin data_dest.su_len = sizeof(struct sockaddr_in); 82256668Sshin data_dest.su_family = AF_INET; 82356668Sshin p = (char *)&data_dest.su_sin.sin_port; 82492272Smaxim p[0] = $9.i; p[1] = $11.i; 82556668Sshin a = (char *)&data_dest.su_sin.sin_addr; 82692272Smaxim a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; 8271592Srgrimes } 8281592Srgrimes ; 8291592Srgrimes 83056668Sshinhost_long_port 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 COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83556668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83656668Sshin NUMBER 83756668Sshin { 83856668Sshin char *a, *p; 83956668Sshin 84056668Sshin memset(&data_dest, 0, sizeof(data_dest)); 84156668Sshin data_dest.su_len = sizeof(struct sockaddr_in6); 84256668Sshin data_dest.su_family = AF_INET6; 84356668Sshin p = (char *)&data_dest.su_port; 84492272Smaxim p[0] = $39.i; p[1] = $41.i; 84556668Sshin a = (char *)&data_dest.su_sin6.sin6_addr; 84692272Smaxim a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 84792272Smaxim a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i; 84892272Smaxim a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i; 84992272Smaxim a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i; 85056668Sshin if (his_addr.su_family == AF_INET6) { 85156668Sshin /* XXX more sanity checks! */ 85256668Sshin data_dest.su_sin6.sin6_scope_id = 85356668Sshin his_addr.su_sin6.sin6_scope_id; 85456668Sshin } 85592272Smaxim if ($1.i != 6 || $3.i != 16 || $37.i != 2) 85656668Sshin memset(&data_dest, 0, sizeof(data_dest)); 85756668Sshin } 85856668Sshin | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 85956668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 86056668Sshin NUMBER 86156668Sshin { 86256668Sshin char *a, *p; 86356668Sshin 86456668Sshin memset(&data_dest, 0, sizeof(data_dest)); 86556668Sshin data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 86656668Sshin data_dest.su_family = AF_INET; 86756668Sshin p = (char *)&data_dest.su_port; 86892272Smaxim p[0] = $15.i; p[1] = $17.i; 86956668Sshin a = (char *)&data_dest.su_sin.sin_addr; 87092272Smaxim a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 87192272Smaxim if ($1.i != 4 || $3.i != 4 || $13.i != 2) 87256668Sshin memset(&data_dest, 0, sizeof(data_dest)); 87356668Sshin } 87456668Sshin ; 87556668Sshin 8761592Srgrimesform_code 8771592Srgrimes : N 8781592Srgrimes { 8791592Srgrimes $$ = FORM_N; 8801592Srgrimes } 8811592Srgrimes | T 8821592Srgrimes { 8831592Srgrimes $$ = FORM_T; 8841592Srgrimes } 8851592Srgrimes | C 8861592Srgrimes { 8871592Srgrimes $$ = FORM_C; 8881592Srgrimes } 8891592Srgrimes ; 8901592Srgrimes 8911592Srgrimestype_code 8921592Srgrimes : A 8931592Srgrimes { 8941592Srgrimes cmd_type = TYPE_A; 8951592Srgrimes cmd_form = FORM_N; 8961592Srgrimes } 8971592Srgrimes | A SP form_code 8981592Srgrimes { 8991592Srgrimes cmd_type = TYPE_A; 9001592Srgrimes cmd_form = $3; 9011592Srgrimes } 9021592Srgrimes | E 9031592Srgrimes { 9041592Srgrimes cmd_type = TYPE_E; 9051592Srgrimes cmd_form = FORM_N; 9061592Srgrimes } 9071592Srgrimes | E SP form_code 9081592Srgrimes { 9091592Srgrimes cmd_type = TYPE_E; 9101592Srgrimes cmd_form = $3; 9111592Srgrimes } 9121592Srgrimes | I 9131592Srgrimes { 9141592Srgrimes cmd_type = TYPE_I; 9151592Srgrimes } 9161592Srgrimes | L 9171592Srgrimes { 9181592Srgrimes cmd_type = TYPE_L; 919103949Smike cmd_bytesz = CHAR_BIT; 9201592Srgrimes } 9211592Srgrimes | L SP byte_size 9221592Srgrimes { 9231592Srgrimes cmd_type = TYPE_L; 9241592Srgrimes cmd_bytesz = $3; 9251592Srgrimes } 9261592Srgrimes /* this is for a bug in the BBN ftp */ 9271592Srgrimes | L byte_size 9281592Srgrimes { 9291592Srgrimes cmd_type = TYPE_L; 9301592Srgrimes cmd_bytesz = $2; 9311592Srgrimes } 9321592Srgrimes ; 9331592Srgrimes 9341592Srgrimesstruct_code 9351592Srgrimes : F 9361592Srgrimes { 9371592Srgrimes $$ = STRU_F; 9381592Srgrimes } 9391592Srgrimes | R 9401592Srgrimes { 9411592Srgrimes $$ = STRU_R; 9421592Srgrimes } 9431592Srgrimes | P 9441592Srgrimes { 9451592Srgrimes $$ = STRU_P; 9461592Srgrimes } 9471592Srgrimes ; 9481592Srgrimes 9491592Srgrimesmode_code 9501592Srgrimes : S 9511592Srgrimes { 9521592Srgrimes $$ = MODE_S; 9531592Srgrimes } 9541592Srgrimes | B 9551592Srgrimes { 9561592Srgrimes $$ = MODE_B; 9571592Srgrimes } 9581592Srgrimes | C 9591592Srgrimes { 9601592Srgrimes $$ = MODE_C; 9611592Srgrimes } 9621592Srgrimes ; 9631592Srgrimes 9641592Srgrimespathname 9651592Srgrimes : pathstring 9661592Srgrimes { 96775567Speter if (logged_in && $1) { 968110340Syar char *p; 9691592Srgrimes 970110340Syar /* 971110340Syar * Expand ~user manually since glob(3) 972110340Syar * will return the unexpanded pathname 973110340Syar * if the corresponding file/directory 974110340Syar * doesn't exist yet. Using sole glob(3) 975110340Syar * would break natural commands like 976110340Syar * MKD ~user/newdir 977110340Syar * or 978110340Syar * RNTO ~/newfile 979110340Syar */ 980110340Syar if ((p = exptilde($1)) != NULL) { 981110340Syar $$ = expglob(p); 982110340Syar free(p); 983110340Syar } else 9841592Srgrimes $$ = NULL; 9851592Srgrimes free($1); 9861592Srgrimes } else 9871592Srgrimes $$ = $1; 9881592Srgrimes } 9891592Srgrimes ; 9901592Srgrimes 9911592Srgrimespathstring 9921592Srgrimes : STRING 9931592Srgrimes ; 9941592Srgrimes 9951592Srgrimesoctal_number 9961592Srgrimes : NUMBER 9971592Srgrimes { 9981592Srgrimes int ret, dec, multby, digit; 9991592Srgrimes 10001592Srgrimes /* 10011592Srgrimes * Convert a number that was read as decimal number 10021592Srgrimes * to what it would be if it had been read as octal. 10031592Srgrimes */ 100492272Smaxim dec = $1.i; 10051592Srgrimes multby = 1; 10061592Srgrimes ret = 0; 10071592Srgrimes while (dec) { 10081592Srgrimes digit = dec%10; 10091592Srgrimes if (digit > 7) { 10101592Srgrimes ret = -1; 10111592Srgrimes break; 10121592Srgrimes } 10131592Srgrimes ret += digit * multby; 10141592Srgrimes multby *= 8; 10151592Srgrimes dec /= 10; 10161592Srgrimes } 10171592Srgrimes $$ = ret; 10181592Srgrimes } 10191592Srgrimes ; 10201592Srgrimes 10211592Srgrimes 10221592Srgrimescheck_login 10231592Srgrimes : /* empty */ 10241592Srgrimes { 102570102Sphk $$ = check_login1(); 10261592Srgrimes } 10271592Srgrimes ; 10281592Srgrimes 102970102Sphkcheck_login_epsv 103070102Sphk : /* empty */ 103170102Sphk { 103270102Sphk if (noepsv) { 103370102Sphk reply(500, "EPSV command disabled"); 103470102Sphk $$ = 0; 103570102Sphk } 103670102Sphk else 103770102Sphk $$ = check_login1(); 103870102Sphk } 103970102Sphk ; 104070102Sphk 104170102Sphkcheck_login_ro 104270102Sphk : /* empty */ 104370102Sphk { 104470102Sphk if (readonly) { 104572710Sdes reply(550, "Permission denied."); 104670102Sphk $$ = 0; 104770102Sphk } 104870102Sphk else 104970102Sphk $$ = check_login1(); 105070102Sphk } 105170102Sphk ; 105270102Sphk 10531592Srgrimes%% 10541592Srgrimes 10551592Srgrimes#define CMD 0 /* beginning of command */ 10561592Srgrimes#define ARGS 1 /* expect miscellaneous arguments */ 10571592Srgrimes#define STR1 2 /* expect SP followed by STRING */ 10581592Srgrimes#define STR2 3 /* expect STRING */ 10591592Srgrimes#define OSTR 4 /* optional SP then STRING */ 106075556Sgreen#define ZSTR1 5 /* optional SP then optional STRING */ 10611592Srgrimes#define ZSTR2 6 /* optional STRING after SP */ 10621592Srgrimes#define SITECMD 7 /* SITE command */ 10631592Srgrimes#define NSTR 8 /* Number followed by a string */ 10641592Srgrimes 106575560Sjedgar#define MAXGLOBARGS 1000 106675560Sjedgar 1067101034Syar#define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */ 1068101034Syar 10691592Srgrimesstruct tab { 10701592Srgrimes char *name; 10711592Srgrimes short token; 10721592Srgrimes short state; 10731592Srgrimes short implemented; /* 1 if command is implemented */ 10741592Srgrimes char *help; 10751592Srgrimes}; 10761592Srgrimes 10771592Srgrimesstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 10781592Srgrimes { "USER", USER, STR1, 1, "<sp> username" }, 107975556Sgreen { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" }, 10801592Srgrimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 10811592Srgrimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 10821592Srgrimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 10831592Srgrimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 1084101806Syar { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" }, 108556668Sshin { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 108656668Sshin { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 10871592Srgrimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 108856668Sshin { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 108956668Sshin { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 1090101806Syar { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" }, 10911592Srgrimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 10921592Srgrimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 10931592Srgrimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 10941592Srgrimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 10951592Srgrimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 10961592Srgrimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 10971592Srgrimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 10981592Srgrimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 10991592Srgrimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 11001592Srgrimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 11011592Srgrimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 11021592Srgrimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 11031592Srgrimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 11041592Srgrimes { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 11051592Srgrimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 11061592Srgrimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 11071592Srgrimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 11081592Srgrimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 11091592Srgrimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 11101592Srgrimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 11111592Srgrimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 11121592Srgrimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 11131592Srgrimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 11141592Srgrimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 11151592Srgrimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 11161592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11171592Srgrimes { "NOOP", NOOP, ARGS, 1, "" }, 11181592Srgrimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 11191592Srgrimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 11201592Srgrimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 11211592Srgrimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 11221592Srgrimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 11231592Srgrimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 11241592Srgrimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11251592Srgrimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11261592Srgrimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 11271592Srgrimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 11281592Srgrimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 11291592Srgrimes { NULL, 0, 0, 0, 0 } 11301592Srgrimes}; 11311592Srgrimes 11321592Srgrimesstruct tab sitetab[] = { 113375535Sphk { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" }, 11341592Srgrimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 11351592Srgrimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 11361592Srgrimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 11371592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11381592Srgrimes { NULL, 0, 0, 0, 0 } 11391592Srgrimes}; 11401592Srgrimes 114190148Simpstatic char *copy(char *); 1142110340Syarstatic char *expglob(char *); 1143110340Syarstatic char *exptilde(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) { 1292102565Syar yylval.s = p->name; 1293102565Syar if (!p->implemented) 1294102565Syar return (NOTIMPL); /* state remains CMD */ 12951592Srgrimes state = p->state; 12961592Srgrimes return (p->token); 12971592Srgrimes } 12981592Srgrimes break; 12991592Srgrimes 13001592Srgrimes case SITECMD: 13011592Srgrimes if (cbuf[cpos] == ' ') { 13021592Srgrimes cpos++; 13031592Srgrimes return (SP); 13041592Srgrimes } 13051592Srgrimes cp = &cbuf[cpos]; 13061592Srgrimes if ((cp2 = strpbrk(cp, " \n"))) 13071592Srgrimes cpos = cp2 - cbuf; 13081592Srgrimes c = cbuf[cpos]; 13091592Srgrimes cbuf[cpos] = '\0'; 13101592Srgrimes upper(cp); 13111592Srgrimes p = lookup(sitetab, cp); 13121592Srgrimes cbuf[cpos] = c; 13133777Spst if (guest == 0 && p != 0) { 1314102565Syar yylval.s = p->name; 1315102565Syar if (!p->implemented) { 13161592Srgrimes state = CMD; 1317102565Syar return (NOTIMPL); 13181592Srgrimes } 13191592Srgrimes state = p->state; 13201592Srgrimes return (p->token); 13211592Srgrimes } 13221592Srgrimes state = CMD; 13231592Srgrimes break; 13241592Srgrimes 132575556Sgreen case ZSTR1: 13261592Srgrimes case OSTR: 13271592Srgrimes if (cbuf[cpos] == '\n') { 13281592Srgrimes state = CMD; 13291592Srgrimes return (CRLF); 13301592Srgrimes } 13311592Srgrimes /* FALLTHROUGH */ 13321592Srgrimes 13331592Srgrimes case STR1: 13341592Srgrimes dostr1: 13351592Srgrimes if (cbuf[cpos] == ' ') { 13361592Srgrimes cpos++; 133751979Salfred state = state == OSTR ? STR2 : state+1; 13381592Srgrimes return (SP); 13391592Srgrimes } 13401592Srgrimes break; 13411592Srgrimes 13421592Srgrimes case ZSTR2: 13431592Srgrimes if (cbuf[cpos] == '\n') { 13441592Srgrimes state = CMD; 13451592Srgrimes return (CRLF); 13461592Srgrimes } 13471592Srgrimes /* FALLTHROUGH */ 13481592Srgrimes 13491592Srgrimes case STR2: 13501592Srgrimes cp = &cbuf[cpos]; 13511592Srgrimes n = strlen(cp); 13521592Srgrimes cpos += n - 1; 13531592Srgrimes /* 13541592Srgrimes * Make sure the string is nonempty and \n terminated. 13551592Srgrimes */ 13561592Srgrimes if (n > 1 && cbuf[cpos] == '\n') { 13571592Srgrimes cbuf[cpos] = '\0'; 13581592Srgrimes yylval.s = copy(cp); 13591592Srgrimes cbuf[cpos] = '\n'; 13601592Srgrimes state = ARGS; 13611592Srgrimes return (STRING); 13621592Srgrimes } 13631592Srgrimes break; 13641592Srgrimes 13651592Srgrimes case NSTR: 13661592Srgrimes if (cbuf[cpos] == ' ') { 13671592Srgrimes cpos++; 13681592Srgrimes return (SP); 13691592Srgrimes } 13701592Srgrimes if (isdigit(cbuf[cpos])) { 13711592Srgrimes cp = &cbuf[cpos]; 13721592Srgrimes while (isdigit(cbuf[++cpos])) 13731592Srgrimes ; 13741592Srgrimes c = cbuf[cpos]; 13751592Srgrimes cbuf[cpos] = '\0'; 137692272Smaxim yylval.u.i = atoi(cp); 13771592Srgrimes cbuf[cpos] = c; 13781592Srgrimes state = STR1; 13791592Srgrimes return (NUMBER); 13801592Srgrimes } 13811592Srgrimes state = STR1; 13821592Srgrimes goto dostr1; 13831592Srgrimes 13841592Srgrimes case ARGS: 13851592Srgrimes if (isdigit(cbuf[cpos])) { 13861592Srgrimes cp = &cbuf[cpos]; 13871592Srgrimes while (isdigit(cbuf[++cpos])) 13881592Srgrimes ; 13891592Srgrimes c = cbuf[cpos]; 13901592Srgrimes cbuf[cpos] = '\0'; 139192272Smaxim yylval.u.i = atoi(cp); 139292272Smaxim yylval.u.o = strtoull(cp, (char **)NULL, 10); 13931592Srgrimes cbuf[cpos] = c; 13941592Srgrimes return (NUMBER); 13951592Srgrimes } 139656668Sshin if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 139756668Sshin && !isalnum(cbuf[cpos + 3])) { 139856668Sshin cpos += 3; 139956668Sshin return ALL; 140056668Sshin } 14011592Srgrimes switch (cbuf[cpos++]) { 14021592Srgrimes 14031592Srgrimes case '\n': 14041592Srgrimes state = CMD; 14051592Srgrimes return (CRLF); 14061592Srgrimes 14071592Srgrimes case ' ': 14081592Srgrimes return (SP); 14091592Srgrimes 14101592Srgrimes case ',': 14111592Srgrimes return (COMMA); 14121592Srgrimes 14131592Srgrimes case 'A': 14141592Srgrimes case 'a': 14151592Srgrimes return (A); 14161592Srgrimes 14171592Srgrimes case 'B': 14181592Srgrimes case 'b': 14191592Srgrimes return (B); 14201592Srgrimes 14211592Srgrimes case 'C': 14221592Srgrimes case 'c': 14231592Srgrimes return (C); 14241592Srgrimes 14251592Srgrimes case 'E': 14261592Srgrimes case 'e': 14271592Srgrimes return (E); 14281592Srgrimes 14291592Srgrimes case 'F': 14301592Srgrimes case 'f': 14311592Srgrimes return (F); 14321592Srgrimes 14331592Srgrimes case 'I': 14341592Srgrimes case 'i': 14351592Srgrimes return (I); 14361592Srgrimes 14371592Srgrimes case 'L': 14381592Srgrimes case 'l': 14391592Srgrimes return (L); 14401592Srgrimes 14411592Srgrimes case 'N': 14421592Srgrimes case 'n': 14431592Srgrimes return (N); 14441592Srgrimes 14451592Srgrimes case 'P': 14461592Srgrimes case 'p': 14471592Srgrimes return (P); 14481592Srgrimes 14491592Srgrimes case 'R': 14501592Srgrimes case 'r': 14511592Srgrimes return (R); 14521592Srgrimes 14531592Srgrimes case 'S': 14541592Srgrimes case 's': 14551592Srgrimes return (S); 14561592Srgrimes 14571592Srgrimes case 'T': 14581592Srgrimes case 't': 14591592Srgrimes return (T); 14601592Srgrimes 14611592Srgrimes } 14621592Srgrimes break; 14631592Srgrimes 14641592Srgrimes default: 146576096Smarkm fatalerror("Unknown state in scanner."); 14661592Srgrimes } 14671592Srgrimes state = CMD; 146889935Syar return (LEXERR); 14691592Srgrimes } 14701592Srgrimes} 14711592Srgrimes 14721592Srgrimesvoid 147390148Simpupper(char *s) 14741592Srgrimes{ 14751592Srgrimes while (*s != '\0') { 14761592Srgrimes if (islower(*s)) 14771592Srgrimes *s = toupper(*s); 14781592Srgrimes s++; 14791592Srgrimes } 14801592Srgrimes} 14811592Srgrimes 14821592Srgrimesstatic char * 148390148Simpcopy(char *s) 14841592Srgrimes{ 14851592Srgrimes char *p; 14861592Srgrimes 14871592Srgrimes p = malloc((unsigned) strlen(s) + 1); 14881592Srgrimes if (p == NULL) 148976096Smarkm fatalerror("Ran out of memory."); 14901592Srgrimes (void) strcpy(p, s); 14911592Srgrimes return (p); 14921592Srgrimes} 14931592Srgrimes 14941592Srgrimesstatic void 149590148Simphelp(struct tab *ctab, char *s) 14961592Srgrimes{ 14971592Srgrimes struct tab *c; 14981592Srgrimes int width, NCMDS; 14991592Srgrimes char *type; 15001592Srgrimes 15011592Srgrimes if (ctab == sitetab) 15021592Srgrimes type = "SITE "; 15031592Srgrimes else 15041592Srgrimes type = ""; 15051592Srgrimes width = 0, NCMDS = 0; 15061592Srgrimes for (c = ctab; c->name != NULL; c++) { 15071592Srgrimes int len = strlen(c->name); 15081592Srgrimes 15091592Srgrimes if (len > width) 15101592Srgrimes width = len; 15111592Srgrimes NCMDS++; 15121592Srgrimes } 15131592Srgrimes width = (width + 8) &~ 7; 15141592Srgrimes if (s == 0) { 15151592Srgrimes int i, j, w; 15161592Srgrimes int columns, lines; 15171592Srgrimes 15181592Srgrimes lreply(214, "The following %scommands are recognized %s.", 15191592Srgrimes type, "(* =>'s unimplemented)"); 15201592Srgrimes columns = 76 / width; 15211592Srgrimes if (columns == 0) 15221592Srgrimes columns = 1; 15231592Srgrimes lines = (NCMDS + columns - 1) / columns; 15241592Srgrimes for (i = 0; i < lines; i++) { 15251592Srgrimes printf(" "); 15261592Srgrimes for (j = 0; j < columns; j++) { 15271592Srgrimes c = ctab + j * lines + i; 15281592Srgrimes printf("%s%c", c->name, 15291592Srgrimes c->implemented ? ' ' : '*'); 15301592Srgrimes if (c + lines >= &ctab[NCMDS]) 15311592Srgrimes break; 15321592Srgrimes w = strlen(c->name) + 1; 15331592Srgrimes while (w < width) { 15341592Srgrimes putchar(' '); 15351592Srgrimes w++; 15361592Srgrimes } 15371592Srgrimes } 15381592Srgrimes printf("\r\n"); 15391592Srgrimes } 15401592Srgrimes (void) fflush(stdout); 1541110037Syar if (hostinfo) 1542110037Syar reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1543110037Syar else 1544110037Syar reply(214, "End."); 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; 1592101034Syar } else if (stbuf.st_size > MAXASIZE) { 1593101034Syar reply(550, "%s: too large for type A SIZE.", filename); 1594101034Syar (void) fclose(fin); 1595101034Syar return; 15961592Srgrimes } 15971592Srgrimes 15981592Srgrimes count = 0; 15991592Srgrimes while((c=getc(fin)) != EOF) { 16001592Srgrimes if (c == '\n') /* will get expanded to \r\n */ 16011592Srgrimes count++; 16021592Srgrimes count++; 16031592Srgrimes } 16041592Srgrimes (void) fclose(fin); 16051592Srgrimes 16061592Srgrimes reply(213, "%qd", count); 16071592Srgrimes break; } 16081592Srgrimes default: 1609100684Syar reply(504, "SIZE not implemented for type %s.", 1610100684Syar typenames[type]); 16111592Srgrimes } 16121592Srgrimes} 161356668Sshin 161456668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 161556668Sshinstatic int 161690148Simpport_check(const char *pcmd) 161756668Sshin{ 161856668Sshin if (his_addr.su_family == AF_INET) { 161956668Sshin if (data_dest.su_family != AF_INET) { 162056668Sshin usedefault = 1; 162156668Sshin reply(500, "Invalid address rejected."); 162256668Sshin return 1; 162356668Sshin } 162456668Sshin if (paranoid && 162556668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 162656668Sshin memcmp(&data_dest.su_sin.sin_addr, 162756668Sshin &his_addr.su_sin.sin_addr, 162856668Sshin sizeof(data_dest.su_sin.sin_addr)))) { 162956668Sshin usedefault = 1; 163056668Sshin reply(500, "Illegal PORT range rejected."); 163156668Sshin } else { 163256668Sshin usedefault = 0; 163356668Sshin if (pdata >= 0) { 163456668Sshin (void) close(pdata); 163556668Sshin pdata = -1; 163656668Sshin } 163756668Sshin reply(200, "%s command successful.", pcmd); 163856668Sshin } 163956668Sshin return 1; 164056668Sshin } 164156668Sshin return 0; 164256668Sshin} 164356668Sshin 164470102Sphkstatic int 164590148Simpcheck_login1(void) 164670102Sphk{ 164770102Sphk if (logged_in) 164870102Sphk return 1; 164970102Sphk else { 165070102Sphk reply(530, "Please login with USER and PASS."); 165170102Sphk return 0; 165270102Sphk } 165370102Sphk} 165470102Sphk 1655110340Syar/* 1656110340Syar * Replace leading "~user" in a pathname by the user's login directory. 1657110340Syar * Returned string will be in a freshly malloced buffer unless it's NULL. 1658110340Syar */ 1659110340Syarstatic char * 1660110340Syarexptilde(char *s) 1661110340Syar{ 1662110340Syar char *p, *q; 1663110340Syar char *path, *user; 1664110340Syar struct passwd *ppw; 1665110340Syar 1666110340Syar if ((p = strdup(s)) == NULL) 1667110340Syar return (NULL); 1668110340Syar if (*p != '~') 1669110340Syar return (p); 1670110340Syar 1671110340Syar user = p + 1; /* skip tilde */ 1672110340Syar if ((path = strchr(p, '/')) != NULL) 1673110340Syar *(path++) = '\0'; /* separate ~user from the rest of path */ 1674110378Syar if (*user == '\0') /* no user specified, use the current user */ 1675110378Syar user = pw->pw_name; 1676110378Syar /* read passwd even for the current user since we may be chrooted */ 1677110378Syar if ((ppw = getpwnam(user)) != NULL) { 1678110340Syar /* user found, substitute login directory for ~user */ 1679110340Syar if (path) 1680110340Syar asprintf(&q, "%s/%s", ppw->pw_dir, path); 1681110340Syar else 1682110340Syar q = strdup(ppw->pw_dir); 1683110340Syar free(p); 1684110340Syar p = q; 1685110340Syar } else { 1686110340Syar /* user not found, undo the damage */ 1687110340Syar if (path) 1688110340Syar path[-1] = '/'; 1689110340Syar } 1690110340Syar return (p); 1691110340Syar} 1692110340Syar 1693110340Syar/* 1694110340Syar * Expand glob(3) patterns possibly present in a pathname. 1695110340Syar * Avoid expanding to a pathname including '\r' or '\n' in order to 1696110340Syar * not disrupt the FTP protocol. 1697110340Syar * The expansion found must be unique. 1698110340Syar * Return the result as a malloced string, or NULL if an error occured. 1699110340Syar * 1700110340Syar * Problem: this production is used for all pathname 1701110340Syar * processing, but only gives a 550 error reply. 1702110340Syar * This is a valid reply in some cases but not in others. 1703110340Syar */ 1704110340Syarstatic char * 1705110340Syarexpglob(char *s) 1706110340Syar{ 1707110340Syar char *p, **pp, *rval; 1708110340Syar int flags = GLOB_BRACE | GLOB_NOCHECK; 1709110340Syar int n; 1710110340Syar glob_t gl; 1711110340Syar 1712110340Syar memset(&gl, 0, sizeof(gl)); 1713110340Syar flags |= GLOB_LIMIT; 1714110340Syar gl.gl_matchc = MAXGLOBARGS; 1715110340Syar if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) { 1716110340Syar for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++) 1717110340Syar if (*(*pp + strcspn(*pp, "\r\n")) == '\0') { 1718110340Syar p = *pp; 1719110340Syar n++; 1720110340Syar } 1721110340Syar if (n == 0) 1722110340Syar rval = strdup(s); 1723110340Syar else if (n == 1) 1724110340Syar rval = strdup(p); 1725110340Syar else { 1726110340Syar reply(550, "ambiguous"); 1727110340Syar rval = NULL; 1728110340Syar } 1729110340Syar } else { 1730110340Syar reply(550, "wildcard expansion error"); 1731110340Syar rval = NULL; 1732110340Syar } 1733110340Syar globfree(&gl); 1734110340Syar return (rval); 1735110340Syar} 1736110340Syar 173756668Sshin#ifdef INET6 173856668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 173956668Sshinstatic int 174090148Simpport_check_v6(const char *pcmd) 174156668Sshin{ 174256668Sshin if (his_addr.su_family == AF_INET6) { 174356668Sshin if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 174456668Sshin /* Convert data_dest into v4 mapped sockaddr.*/ 174556668Sshin v4map_data_dest(); 174656668Sshin if (data_dest.su_family != AF_INET6) { 174756668Sshin usedefault = 1; 174856668Sshin reply(500, "Invalid address rejected."); 174956668Sshin return 1; 175056668Sshin } 175156668Sshin if (paranoid && 175256668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 175356668Sshin memcmp(&data_dest.su_sin6.sin6_addr, 175456668Sshin &his_addr.su_sin6.sin6_addr, 175556668Sshin sizeof(data_dest.su_sin6.sin6_addr)))) { 175656668Sshin usedefault = 1; 175756668Sshin reply(500, "Illegal PORT range rejected."); 175856668Sshin } else { 175956668Sshin usedefault = 0; 176056668Sshin if (pdata >= 0) { 176156668Sshin (void) close(pdata); 176256668Sshin pdata = -1; 176356668Sshin } 176456668Sshin reply(200, "%s command successful.", pcmd); 176556668Sshin } 176656668Sshin return 1; 176756668Sshin } 176856668Sshin return 0; 176956668Sshin} 177056668Sshin 177156668Sshinstatic void 177290148Simpv4map_data_dest(void) 177356668Sshin{ 177456668Sshin struct in_addr savedaddr; 177556668Sshin int savedport; 177656668Sshin 177756668Sshin if (data_dest.su_family != AF_INET) { 177856668Sshin usedefault = 1; 177956668Sshin reply(500, "Invalid address rejected."); 178056668Sshin return; 178156668Sshin } 178256668Sshin 178356668Sshin savedaddr = data_dest.su_sin.sin_addr; 178456668Sshin savedport = data_dest.su_port; 178556668Sshin 178656668Sshin memset(&data_dest, 0, sizeof(data_dest)); 178756668Sshin data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 178856668Sshin data_dest.su_sin6.sin6_family = AF_INET6; 178956668Sshin data_dest.su_sin6.sin6_port = savedport; 179056668Sshin memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 179156668Sshin memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 179256668Sshin (caddr_t)&savedaddr, sizeof(savedaddr)); 179356668Sshin} 179456668Sshin#endif 1795