ftpcmd.y revision 117352
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 117352 2003-07-09 13:54:33Z 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; 921592Srgrimesextern char proctitle[]; 931592Srgrimesextern int usedefault; 941592Srgrimesextern char tmpline[]; 9570102Sphkextern int readonly; 9670102Sphkextern int noepsv; 9782460Snikextern int noretr; 9882796Ssheldonhextern int noguestretr; 99100684Syarextern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */ 1001592Srgrimes 1011592Srgrimesoff_t restart_point; 1021592Srgrimes 1031592Srgrimesstatic int cmd_type; 1041592Srgrimesstatic int cmd_form; 1051592Srgrimesstatic int cmd_bytesz; 10689935Syarstatic int state; 1071592Srgrimeschar cbuf[512]; 10888935Sdwmalonechar *fromname = (char *) 0; 1091592Srgrimes 11056668Sshinextern int epsvall; 11156668Sshin 1121592Srgrimes%} 1131592Srgrimes 1141592Srgrimes%union { 11592272Smaxim struct { 11692272Smaxim off_t o; 11792272Smaxim int i; 11892272Smaxim } u; 1191592Srgrimes char *s; 1201592Srgrimes} 1211592Srgrimes 1221592Srgrimes%token 1231592Srgrimes A B C E F I 1241592Srgrimes L N P R S T 12556668Sshin ALL 1261592Srgrimes 1271592Srgrimes SP CRLF COMMA 1281592Srgrimes 1291592Srgrimes USER PASS ACCT REIN QUIT PORT 1301592Srgrimes PASV TYPE STRU MODE RETR STOR 1311592Srgrimes APPE MLFL MAIL MSND MSOM MSAM 1321592Srgrimes MRSQ MRCP ALLO REST RNFR RNTO 1331592Srgrimes ABOR DELE CWD LIST NLST SITE 1341592Srgrimes STAT HELP NOOP MKD RMD PWD 1351592Srgrimes CDUP STOU SMNT SYST SIZE MDTM 13656668Sshin LPRT LPSV EPRT EPSV 1371592Srgrimes 13875535Sphk UMASK IDLE CHMOD MDFIVE 1391592Srgrimes 140102565Syar LEXERR NOTIMPL 1411592Srgrimes 1421592Srgrimes%token <s> STRING 14392272Smaxim%token <u> NUMBER 1441592Srgrimes 14592272Smaxim%type <u.i> check_login octal_number byte_size 14692272Smaxim%type <u.i> check_login_ro check_login_epsv 14792272Smaxim%type <u.i> struct_code mode_code type_code form_code 14875567Speter%type <s> pathstring pathname password username 149102565Syar%type <s> ALL NOTIMPL 1501592Srgrimes 1511592Srgrimes%start cmd_list 1521592Srgrimes 1531592Srgrimes%% 1541592Srgrimes 1551592Srgrimescmd_list 1561592Srgrimes : /* empty */ 1571592Srgrimes | cmd_list cmd 1581592Srgrimes { 15988935Sdwmalone if (fromname) 16088935Sdwmalone free(fromname); 1611592Srgrimes fromname = (char *) 0; 1621592Srgrimes restart_point = (off_t) 0; 1631592Srgrimes } 1641592Srgrimes | cmd_list rcmd 1651592Srgrimes ; 1661592Srgrimes 1671592Srgrimescmd 1681592Srgrimes : USER SP username CRLF 1691592Srgrimes { 1701592Srgrimes user($3); 1711592Srgrimes free($3); 1721592Srgrimes } 1731592Srgrimes | PASS SP password CRLF 1741592Srgrimes { 1751592Srgrimes pass($3); 1761592Srgrimes free($3); 1771592Srgrimes } 17875556Sgreen | PASS CRLF 17975556Sgreen { 18075556Sgreen pass(""); 18175556Sgreen } 18217433Spst | PORT check_login SP host_port CRLF 1831592Srgrimes { 18456668Sshin if (epsvall) { 18556668Sshin reply(501, "no PORT allowed after EPSV ALL"); 18656668Sshin goto port_done; 18756668Sshin } 18856668Sshin if (!$2) 18956668Sshin goto port_done; 19056668Sshin if (port_check("PORT") == 1) 19156668Sshin goto port_done; 19256668Sshin#ifdef INET6 19356668Sshin if ((his_addr.su_family != AF_INET6 || 19456668Sshin !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { 19556668Sshin /* shoud never happen */ 19656668Sshin usedefault = 1; 19756668Sshin reply(500, "Invalid address rejected."); 19856668Sshin goto port_done; 19956668Sshin } 20056668Sshin port_check_v6("pcmd"); 20156668Sshin#endif 20256668Sshin port_done: 20356668Sshin } 20456668Sshin | LPRT check_login SP host_long_port CRLF 20556668Sshin { 20656668Sshin if (epsvall) { 20756668Sshin reply(501, "no LPRT allowed after EPSV ALL"); 20856668Sshin goto lprt_done; 20956668Sshin } 21056668Sshin if (!$2) 21156668Sshin goto lprt_done; 21256668Sshin if (port_check("LPRT") == 1) 21356668Sshin goto lprt_done; 21456668Sshin#ifdef INET6 21556668Sshin if (his_addr.su_family != AF_INET6) { 21656668Sshin usedefault = 1; 21756668Sshin reply(500, "Invalid address rejected."); 21856668Sshin goto lprt_done; 21956668Sshin } 22056668Sshin if (port_check_v6("LPRT") == 1) 22156668Sshin goto lprt_done; 22256668Sshin#endif 22356668Sshin lprt_done: 22456668Sshin } 22556668Sshin | EPRT check_login SP STRING CRLF 22656668Sshin { 22756668Sshin char delim; 22856668Sshin char *tmp = NULL; 22956668Sshin char *p, *q; 23056668Sshin char *result[3]; 23156668Sshin struct addrinfo hints; 23256668Sshin struct addrinfo *res; 23356668Sshin int i; 23456668Sshin 23556668Sshin if (epsvall) { 23656668Sshin reply(501, "no EPRT allowed after EPSV ALL"); 23756668Sshin goto eprt_done; 23856668Sshin } 23956668Sshin if (!$2) 24056668Sshin goto eprt_done; 24156668Sshin 24256668Sshin memset(&data_dest, 0, sizeof(data_dest)); 24356668Sshin tmp = strdup($4); 24476096Smarkm if (ftpdebug) 24556668Sshin syslog(LOG_DEBUG, "%s", tmp); 24656668Sshin if (!tmp) { 24776096Smarkm fatalerror("not enough core"); 24856668Sshin /*NOTREACHED*/ 24956668Sshin } 25056668Sshin p = tmp; 25156668Sshin delim = p[0]; 25256668Sshin p++; 25356668Sshin memset(result, 0, sizeof(result)); 25456668Sshin for (i = 0; i < 3; i++) { 25556668Sshin q = strchr(p, delim); 25656668Sshin if (!q || *q != delim) { 25756668Sshin parsefail: 25856668Sshin reply(500, 25956668Sshin "Invalid argument, rejected."); 26056668Sshin if (tmp) 26156668Sshin free(tmp); 26217433Spst usedefault = 1; 26356668Sshin goto eprt_done; 26417433Spst } 26556668Sshin *q++ = '\0'; 26656668Sshin result[i] = p; 26776096Smarkm if (ftpdebug) 26856668Sshin syslog(LOG_DEBUG, "%d: %s", i, p); 26956668Sshin p = q; 2701592Srgrimes } 27156668Sshin 27256668Sshin /* some more sanity check */ 27356668Sshin p = result[0]; 27456668Sshin while (*p) { 27556668Sshin if (!isdigit(*p)) 27656668Sshin goto parsefail; 27756668Sshin p++; 27856668Sshin } 27956668Sshin p = result[2]; 28056668Sshin while (*p) { 28156668Sshin if (!isdigit(*p)) 28256668Sshin goto parsefail; 28356668Sshin p++; 28456668Sshin } 28556668Sshin 28656668Sshin /* grab address */ 28756668Sshin memset(&hints, 0, sizeof(hints)); 28856668Sshin if (atoi(result[0]) == 1) 28956668Sshin hints.ai_family = PF_INET; 29056668Sshin#ifdef INET6 29156668Sshin else if (atoi(result[0]) == 2) 29256668Sshin hints.ai_family = PF_INET6; 29356668Sshin#endif 29456668Sshin else 29556668Sshin hints.ai_family = PF_UNSPEC; /*XXX*/ 29656668Sshin hints.ai_socktype = SOCK_STREAM; 29756668Sshin i = getaddrinfo(result[1], result[2], &hints, &res); 29856668Sshin if (i) 29956668Sshin goto parsefail; 30056668Sshin memcpy(&data_dest, res->ai_addr, res->ai_addrlen); 30156668Sshin#ifdef INET6 30256668Sshin if (his_addr.su_family == AF_INET6 30356668Sshin && data_dest.su_family == AF_INET6) { 30456668Sshin /* XXX more sanity checks! */ 30556668Sshin data_dest.su_sin6.sin6_scope_id = 30656668Sshin his_addr.su_sin6.sin6_scope_id; 30756668Sshin } 30856668Sshin#endif 30956668Sshin free(tmp); 31056668Sshin tmp = NULL; 31156668Sshin 31256668Sshin if (port_check("EPRT") == 1) 31356668Sshin goto eprt_done; 31456668Sshin#ifdef INET6 31556668Sshin if (his_addr.su_family != AF_INET6) { 31656668Sshin usedefault = 1; 31756668Sshin reply(500, "Invalid address rejected."); 31856668Sshin goto eprt_done; 31956668Sshin } 32056668Sshin if (port_check_v6("EPRT") == 1) 32156668Sshin goto eprt_done; 32256668Sshin#endif 32388935Sdwmalone eprt_done: 32488935Sdwmalone free($4); 3251592Srgrimes } 32617433Spst | PASV check_login CRLF 3271592Srgrimes { 32856668Sshin if (epsvall) 32956668Sshin reply(501, "no PASV allowed after EPSV ALL"); 33056668Sshin else if ($2) 33117433Spst passive(); 3321592Srgrimes } 33356668Sshin | LPSV check_login CRLF 33456668Sshin { 33556668Sshin if (epsvall) 33656668Sshin reply(501, "no LPSV allowed after EPSV ALL"); 33756668Sshin else if ($2) 33856668Sshin long_passive("LPSV", PF_UNSPEC); 33956668Sshin } 34070102Sphk | EPSV check_login_epsv SP NUMBER CRLF 34156668Sshin { 34256668Sshin if ($2) { 34356668Sshin int pf; 34492272Smaxim switch ($4.i) { 34556668Sshin case 1: 34656668Sshin pf = PF_INET; 34756668Sshin break; 34856668Sshin#ifdef INET6 34956668Sshin case 2: 35056668Sshin pf = PF_INET6; 35156668Sshin break; 35256668Sshin#endif 35356668Sshin default: 35456668Sshin pf = -1; /*junk value*/ 35556668Sshin break; 35656668Sshin } 35756668Sshin long_passive("EPSV", pf); 35856668Sshin } 35956668Sshin } 36070102Sphk | EPSV check_login_epsv SP ALL CRLF 36156668Sshin { 36256668Sshin if ($2) { 36356668Sshin reply(200, 36456668Sshin "EPSV ALL command successful."); 36556668Sshin epsvall++; 36656668Sshin } 36756668Sshin } 36870102Sphk | EPSV check_login_epsv CRLF 36956668Sshin { 37056668Sshin if ($2) 37156668Sshin long_passive("EPSV", PF_UNSPEC); 37256668Sshin } 37371278Sjedgar | TYPE check_login SP type_code CRLF 3741592Srgrimes { 37571278Sjedgar if ($2) { 37671278Sjedgar switch (cmd_type) { 3771592Srgrimes 37871278Sjedgar case TYPE_A: 37971278Sjedgar if (cmd_form == FORM_N) { 38071278Sjedgar reply(200, "Type set to A."); 38171278Sjedgar type = cmd_type; 38271278Sjedgar form = cmd_form; 38371278Sjedgar } else 38471278Sjedgar reply(504, "Form must be N."); 38571278Sjedgar break; 3861592Srgrimes 38771278Sjedgar case TYPE_E: 38871278Sjedgar reply(504, "Type E not implemented."); 38971278Sjedgar break; 3901592Srgrimes 39171278Sjedgar case TYPE_I: 39271278Sjedgar reply(200, "Type set to I."); 39371278Sjedgar type = cmd_type; 39471278Sjedgar break; 3951592Srgrimes 39671278Sjedgar case TYPE_L: 397103949Smike#if CHAR_BIT == 8 39871278Sjedgar if (cmd_bytesz == 8) { 39971278Sjedgar reply(200, 40071278Sjedgar "Type set to L (byte size 8)."); 40171278Sjedgar type = cmd_type; 40271278Sjedgar } else 40371278Sjedgar reply(504, "Byte size must be 8."); 404103949Smike#else /* CHAR_BIT == 8 */ 405103949Smike UNIMPLEMENTED for CHAR_BIT != 8 406103949Smike#endif /* CHAR_BIT == 8 */ 40771278Sjedgar } 4081592Srgrimes } 4091592Srgrimes } 41071278Sjedgar | STRU check_login SP struct_code CRLF 4111592Srgrimes { 41271278Sjedgar if ($2) { 41371278Sjedgar switch ($4) { 4141592Srgrimes 41571278Sjedgar case STRU_F: 41671278Sjedgar reply(200, "STRU F ok."); 41771278Sjedgar break; 4181592Srgrimes 41971278Sjedgar default: 42071278Sjedgar reply(504, "Unimplemented STRU type."); 42171278Sjedgar } 4221592Srgrimes } 4231592Srgrimes } 42471278Sjedgar | MODE check_login SP mode_code CRLF 4251592Srgrimes { 42671278Sjedgar if ($2) { 42771278Sjedgar switch ($4) { 4281592Srgrimes 42971278Sjedgar case MODE_S: 43071278Sjedgar reply(200, "MODE S ok."); 43171278Sjedgar break; 43271278Sjedgar 43371278Sjedgar default: 43471278Sjedgar reply(502, "Unimplemented MODE type."); 43571278Sjedgar } 4361592Srgrimes } 4371592Srgrimes } 43871278Sjedgar | ALLO check_login SP NUMBER CRLF 4391592Srgrimes { 44071278Sjedgar if ($2) { 44171278Sjedgar reply(202, "ALLO command ignored."); 44271278Sjedgar } 4431592Srgrimes } 44471278Sjedgar | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 4451592Srgrimes { 44671278Sjedgar if ($2) { 44771278Sjedgar reply(202, "ALLO command ignored."); 44871278Sjedgar } 4491592Srgrimes } 4501592Srgrimes | RETR check_login SP pathname CRLF 4511592Srgrimes { 45282796Ssheldonh if (noretr || (guest && noguestretr)) 45382460Snik reply(500, "RETR command is disabled"); 45482460Snik else if ($2 && $4 != NULL) 4551592Srgrimes retrieve((char *) 0, $4); 45682460Snik 4571592Srgrimes if ($4 != NULL) 4581592Srgrimes free($4); 4591592Srgrimes } 46070102Sphk | STOR check_login_ro SP pathname CRLF 4611592Srgrimes { 4621592Srgrimes if ($2 && $4 != NULL) 4631592Srgrimes store($4, "w", 0); 4641592Srgrimes if ($4 != NULL) 4651592Srgrimes free($4); 4661592Srgrimes } 46770102Sphk | APPE check_login_ro SP pathname CRLF 4681592Srgrimes { 4691592Srgrimes if ($2 && $4 != NULL) 4701592Srgrimes store($4, "a", 0); 4711592Srgrimes if ($4 != NULL) 4721592Srgrimes free($4); 4731592Srgrimes } 4741592Srgrimes | NLST check_login CRLF 4751592Srgrimes { 4761592Srgrimes if ($2) 4771592Srgrimes send_file_list("."); 4781592Srgrimes } 479101395Syar | NLST check_login SP pathstring CRLF 4801592Srgrimes { 481101395Syar if ($2) 4821592Srgrimes send_file_list($4); 483101395Syar free($4); 4841592Srgrimes } 4851592Srgrimes | LIST check_login CRLF 4861592Srgrimes { 4871592Srgrimes if ($2) 488109380Syar retrieve(_PATH_LS " -lgA", ""); 4891592Srgrimes } 49075567Speter | LIST check_login SP pathstring CRLF 4911592Srgrimes { 492101395Syar if ($2) 493109380Syar retrieve(_PATH_LS " -lgA %s", $4); 494101395Syar free($4); 4951592Srgrimes } 4961592Srgrimes | STAT check_login SP pathname CRLF 4971592Srgrimes { 4981592Srgrimes if ($2 && $4 != NULL) 4991592Srgrimes statfilecmd($4); 5001592Srgrimes if ($4 != NULL) 5011592Srgrimes free($4); 5021592Srgrimes } 50371278Sjedgar | STAT check_login CRLF 5041592Srgrimes { 50571278Sjedgar if ($2) { 50671278Sjedgar statcmd(); 50771278Sjedgar } 5081592Srgrimes } 50970102Sphk | DELE check_login_ro SP pathname CRLF 5101592Srgrimes { 5111592Srgrimes if ($2 && $4 != NULL) 5121592Srgrimes delete($4); 5131592Srgrimes if ($4 != NULL) 5141592Srgrimes free($4); 5151592Srgrimes } 51670102Sphk | RNTO check_login_ro SP pathname CRLF 5171592Srgrimes { 518101379Syar if ($2 && $4 != NULL) { 51917433Spst if (fromname) { 52017433Spst renamecmd(fromname, $4); 52117433Spst free(fromname); 52217433Spst fromname = (char *) 0; 52317433Spst } else { 52417433Spst reply(503, "Bad sequence of commands."); 52517433Spst } 5261592Srgrimes } 527101379Syar if ($4 != NULL) 528101379Syar 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) { 538110036Syar cwd(homedir); 53969234Sdanny } 5401592Srgrimes } 5411592Srgrimes | CWD check_login SP pathname CRLF 5421592Srgrimes { 5431592Srgrimes if ($2 && $4 != NULL) 5441592Srgrimes cwd($4); 5451592Srgrimes if ($4 != NULL) 5461592Srgrimes free($4); 5471592Srgrimes } 5481592Srgrimes | HELP CRLF 5491592Srgrimes { 5501592Srgrimes help(cmdtab, (char *) 0); 5511592Srgrimes } 5521592Srgrimes | HELP SP STRING CRLF 5531592Srgrimes { 5541592Srgrimes char *cp = $3; 5551592Srgrimes 5561592Srgrimes if (strncasecmp(cp, "SITE", 4) == 0) { 5571592Srgrimes cp = $3 + 4; 5581592Srgrimes if (*cp == ' ') 5591592Srgrimes cp++; 5601592Srgrimes if (*cp) 5611592Srgrimes help(sitetab, cp); 5621592Srgrimes else 5631592Srgrimes help(sitetab, (char *) 0); 5641592Srgrimes } else 5651592Srgrimes help(cmdtab, $3); 56688935Sdwmalone free($3); 5671592Srgrimes } 5681592Srgrimes | NOOP CRLF 5691592Srgrimes { 5701592Srgrimes reply(200, "NOOP command successful."); 5711592Srgrimes } 57270102Sphk | MKD check_login_ro SP pathname CRLF 5731592Srgrimes { 5741592Srgrimes if ($2 && $4 != NULL) 5751592Srgrimes makedir($4); 5761592Srgrimes if ($4 != NULL) 5771592Srgrimes free($4); 5781592Srgrimes } 57970102Sphk | RMD check_login_ro SP pathname CRLF 5801592Srgrimes { 5811592Srgrimes if ($2 && $4 != NULL) 5821592Srgrimes removedir($4); 5831592Srgrimes if ($4 != NULL) 5841592Srgrimes free($4); 5851592Srgrimes } 5861592Srgrimes | PWD check_login CRLF 5871592Srgrimes { 5881592Srgrimes if ($2) 5891592Srgrimes pwd(); 5901592Srgrimes } 5911592Srgrimes | CDUP check_login CRLF 5921592Srgrimes { 5931592Srgrimes if ($2) 5941592Srgrimes cwd(".."); 5951592Srgrimes } 5961592Srgrimes | SITE SP HELP CRLF 5971592Srgrimes { 5981592Srgrimes help(sitetab, (char *) 0); 5991592Srgrimes } 6001592Srgrimes | SITE SP HELP SP STRING CRLF 6011592Srgrimes { 6021592Srgrimes help(sitetab, $5); 60388935Sdwmalone free($5); 6041592Srgrimes } 60575535Sphk | SITE SP MDFIVE check_login SP pathname CRLF 60675535Sphk { 60775535Sphk char p[64], *q; 60875535Sphk 609101379Syar if ($4 && $6) { 61075535Sphk q = MD5File($6, p); 61175535Sphk if (q != NULL) 61275535Sphk reply(200, "MD5(%s) = %s", $6, p); 61375535Sphk else 61475535Sphk perror_reply(550, $6); 61575535Sphk } 61688935Sdwmalone if ($6) 61788935Sdwmalone free($6); 61875535Sphk } 6191592Srgrimes | SITE SP UMASK check_login CRLF 6201592Srgrimes { 6211592Srgrimes int oldmask; 6221592Srgrimes 6231592Srgrimes if ($4) { 6241592Srgrimes oldmask = umask(0); 6251592Srgrimes (void) umask(oldmask); 6261592Srgrimes reply(200, "Current UMASK is %03o", oldmask); 6271592Srgrimes } 6281592Srgrimes } 6291592Srgrimes | SITE SP UMASK check_login SP octal_number CRLF 6301592Srgrimes { 6311592Srgrimes int oldmask; 6321592Srgrimes 6331592Srgrimes if ($4) { 6341592Srgrimes if (($6 == -1) || ($6 > 0777)) { 6351592Srgrimes reply(501, "Bad UMASK value"); 6361592Srgrimes } else { 6371592Srgrimes oldmask = umask($6); 6381592Srgrimes reply(200, 6391592Srgrimes "UMASK set to %03o (was %03o)", 6401592Srgrimes $6, oldmask); 6411592Srgrimes } 6421592Srgrimes } 6431592Srgrimes } 64470102Sphk | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF 6451592Srgrimes { 6461592Srgrimes if ($4 && ($8 != NULL)) { 647101378Syar if (($6 == -1 ) || ($6 > 0777)) 648101378Syar reply(501, "Bad mode value"); 6491592Srgrimes else if (chmod($8, $6) < 0) 6501592Srgrimes perror_reply(550, $8); 6511592Srgrimes else 6521592Srgrimes reply(200, "CHMOD command successful."); 6531592Srgrimes } 6541592Srgrimes if ($8 != NULL) 6551592Srgrimes free($8); 6561592Srgrimes } 65771278Sjedgar | SITE SP check_login IDLE CRLF 6581592Srgrimes { 65971278Sjedgar if ($3) 66071278Sjedgar reply(200, 66171278Sjedgar "Current IDLE time limit is %d seconds; max %d", 66271278Sjedgar timeout, maxtimeout); 6631592Srgrimes } 66471278Sjedgar | SITE SP check_login IDLE SP NUMBER CRLF 6651592Srgrimes { 66671278Sjedgar if ($3) { 66792272Smaxim if ($6.i < 30 || $6.i > maxtimeout) { 66871278Sjedgar reply(501, 66971278Sjedgar "Maximum IDLE time must be between 30 and %d seconds", 67071278Sjedgar maxtimeout); 67171278Sjedgar } else { 67292272Smaxim timeout = $6.i; 67371278Sjedgar (void) alarm((unsigned) timeout); 67471278Sjedgar reply(200, 67571278Sjedgar "Maximum IDLE time set to %d seconds", 67671278Sjedgar timeout); 67771278Sjedgar } 6781592Srgrimes } 6791592Srgrimes } 68070102Sphk | STOU check_login_ro SP pathname CRLF 6811592Srgrimes { 6821592Srgrimes if ($2 && $4 != NULL) 6831592Srgrimes store($4, "w", 1); 6841592Srgrimes if ($4 != NULL) 6851592Srgrimes free($4); 6861592Srgrimes } 68771278Sjedgar | SYST check_login CRLF 6881592Srgrimes { 689116439Syar if ($2) { 690116439Syar if (hostinfo) 6911592Srgrimes#ifdef BSD 692116439Syar reply(215, "UNIX Type: L%d Version: BSD-%d", 693116439Syar CHAR_BIT, BSD); 6941592Srgrimes#else /* BSD */ 695116439Syar reply(215, "UNIX Type: L%d", CHAR_BIT); 6961592Srgrimes#endif /* BSD */ 697116439Syar else 698116439Syar reply(215, "UNKNOWN Type: L%d", CHAR_BIT); 699116439Syar } 7001592Srgrimes } 7011592Srgrimes 7021592Srgrimes /* 7031592Srgrimes * SIZE is not in RFC959, but Postel has blessed it and 7041592Srgrimes * it will be in the updated RFC. 7051592Srgrimes * 7061592Srgrimes * Return size of file in a format suitable for 7071592Srgrimes * using with RESTART (we just count bytes). 7081592Srgrimes */ 7091592Srgrimes | SIZE check_login SP pathname CRLF 7101592Srgrimes { 7111592Srgrimes if ($2 && $4 != NULL) 7121592Srgrimes sizecmd($4); 7131592Srgrimes if ($4 != NULL) 7141592Srgrimes free($4); 7151592Srgrimes } 7161592Srgrimes 7171592Srgrimes /* 7181592Srgrimes * MDTM is not in RFC959, but Postel has blessed it and 7191592Srgrimes * it will be in the updated RFC. 7201592Srgrimes * 7211592Srgrimes * Return modification time of file as an ISO 3307 7221592Srgrimes * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 7231592Srgrimes * where xxx is the fractional second (of any precision, 7241592Srgrimes * not necessarily 3 digits) 7251592Srgrimes */ 7261592Srgrimes | MDTM check_login SP pathname CRLF 7271592Srgrimes { 7281592Srgrimes if ($2 && $4 != NULL) { 7291592Srgrimes struct stat stbuf; 7301592Srgrimes if (stat($4, &stbuf) < 0) 7311592Srgrimes reply(550, "%s: %s", 7321592Srgrimes $4, strerror(errno)); 7331592Srgrimes else if (!S_ISREG(stbuf.st_mode)) { 7341592Srgrimes reply(550, "%s: not a plain file.", $4); 7351592Srgrimes } else { 7361592Srgrimes struct tm *t; 7371592Srgrimes t = gmtime(&stbuf.st_mtime); 7381592Srgrimes reply(213, 73917435Spst "%04d%02d%02d%02d%02d%02d", 74017435Spst 1900 + t->tm_year, 74117435Spst t->tm_mon+1, t->tm_mday, 7421592Srgrimes t->tm_hour, t->tm_min, t->tm_sec); 7431592Srgrimes } 7441592Srgrimes } 7451592Srgrimes if ($4 != NULL) 7461592Srgrimes free($4); 7471592Srgrimes } 7481592Srgrimes | QUIT CRLF 7491592Srgrimes { 7501592Srgrimes reply(221, "Goodbye."); 7511592Srgrimes dologout(0); 7521592Srgrimes } 753102565Syar | NOTIMPL 754102565Syar { 755102565Syar nack($1); 756102565Syar } 75789935Syar | error 7581592Srgrimes { 75989935Syar yyclearin; /* discard lookahead data */ 76089935Syar yyerrok; /* clear error condition */ 761102565Syar state = CMD; /* 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; 917103949Smike cmd_bytesz = CHAR_BIT; 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 { 96575567Speter if (logged_in && $1) { 966110340Syar char *p; 9671592Srgrimes 968110340Syar /* 969110340Syar * Expand ~user manually since glob(3) 970110340Syar * will return the unexpanded pathname 971110340Syar * if the corresponding file/directory 972110340Syar * doesn't exist yet. Using sole glob(3) 973110340Syar * would break natural commands like 974110340Syar * MKD ~user/newdir 975110340Syar * or 976110340Syar * RNTO ~/newfile 977110340Syar */ 978110340Syar if ((p = exptilde($1)) != NULL) { 979110340Syar $$ = expglob(p); 980110340Syar free(p); 981110340Syar } else 9821592Srgrimes $$ = NULL; 9831592Srgrimes free($1); 9841592Srgrimes } else 9851592Srgrimes $$ = $1; 9861592Srgrimes } 9871592Srgrimes ; 9881592Srgrimes 9891592Srgrimespathstring 9901592Srgrimes : STRING 9911592Srgrimes ; 9921592Srgrimes 9931592Srgrimesoctal_number 9941592Srgrimes : NUMBER 9951592Srgrimes { 9961592Srgrimes int ret, dec, multby, digit; 9971592Srgrimes 9981592Srgrimes /* 9991592Srgrimes * Convert a number that was read as decimal number 10001592Srgrimes * to what it would be if it had been read as octal. 10011592Srgrimes */ 100292272Smaxim dec = $1.i; 10031592Srgrimes multby = 1; 10041592Srgrimes ret = 0; 10051592Srgrimes while (dec) { 10061592Srgrimes digit = dec%10; 10071592Srgrimes if (digit > 7) { 10081592Srgrimes ret = -1; 10091592Srgrimes break; 10101592Srgrimes } 10111592Srgrimes ret += digit * multby; 10121592Srgrimes multby *= 8; 10131592Srgrimes dec /= 10; 10141592Srgrimes } 10151592Srgrimes $$ = ret; 10161592Srgrimes } 10171592Srgrimes ; 10181592Srgrimes 10191592Srgrimes 10201592Srgrimescheck_login 10211592Srgrimes : /* empty */ 10221592Srgrimes { 102370102Sphk $$ = check_login1(); 10241592Srgrimes } 10251592Srgrimes ; 10261592Srgrimes 102770102Sphkcheck_login_epsv 102870102Sphk : /* empty */ 102970102Sphk { 103070102Sphk if (noepsv) { 103170102Sphk reply(500, "EPSV command disabled"); 103270102Sphk $$ = 0; 103370102Sphk } 103470102Sphk else 103570102Sphk $$ = check_login1(); 103670102Sphk } 103770102Sphk ; 103870102Sphk 103970102Sphkcheck_login_ro 104070102Sphk : /* empty */ 104170102Sphk { 104270102Sphk if (readonly) { 104372710Sdes reply(550, "Permission denied."); 104470102Sphk $$ = 0; 104570102Sphk } 104670102Sphk else 104770102Sphk $$ = check_login1(); 104870102Sphk } 104970102Sphk ; 105070102Sphk 10511592Srgrimes%% 10521592Srgrimes 10531592Srgrimes#define CMD 0 /* beginning of command */ 10541592Srgrimes#define ARGS 1 /* expect miscellaneous arguments */ 10551592Srgrimes#define STR1 2 /* expect SP followed by STRING */ 10561592Srgrimes#define STR2 3 /* expect STRING */ 10571592Srgrimes#define OSTR 4 /* optional SP then STRING */ 105875556Sgreen#define ZSTR1 5 /* optional SP then optional STRING */ 10591592Srgrimes#define ZSTR2 6 /* optional STRING after SP */ 10601592Srgrimes#define SITECMD 7 /* SITE command */ 10611592Srgrimes#define NSTR 8 /* Number followed by a string */ 10621592Srgrimes 106375560Sjedgar#define MAXGLOBARGS 1000 106475560Sjedgar 1065101034Syar#define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */ 1066101034Syar 10671592Srgrimesstruct tab { 10681592Srgrimes char *name; 10691592Srgrimes short token; 10701592Srgrimes short state; 10711592Srgrimes short implemented; /* 1 if command is implemented */ 10721592Srgrimes char *help; 10731592Srgrimes}; 10741592Srgrimes 10751592Srgrimesstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 10761592Srgrimes { "USER", USER, STR1, 1, "<sp> username" }, 107775556Sgreen { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" }, 10781592Srgrimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 10791592Srgrimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 10801592Srgrimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 10811592Srgrimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 1082101806Syar { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" }, 108356668Sshin { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 108456668Sshin { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 10851592Srgrimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 108656668Sshin { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 108756668Sshin { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 1088101806Syar { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" }, 10891592Srgrimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 10901592Srgrimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 10911592Srgrimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 10921592Srgrimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 10931592Srgrimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 10941592Srgrimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 10951592Srgrimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 10961592Srgrimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 10971592Srgrimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 10981592Srgrimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 10991592Srgrimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 11001592Srgrimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 11011592Srgrimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 11021592Srgrimes { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 11031592Srgrimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 11041592Srgrimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 11051592Srgrimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 11061592Srgrimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 11071592Srgrimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 11081592Srgrimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 11091592Srgrimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 11101592Srgrimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 11111592Srgrimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 11121592Srgrimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 11131592Srgrimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 11141592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11151592Srgrimes { "NOOP", NOOP, ARGS, 1, "" }, 11161592Srgrimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 11171592Srgrimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 11181592Srgrimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 11191592Srgrimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 11201592Srgrimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 11211592Srgrimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 11221592Srgrimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11231592Srgrimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11241592Srgrimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 11251592Srgrimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 11261592Srgrimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 11271592Srgrimes { NULL, 0, 0, 0, 0 } 11281592Srgrimes}; 11291592Srgrimes 11301592Srgrimesstruct tab sitetab[] = { 113175535Sphk { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" }, 11321592Srgrimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 11331592Srgrimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 11341592Srgrimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 11351592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11361592Srgrimes { NULL, 0, 0, 0, 0 } 11371592Srgrimes}; 11381592Srgrimes 113990148Simpstatic char *copy(char *); 1140110340Syarstatic char *expglob(char *); 1141110340Syarstatic char *exptilde(char *); 114290148Simpstatic void help(struct tab *, char *); 11431592Srgrimesstatic struct tab * 114490148Simp lookup(struct tab *, char *); 114590148Simpstatic int port_check(const char *); 114690148Simpstatic int port_check_v6(const char *); 114790148Simpstatic void sizecmd(char *); 114890148Simpstatic void toolong(int); 114990148Simpstatic void v4map_data_dest(void); 115090148Simpstatic int yylex(void); 11511592Srgrimes 11521592Srgrimesstatic struct tab * 115390148Simplookup(struct tab *p, char *cmd) 11541592Srgrimes{ 11551592Srgrimes 11561592Srgrimes for (; p->name != NULL; p++) 11571592Srgrimes if (strcmp(cmd, p->name) == 0) 11581592Srgrimes return (p); 11591592Srgrimes return (0); 11601592Srgrimes} 11611592Srgrimes 11621592Srgrimes#include <arpa/telnet.h> 11631592Srgrimes 11641592Srgrimes/* 11651592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes. 11661592Srgrimes */ 11671592Srgrimeschar * 116890148Simpgetline(char *s, int n, FILE *iop) 11691592Srgrimes{ 11701592Srgrimes int c; 11711592Srgrimes register char *cs; 1172117352Syar sigset_t sset, osset; 11731592Srgrimes 11741592Srgrimes cs = s; 11751592Srgrimes/* tmpline may contain saved command from urgent mode interruption */ 11761592Srgrimes for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 11771592Srgrimes *cs++ = tmpline[c]; 11781592Srgrimes if (tmpline[c] == '\n') { 11791592Srgrimes *cs++ = '\0'; 118076096Smarkm if (ftpdebug) 11811592Srgrimes syslog(LOG_DEBUG, "command: %s", s); 11821592Srgrimes tmpline[0] = '\0'; 11831592Srgrimes return(s); 11841592Srgrimes } 11851592Srgrimes if (c == 0) 11861592Srgrimes tmpline[0] = '\0'; 11871592Srgrimes } 1188117352Syar /* SIGURG would interrupt stdio if not blocked during the read loop */ 1189117352Syar sigemptyset(&sset); 1190117352Syar sigaddset(&sset, SIGURG); 1191117352Syar sigprocmask(SIG_BLOCK, &sset, &osset); 11921592Srgrimes while ((c = getc(iop)) != EOF) { 11931592Srgrimes c &= 0377; 11941592Srgrimes if (c == IAC) { 1195117351Syar if ((c = getc(iop)) == EOF) 1196117351Syar goto got_eof; 11971592Srgrimes c &= 0377; 11981592Srgrimes switch (c) { 11991592Srgrimes case WILL: 12001592Srgrimes case WONT: 1201117351Syar if ((c = getc(iop)) == EOF) 1202117351Syar goto got_eof; 12031592Srgrimes printf("%c%c%c", IAC, DONT, 0377&c); 12041592Srgrimes (void) fflush(stdout); 12051592Srgrimes continue; 12061592Srgrimes case DO: 12071592Srgrimes case DONT: 1208117351Syar if ((c = getc(iop)) == EOF) 1209117351Syar goto got_eof; 12101592Srgrimes printf("%c%c%c", IAC, WONT, 0377&c); 12111592Srgrimes (void) fflush(stdout); 12121592Srgrimes continue; 12131592Srgrimes case IAC: 12141592Srgrimes break; 12151592Srgrimes default: 12161592Srgrimes continue; /* ignore command */ 12171592Srgrimes } 12181592Srgrimes } 12191592Srgrimes *cs++ = c; 12201592Srgrimes if (--n <= 0 || c == '\n') 12211592Srgrimes break; 12221592Srgrimes } 1223117351Syargot_eof: 1224117352Syar sigprocmask(SIG_SETMASK, &osset, NULL); 12251592Srgrimes if (c == EOF && cs == s) 12261592Srgrimes return (NULL); 12271592Srgrimes *cs++ = '\0'; 122876096Smarkm if (ftpdebug) { 12291592Srgrimes if (!guest && strncasecmp("pass ", s, 5) == 0) { 12301592Srgrimes /* Don't syslog passwords */ 12311592Srgrimes syslog(LOG_DEBUG, "command: %.5s ???", s); 12321592Srgrimes } else { 12331592Srgrimes register char *cp; 12341592Srgrimes register int len; 12351592Srgrimes 12361592Srgrimes /* Don't syslog trailing CR-LF */ 12371592Srgrimes len = strlen(s); 12381592Srgrimes cp = s + len - 1; 12391592Srgrimes while (cp >= s && (*cp == '\n' || *cp == '\r')) { 12401592Srgrimes --cp; 12411592Srgrimes --len; 12421592Srgrimes } 12431592Srgrimes syslog(LOG_DEBUG, "command: %.*s", len, s); 12441592Srgrimes } 12451592Srgrimes } 12461592Srgrimes return (s); 12471592Srgrimes} 12481592Srgrimes 12491592Srgrimesstatic void 125090148Simptoolong(int signo) 12511592Srgrimes{ 12521592Srgrimes 12531592Srgrimes reply(421, 12541592Srgrimes "Timeout (%d seconds): closing control connection.", timeout); 12551592Srgrimes if (logging) 12561592Srgrimes syslog(LOG_INFO, "User %s timed out after %d seconds", 12571592Srgrimes (pw ? pw -> pw_name : "unknown"), timeout); 12581592Srgrimes dologout(1); 12591592Srgrimes} 12601592Srgrimes 12611592Srgrimesstatic int 126290148Simpyylex(void) 12631592Srgrimes{ 126489935Syar static int cpos; 12651592Srgrimes char *cp, *cp2; 12661592Srgrimes struct tab *p; 12671592Srgrimes int n; 12681592Srgrimes char c; 12691592Srgrimes 12701592Srgrimes for (;;) { 12711592Srgrimes switch (state) { 12721592Srgrimes 12731592Srgrimes case CMD: 12741592Srgrimes (void) signal(SIGALRM, toolong); 12751592Srgrimes (void) alarm((unsigned) timeout); 12761592Srgrimes if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 12771592Srgrimes reply(221, "You could at least say goodbye."); 12781592Srgrimes dologout(0); 12791592Srgrimes } 12801592Srgrimes (void) alarm(0); 12811592Srgrimes#ifdef SETPROCTITLE 128229574Sphk if (strncasecmp(cbuf, "PASS", 4) != 0) 12831592Srgrimes setproctitle("%s: %s", proctitle, cbuf); 12841592Srgrimes#endif /* SETPROCTITLE */ 12851592Srgrimes if ((cp = strchr(cbuf, '\r'))) { 12861592Srgrimes *cp++ = '\n'; 12871592Srgrimes *cp = '\0'; 12881592Srgrimes } 12891592Srgrimes if ((cp = strpbrk(cbuf, " \n"))) 12901592Srgrimes cpos = cp - cbuf; 12911592Srgrimes if (cpos == 0) 12921592Srgrimes cpos = 4; 12931592Srgrimes c = cbuf[cpos]; 12941592Srgrimes cbuf[cpos] = '\0'; 12951592Srgrimes upper(cbuf); 12961592Srgrimes p = lookup(cmdtab, cbuf); 12971592Srgrimes cbuf[cpos] = c; 12983776Spst if (p != 0) { 1299102565Syar yylval.s = p->name; 1300102565Syar if (!p->implemented) 1301102565Syar return (NOTIMPL); /* state remains CMD */ 13021592Srgrimes state = p->state; 13031592Srgrimes return (p->token); 13041592Srgrimes } 13051592Srgrimes break; 13061592Srgrimes 13071592Srgrimes case SITECMD: 13081592Srgrimes if (cbuf[cpos] == ' ') { 13091592Srgrimes cpos++; 13101592Srgrimes return (SP); 13111592Srgrimes } 13121592Srgrimes cp = &cbuf[cpos]; 13131592Srgrimes if ((cp2 = strpbrk(cp, " \n"))) 13141592Srgrimes cpos = cp2 - cbuf; 13151592Srgrimes c = cbuf[cpos]; 13161592Srgrimes cbuf[cpos] = '\0'; 13171592Srgrimes upper(cp); 13181592Srgrimes p = lookup(sitetab, cp); 13191592Srgrimes cbuf[cpos] = c; 13203777Spst if (guest == 0 && p != 0) { 1321102565Syar yylval.s = p->name; 1322102565Syar if (!p->implemented) { 13231592Srgrimes state = CMD; 1324102565Syar return (NOTIMPL); 13251592Srgrimes } 13261592Srgrimes state = p->state; 13271592Srgrimes return (p->token); 13281592Srgrimes } 13291592Srgrimes state = CMD; 13301592Srgrimes break; 13311592Srgrimes 133275556Sgreen case ZSTR1: 13331592Srgrimes case OSTR: 13341592Srgrimes if (cbuf[cpos] == '\n') { 13351592Srgrimes state = CMD; 13361592Srgrimes return (CRLF); 13371592Srgrimes } 13381592Srgrimes /* FALLTHROUGH */ 13391592Srgrimes 13401592Srgrimes case STR1: 13411592Srgrimes dostr1: 13421592Srgrimes if (cbuf[cpos] == ' ') { 13431592Srgrimes cpos++; 134451979Salfred state = state == OSTR ? STR2 : state+1; 13451592Srgrimes return (SP); 13461592Srgrimes } 13471592Srgrimes break; 13481592Srgrimes 13491592Srgrimes case ZSTR2: 13501592Srgrimes if (cbuf[cpos] == '\n') { 13511592Srgrimes state = CMD; 13521592Srgrimes return (CRLF); 13531592Srgrimes } 13541592Srgrimes /* FALLTHROUGH */ 13551592Srgrimes 13561592Srgrimes case STR2: 13571592Srgrimes cp = &cbuf[cpos]; 13581592Srgrimes n = strlen(cp); 13591592Srgrimes cpos += n - 1; 13601592Srgrimes /* 13611592Srgrimes * Make sure the string is nonempty and \n terminated. 13621592Srgrimes */ 13631592Srgrimes if (n > 1 && cbuf[cpos] == '\n') { 13641592Srgrimes cbuf[cpos] = '\0'; 13651592Srgrimes yylval.s = copy(cp); 13661592Srgrimes cbuf[cpos] = '\n'; 13671592Srgrimes state = ARGS; 13681592Srgrimes return (STRING); 13691592Srgrimes } 13701592Srgrimes break; 13711592Srgrimes 13721592Srgrimes case NSTR: 13731592Srgrimes if (cbuf[cpos] == ' ') { 13741592Srgrimes cpos++; 13751592Srgrimes return (SP); 13761592Srgrimes } 13771592Srgrimes if (isdigit(cbuf[cpos])) { 13781592Srgrimes cp = &cbuf[cpos]; 13791592Srgrimes while (isdigit(cbuf[++cpos])) 13801592Srgrimes ; 13811592Srgrimes c = cbuf[cpos]; 13821592Srgrimes cbuf[cpos] = '\0'; 138392272Smaxim yylval.u.i = atoi(cp); 13841592Srgrimes cbuf[cpos] = c; 13851592Srgrimes state = STR1; 13861592Srgrimes return (NUMBER); 13871592Srgrimes } 13881592Srgrimes state = STR1; 13891592Srgrimes goto dostr1; 13901592Srgrimes 13911592Srgrimes case ARGS: 13921592Srgrimes if (isdigit(cbuf[cpos])) { 13931592Srgrimes cp = &cbuf[cpos]; 13941592Srgrimes while (isdigit(cbuf[++cpos])) 13951592Srgrimes ; 13961592Srgrimes c = cbuf[cpos]; 13971592Srgrimes cbuf[cpos] = '\0'; 139892272Smaxim yylval.u.i = atoi(cp); 139992272Smaxim yylval.u.o = strtoull(cp, (char **)NULL, 10); 14001592Srgrimes cbuf[cpos] = c; 14011592Srgrimes return (NUMBER); 14021592Srgrimes } 140356668Sshin if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 140456668Sshin && !isalnum(cbuf[cpos + 3])) { 140556668Sshin cpos += 3; 140656668Sshin return ALL; 140756668Sshin } 14081592Srgrimes switch (cbuf[cpos++]) { 14091592Srgrimes 14101592Srgrimes case '\n': 14111592Srgrimes state = CMD; 14121592Srgrimes return (CRLF); 14131592Srgrimes 14141592Srgrimes case ' ': 14151592Srgrimes return (SP); 14161592Srgrimes 14171592Srgrimes case ',': 14181592Srgrimes return (COMMA); 14191592Srgrimes 14201592Srgrimes case 'A': 14211592Srgrimes case 'a': 14221592Srgrimes return (A); 14231592Srgrimes 14241592Srgrimes case 'B': 14251592Srgrimes case 'b': 14261592Srgrimes return (B); 14271592Srgrimes 14281592Srgrimes case 'C': 14291592Srgrimes case 'c': 14301592Srgrimes return (C); 14311592Srgrimes 14321592Srgrimes case 'E': 14331592Srgrimes case 'e': 14341592Srgrimes return (E); 14351592Srgrimes 14361592Srgrimes case 'F': 14371592Srgrimes case 'f': 14381592Srgrimes return (F); 14391592Srgrimes 14401592Srgrimes case 'I': 14411592Srgrimes case 'i': 14421592Srgrimes return (I); 14431592Srgrimes 14441592Srgrimes case 'L': 14451592Srgrimes case 'l': 14461592Srgrimes return (L); 14471592Srgrimes 14481592Srgrimes case 'N': 14491592Srgrimes case 'n': 14501592Srgrimes return (N); 14511592Srgrimes 14521592Srgrimes case 'P': 14531592Srgrimes case 'p': 14541592Srgrimes return (P); 14551592Srgrimes 14561592Srgrimes case 'R': 14571592Srgrimes case 'r': 14581592Srgrimes return (R); 14591592Srgrimes 14601592Srgrimes case 'S': 14611592Srgrimes case 's': 14621592Srgrimes return (S); 14631592Srgrimes 14641592Srgrimes case 'T': 14651592Srgrimes case 't': 14661592Srgrimes return (T); 14671592Srgrimes 14681592Srgrimes } 14691592Srgrimes break; 14701592Srgrimes 14711592Srgrimes default: 147276096Smarkm fatalerror("Unknown state in scanner."); 14731592Srgrimes } 14741592Srgrimes state = CMD; 147589935Syar return (LEXERR); 14761592Srgrimes } 14771592Srgrimes} 14781592Srgrimes 14791592Srgrimesvoid 148090148Simpupper(char *s) 14811592Srgrimes{ 14821592Srgrimes while (*s != '\0') { 14831592Srgrimes if (islower(*s)) 14841592Srgrimes *s = toupper(*s); 14851592Srgrimes s++; 14861592Srgrimes } 14871592Srgrimes} 14881592Srgrimes 14891592Srgrimesstatic char * 149090148Simpcopy(char *s) 14911592Srgrimes{ 14921592Srgrimes char *p; 14931592Srgrimes 14941592Srgrimes p = malloc((unsigned) strlen(s) + 1); 14951592Srgrimes if (p == NULL) 149676096Smarkm fatalerror("Ran out of memory."); 14971592Srgrimes (void) strcpy(p, s); 14981592Srgrimes return (p); 14991592Srgrimes} 15001592Srgrimes 15011592Srgrimesstatic void 150290148Simphelp(struct tab *ctab, char *s) 15031592Srgrimes{ 15041592Srgrimes struct tab *c; 15051592Srgrimes int width, NCMDS; 15061592Srgrimes char *type; 15071592Srgrimes 15081592Srgrimes if (ctab == sitetab) 15091592Srgrimes type = "SITE "; 15101592Srgrimes else 15111592Srgrimes type = ""; 15121592Srgrimes width = 0, NCMDS = 0; 15131592Srgrimes for (c = ctab; c->name != NULL; c++) { 15141592Srgrimes int len = strlen(c->name); 15151592Srgrimes 15161592Srgrimes if (len > width) 15171592Srgrimes width = len; 15181592Srgrimes NCMDS++; 15191592Srgrimes } 15201592Srgrimes width = (width + 8) &~ 7; 15211592Srgrimes if (s == 0) { 15221592Srgrimes int i, j, w; 15231592Srgrimes int columns, lines; 15241592Srgrimes 15251592Srgrimes lreply(214, "The following %scommands are recognized %s.", 15261592Srgrimes type, "(* =>'s unimplemented)"); 15271592Srgrimes columns = 76 / width; 15281592Srgrimes if (columns == 0) 15291592Srgrimes columns = 1; 15301592Srgrimes lines = (NCMDS + columns - 1) / columns; 15311592Srgrimes for (i = 0; i < lines; i++) { 15321592Srgrimes printf(" "); 15331592Srgrimes for (j = 0; j < columns; j++) { 15341592Srgrimes c = ctab + j * lines + i; 15351592Srgrimes printf("%s%c", c->name, 15361592Srgrimes c->implemented ? ' ' : '*'); 15371592Srgrimes if (c + lines >= &ctab[NCMDS]) 15381592Srgrimes break; 15391592Srgrimes w = strlen(c->name) + 1; 15401592Srgrimes while (w < width) { 15411592Srgrimes putchar(' '); 15421592Srgrimes w++; 15431592Srgrimes } 15441592Srgrimes } 15451592Srgrimes printf("\r\n"); 15461592Srgrimes } 15471592Srgrimes (void) fflush(stdout); 1548110037Syar if (hostinfo) 1549110037Syar reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1550110037Syar else 1551110037Syar reply(214, "End."); 15521592Srgrimes return; 15531592Srgrimes } 15541592Srgrimes upper(s); 15551592Srgrimes c = lookup(ctab, s); 15561592Srgrimes if (c == (struct tab *)0) { 15571592Srgrimes reply(502, "Unknown command %s.", s); 15581592Srgrimes return; 15591592Srgrimes } 15601592Srgrimes if (c->implemented) 15611592Srgrimes reply(214, "Syntax: %s%s %s", type, c->name, c->help); 15621592Srgrimes else 15631592Srgrimes reply(214, "%s%-*s\t%s; unimplemented.", type, width, 15641592Srgrimes c->name, c->help); 15651592Srgrimes} 15661592Srgrimes 15671592Srgrimesstatic void 156890148Simpsizecmd(char *filename) 15691592Srgrimes{ 15701592Srgrimes switch (type) { 15711592Srgrimes case TYPE_L: 15721592Srgrimes case TYPE_I: { 15731592Srgrimes struct stat stbuf; 157463350Sdes if (stat(filename, &stbuf) < 0) 157563350Sdes perror_reply(550, filename); 157663350Sdes else if (!S_ISREG(stbuf.st_mode)) 15771592Srgrimes reply(550, "%s: not a plain file.", filename); 15781592Srgrimes else 15791592Srgrimes reply(213, "%qu", stbuf.st_size); 15801592Srgrimes break; } 15811592Srgrimes case TYPE_A: { 15821592Srgrimes FILE *fin; 15831592Srgrimes int c; 15841592Srgrimes off_t count; 15851592Srgrimes struct stat stbuf; 15861592Srgrimes fin = fopen(filename, "r"); 15871592Srgrimes if (fin == NULL) { 15881592Srgrimes perror_reply(550, filename); 15891592Srgrimes return; 15901592Srgrimes } 159163350Sdes if (fstat(fileno(fin), &stbuf) < 0) { 159263350Sdes perror_reply(550, filename); 159363350Sdes (void) fclose(fin); 159463350Sdes return; 159563350Sdes } else if (!S_ISREG(stbuf.st_mode)) { 15961592Srgrimes reply(550, "%s: not a plain file.", filename); 15971592Srgrimes (void) fclose(fin); 15981592Srgrimes return; 1599101034Syar } else if (stbuf.st_size > MAXASIZE) { 1600101034Syar reply(550, "%s: too large for type A SIZE.", filename); 1601101034Syar (void) fclose(fin); 1602101034Syar return; 16031592Srgrimes } 16041592Srgrimes 16051592Srgrimes count = 0; 16061592Srgrimes while((c=getc(fin)) != EOF) { 16071592Srgrimes if (c == '\n') /* will get expanded to \r\n */ 16081592Srgrimes count++; 16091592Srgrimes count++; 16101592Srgrimes } 16111592Srgrimes (void) fclose(fin); 16121592Srgrimes 16131592Srgrimes reply(213, "%qd", count); 16141592Srgrimes break; } 16151592Srgrimes default: 1616100684Syar reply(504, "SIZE not implemented for type %s.", 1617100684Syar typenames[type]); 16181592Srgrimes } 16191592Srgrimes} 162056668Sshin 162156668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 162256668Sshinstatic int 162390148Simpport_check(const char *pcmd) 162456668Sshin{ 162556668Sshin if (his_addr.su_family == AF_INET) { 162656668Sshin if (data_dest.su_family != AF_INET) { 162756668Sshin usedefault = 1; 162856668Sshin reply(500, "Invalid address rejected."); 162956668Sshin return 1; 163056668Sshin } 163156668Sshin if (paranoid && 163256668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 163356668Sshin memcmp(&data_dest.su_sin.sin_addr, 163456668Sshin &his_addr.su_sin.sin_addr, 163556668Sshin sizeof(data_dest.su_sin.sin_addr)))) { 163656668Sshin usedefault = 1; 163756668Sshin reply(500, "Illegal PORT range rejected."); 163856668Sshin } else { 163956668Sshin usedefault = 0; 164056668Sshin if (pdata >= 0) { 164156668Sshin (void) close(pdata); 164256668Sshin pdata = -1; 164356668Sshin } 164456668Sshin reply(200, "%s command successful.", pcmd); 164556668Sshin } 164656668Sshin return 1; 164756668Sshin } 164856668Sshin return 0; 164956668Sshin} 165056668Sshin 165170102Sphkstatic int 165290148Simpcheck_login1(void) 165370102Sphk{ 165470102Sphk if (logged_in) 165570102Sphk return 1; 165670102Sphk else { 165770102Sphk reply(530, "Please login with USER and PASS."); 165870102Sphk return 0; 165970102Sphk } 166070102Sphk} 166170102Sphk 1662110340Syar/* 1663110340Syar * Replace leading "~user" in a pathname by the user's login directory. 1664110340Syar * Returned string will be in a freshly malloced buffer unless it's NULL. 1665110340Syar */ 1666110340Syarstatic char * 1667110340Syarexptilde(char *s) 1668110340Syar{ 1669110340Syar char *p, *q; 1670110340Syar char *path, *user; 1671110340Syar struct passwd *ppw; 1672110340Syar 1673110340Syar if ((p = strdup(s)) == NULL) 1674110340Syar return (NULL); 1675110340Syar if (*p != '~') 1676110340Syar return (p); 1677110340Syar 1678110340Syar user = p + 1; /* skip tilde */ 1679110340Syar if ((path = strchr(p, '/')) != NULL) 1680110340Syar *(path++) = '\0'; /* separate ~user from the rest of path */ 1681110378Syar if (*user == '\0') /* no user specified, use the current user */ 1682110378Syar user = pw->pw_name; 1683110378Syar /* read passwd even for the current user since we may be chrooted */ 1684110378Syar if ((ppw = getpwnam(user)) != NULL) { 1685110340Syar /* user found, substitute login directory for ~user */ 1686110340Syar if (path) 1687110340Syar asprintf(&q, "%s/%s", ppw->pw_dir, path); 1688110340Syar else 1689110340Syar q = strdup(ppw->pw_dir); 1690110340Syar free(p); 1691110340Syar p = q; 1692110340Syar } else { 1693110340Syar /* user not found, undo the damage */ 1694110340Syar if (path) 1695110340Syar path[-1] = '/'; 1696110340Syar } 1697110340Syar return (p); 1698110340Syar} 1699110340Syar 1700110340Syar/* 1701110340Syar * Expand glob(3) patterns possibly present in a pathname. 1702110340Syar * Avoid expanding to a pathname including '\r' or '\n' in order to 1703110340Syar * not disrupt the FTP protocol. 1704110340Syar * The expansion found must be unique. 1705110340Syar * Return the result as a malloced string, or NULL if an error occured. 1706110340Syar * 1707110340Syar * Problem: this production is used for all pathname 1708110340Syar * processing, but only gives a 550 error reply. 1709110340Syar * This is a valid reply in some cases but not in others. 1710110340Syar */ 1711110340Syarstatic char * 1712110340Syarexpglob(char *s) 1713110340Syar{ 1714110340Syar char *p, **pp, *rval; 1715110340Syar int flags = GLOB_BRACE | GLOB_NOCHECK; 1716110340Syar int n; 1717110340Syar glob_t gl; 1718110340Syar 1719110340Syar memset(&gl, 0, sizeof(gl)); 1720110340Syar flags |= GLOB_LIMIT; 1721110340Syar gl.gl_matchc = MAXGLOBARGS; 1722110340Syar if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) { 1723110340Syar for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++) 1724110340Syar if (*(*pp + strcspn(*pp, "\r\n")) == '\0') { 1725110340Syar p = *pp; 1726110340Syar n++; 1727110340Syar } 1728110340Syar if (n == 0) 1729110340Syar rval = strdup(s); 1730110340Syar else if (n == 1) 1731110340Syar rval = strdup(p); 1732110340Syar else { 1733110340Syar reply(550, "ambiguous"); 1734110340Syar rval = NULL; 1735110340Syar } 1736110340Syar } else { 1737110340Syar reply(550, "wildcard expansion error"); 1738110340Syar rval = NULL; 1739110340Syar } 1740110340Syar globfree(&gl); 1741110340Syar return (rval); 1742110340Syar} 1743110340Syar 174456668Sshin#ifdef INET6 174556668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 174656668Sshinstatic int 174790148Simpport_check_v6(const char *pcmd) 174856668Sshin{ 174956668Sshin if (his_addr.su_family == AF_INET6) { 175056668Sshin if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 175156668Sshin /* Convert data_dest into v4 mapped sockaddr.*/ 175256668Sshin v4map_data_dest(); 175356668Sshin if (data_dest.su_family != AF_INET6) { 175456668Sshin usedefault = 1; 175556668Sshin reply(500, "Invalid address rejected."); 175656668Sshin return 1; 175756668Sshin } 175856668Sshin if (paranoid && 175956668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 176056668Sshin memcmp(&data_dest.su_sin6.sin6_addr, 176156668Sshin &his_addr.su_sin6.sin6_addr, 176256668Sshin sizeof(data_dest.su_sin6.sin6_addr)))) { 176356668Sshin usedefault = 1; 176456668Sshin reply(500, "Illegal PORT range rejected."); 176556668Sshin } else { 176656668Sshin usedefault = 0; 176756668Sshin if (pdata >= 0) { 176856668Sshin (void) close(pdata); 176956668Sshin pdata = -1; 177056668Sshin } 177156668Sshin reply(200, "%s command successful.", pcmd); 177256668Sshin } 177356668Sshin return 1; 177456668Sshin } 177556668Sshin return 0; 177656668Sshin} 177756668Sshin 177856668Sshinstatic void 177990148Simpv4map_data_dest(void) 178056668Sshin{ 178156668Sshin struct in_addr savedaddr; 178256668Sshin int savedport; 178356668Sshin 178456668Sshin if (data_dest.su_family != AF_INET) { 178556668Sshin usedefault = 1; 178656668Sshin reply(500, "Invalid address rejected."); 178756668Sshin return; 178856668Sshin } 178956668Sshin 179056668Sshin savedaddr = data_dest.su_sin.sin_addr; 179156668Sshin savedport = data_dest.su_port; 179256668Sshin 179356668Sshin memset(&data_dest, 0, sizeof(data_dest)); 179456668Sshin data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 179556668Sshin data_dest.su_sin6.sin6_family = AF_INET6; 179656668Sshin data_dest.su_sin6.sin6_port = savedport; 179756668Sshin memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 179856668Sshin memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 179956668Sshin (caddr_t)&savedaddr, sizeof(savedaddr)); 180056668Sshin} 180156668Sshin#endif 1802