ftpcmd.y revision 137811
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 137811 2004-11-17 11:52:41Z 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> 67132929Syar#include <stdint.h> 681592Srgrimes#include <stdio.h> 691592Srgrimes#include <stdlib.h> 701592Srgrimes#include <string.h> 711592Srgrimes#include <syslog.h> 721592Srgrimes#include <time.h> 731592Srgrimes#include <unistd.h> 741592Srgrimes 751592Srgrimes#include "extern.h" 76109380Syar#include "pathnames.h" 771592Srgrimes 7856668Sshinextern union sockunion data_dest, his_addr; 79110037Syarextern int hostinfo; 801592Srgrimesextern int logged_in; 811592Srgrimesextern struct passwd *pw; 821592Srgrimesextern int guest; 83110036Syarextern char *homedir; 8417435Spstextern int paranoid; 851592Srgrimesextern int logging; 861592Srgrimesextern int type; 871592Srgrimesextern int form; 8876096Smarkmextern int ftpdebug; 891592Srgrimesextern int timeout; 901592Srgrimesextern int maxtimeout; 911592Srgrimesextern int pdata; 9227650Sdavidnextern char *hostname; 931592Srgrimesextern char proctitle[]; 941592Srgrimesextern int usedefault; 951592Srgrimesextern char tmpline[]; 9670102Sphkextern int readonly; 9770102Sphkextern int noepsv; 9882460Snikextern int noretr; 9982796Ssheldonhextern int noguestretr; 100100684Syarextern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */ 1011592Srgrimes 1021592Srgrimesoff_t restart_point; 1031592Srgrimes 1041592Srgrimesstatic int cmd_type; 1051592Srgrimesstatic int cmd_form; 1061592Srgrimesstatic int cmd_bytesz; 10789935Syarstatic int state; 1081592Srgrimeschar cbuf[512]; 109132931Syarchar *fromname = NULL; 1101592Srgrimes 11156668Sshinextern int epsvall; 11256668Sshin 1131592Srgrimes%} 1141592Srgrimes 1151592Srgrimes%union { 11692272Smaxim struct { 11792272Smaxim off_t o; 11892272Smaxim int i; 11992272Smaxim } u; 1201592Srgrimes char *s; 1211592Srgrimes} 1221592Srgrimes 1231592Srgrimes%token 1241592Srgrimes A B C E F I 1251592Srgrimes L N P R S T 12656668Sshin ALL 1271592Srgrimes 1281592Srgrimes SP CRLF COMMA 1291592Srgrimes 1301592Srgrimes USER PASS ACCT REIN QUIT PORT 1311592Srgrimes PASV TYPE STRU MODE RETR STOR 1321592Srgrimes APPE MLFL MAIL MSND MSOM MSAM 1331592Srgrimes MRSQ MRCP ALLO REST RNFR RNTO 1341592Srgrimes ABOR DELE CWD LIST NLST SITE 1351592Srgrimes STAT HELP NOOP MKD RMD PWD 1361592Srgrimes CDUP STOU SMNT SYST SIZE MDTM 13756668Sshin LPRT LPSV EPRT EPSV 1381592Srgrimes 13975535Sphk UMASK IDLE CHMOD MDFIVE 1401592Srgrimes 141102565Syar LEXERR NOTIMPL 1421592Srgrimes 1431592Srgrimes%token <s> STRING 14492272Smaxim%token <u> NUMBER 1451592Srgrimes 14692272Smaxim%type <u.i> check_login octal_number byte_size 14792272Smaxim%type <u.i> check_login_ro check_login_epsv 14892272Smaxim%type <u.i> struct_code mode_code type_code form_code 14975567Speter%type <s> pathstring pathname password username 150102565Syar%type <s> ALL NOTIMPL 1511592Srgrimes 1521592Srgrimes%start cmd_list 1531592Srgrimes 1541592Srgrimes%% 1551592Srgrimes 1561592Srgrimescmd_list 1571592Srgrimes : /* empty */ 1581592Srgrimes | cmd_list cmd 1591592Srgrimes { 16088935Sdwmalone if (fromname) 16188935Sdwmalone free(fromname); 162132931Syar fromname = NULL; 163132930Syar restart_point = 0; 1641592Srgrimes } 1651592Srgrimes | cmd_list rcmd 1661592Srgrimes ; 1671592Srgrimes 1681592Srgrimescmd 1691592Srgrimes : USER SP username CRLF 1701592Srgrimes { 1711592Srgrimes user($3); 1721592Srgrimes free($3); 1731592Srgrimes } 1741592Srgrimes | PASS SP password CRLF 1751592Srgrimes { 1761592Srgrimes pass($3); 1771592Srgrimes free($3); 1781592Srgrimes } 17975556Sgreen | PASS CRLF 18075556Sgreen { 18175556Sgreen pass(""); 18275556Sgreen } 18317433Spst | PORT check_login SP host_port CRLF 1841592Srgrimes { 18556668Sshin if (epsvall) { 18656668Sshin reply(501, "no PORT allowed after EPSV ALL"); 18756668Sshin goto port_done; 18856668Sshin } 18956668Sshin if (!$2) 19056668Sshin goto port_done; 19156668Sshin if (port_check("PORT") == 1) 19256668Sshin goto port_done; 19356668Sshin#ifdef INET6 19456668Sshin if ((his_addr.su_family != AF_INET6 || 19556668Sshin !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { 19656668Sshin /* shoud never happen */ 19756668Sshin usedefault = 1; 19856668Sshin reply(500, "Invalid address rejected."); 19956668Sshin goto port_done; 20056668Sshin } 20156668Sshin port_check_v6("pcmd"); 20256668Sshin#endif 20356668Sshin port_done: 204132925Syar ; 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: 226132925Syar ; 22756668Sshin } 22856668Sshin | EPRT check_login SP STRING CRLF 22956668Sshin { 23056668Sshin char delim; 23156668Sshin char *tmp = NULL; 23256668Sshin char *p, *q; 23356668Sshin char *result[3]; 23456668Sshin struct addrinfo hints; 23556668Sshin struct addrinfo *res; 23656668Sshin int i; 23756668Sshin 23856668Sshin if (epsvall) { 23956668Sshin reply(501, "no EPRT allowed after EPSV ALL"); 24056668Sshin goto eprt_done; 24156668Sshin } 24256668Sshin if (!$2) 24356668Sshin goto eprt_done; 24456668Sshin 24556668Sshin memset(&data_dest, 0, sizeof(data_dest)); 24656668Sshin tmp = strdup($4); 24776096Smarkm if (ftpdebug) 24856668Sshin syslog(LOG_DEBUG, "%s", tmp); 24956668Sshin if (!tmp) { 25076096Smarkm fatalerror("not enough core"); 25156668Sshin /*NOTREACHED*/ 25256668Sshin } 25356668Sshin p = tmp; 25456668Sshin delim = p[0]; 25556668Sshin p++; 25656668Sshin memset(result, 0, sizeof(result)); 25756668Sshin for (i = 0; i < 3; i++) { 25856668Sshin q = strchr(p, delim); 25956668Sshin if (!q || *q != delim) { 26056668Sshin parsefail: 26156668Sshin reply(500, 26256668Sshin "Invalid argument, rejected."); 26356668Sshin if (tmp) 26456668Sshin free(tmp); 26517433Spst usedefault = 1; 26656668Sshin goto eprt_done; 26717433Spst } 26856668Sshin *q++ = '\0'; 26956668Sshin result[i] = p; 27076096Smarkm if (ftpdebug) 27156668Sshin syslog(LOG_DEBUG, "%d: %s", i, p); 27256668Sshin p = q; 2731592Srgrimes } 27456668Sshin 27556668Sshin /* some more sanity check */ 27656668Sshin p = result[0]; 27756668Sshin while (*p) { 27856668Sshin if (!isdigit(*p)) 27956668Sshin goto parsefail; 28056668Sshin p++; 28156668Sshin } 28256668Sshin p = result[2]; 28356668Sshin while (*p) { 28456668Sshin if (!isdigit(*p)) 28556668Sshin goto parsefail; 28656668Sshin p++; 28756668Sshin } 28856668Sshin 28956668Sshin /* grab address */ 29056668Sshin memset(&hints, 0, sizeof(hints)); 29156668Sshin if (atoi(result[0]) == 1) 29256668Sshin hints.ai_family = PF_INET; 29356668Sshin#ifdef INET6 29456668Sshin else if (atoi(result[0]) == 2) 29556668Sshin hints.ai_family = PF_INET6; 29656668Sshin#endif 29756668Sshin else 29856668Sshin hints.ai_family = PF_UNSPEC; /*XXX*/ 29956668Sshin hints.ai_socktype = SOCK_STREAM; 30056668Sshin i = getaddrinfo(result[1], result[2], &hints, &res); 30156668Sshin if (i) 30256668Sshin goto parsefail; 30356668Sshin memcpy(&data_dest, res->ai_addr, res->ai_addrlen); 30456668Sshin#ifdef INET6 30556668Sshin if (his_addr.su_family == AF_INET6 30656668Sshin && data_dest.su_family == AF_INET6) { 30756668Sshin /* XXX more sanity checks! */ 30856668Sshin data_dest.su_sin6.sin6_scope_id = 30956668Sshin his_addr.su_sin6.sin6_scope_id; 31056668Sshin } 31156668Sshin#endif 31256668Sshin free(tmp); 31356668Sshin tmp = NULL; 31456668Sshin 31556668Sshin if (port_check("EPRT") == 1) 31656668Sshin goto eprt_done; 31756668Sshin#ifdef INET6 31856668Sshin if (his_addr.su_family != AF_INET6) { 31956668Sshin usedefault = 1; 32056668Sshin reply(500, "Invalid address rejected."); 32156668Sshin goto eprt_done; 32256668Sshin } 32356668Sshin if (port_check_v6("EPRT") == 1) 32456668Sshin goto eprt_done; 32556668Sshin#endif 32688935Sdwmalone eprt_done: 32788935Sdwmalone free($4); 3281592Srgrimes } 32917433Spst | PASV check_login CRLF 3301592Srgrimes { 33156668Sshin if (epsvall) 33256668Sshin reply(501, "no PASV allowed after EPSV ALL"); 33356668Sshin else if ($2) 33417433Spst passive(); 3351592Srgrimes } 33656668Sshin | LPSV check_login CRLF 33756668Sshin { 33856668Sshin if (epsvall) 33956668Sshin reply(501, "no LPSV allowed after EPSV ALL"); 34056668Sshin else if ($2) 34156668Sshin long_passive("LPSV", PF_UNSPEC); 34256668Sshin } 34370102Sphk | EPSV check_login_epsv SP NUMBER CRLF 34456668Sshin { 34556668Sshin if ($2) { 34656668Sshin int pf; 34792272Smaxim switch ($4.i) { 34856668Sshin case 1: 34956668Sshin pf = PF_INET; 35056668Sshin break; 35156668Sshin#ifdef INET6 35256668Sshin case 2: 35356668Sshin pf = PF_INET6; 35456668Sshin break; 35556668Sshin#endif 35656668Sshin default: 35756668Sshin pf = -1; /*junk value*/ 35856668Sshin break; 35956668Sshin } 36056668Sshin long_passive("EPSV", pf); 36156668Sshin } 36256668Sshin } 36370102Sphk | EPSV check_login_epsv SP ALL CRLF 36456668Sshin { 36556668Sshin if ($2) { 36656668Sshin reply(200, 36756668Sshin "EPSV ALL command successful."); 36856668Sshin epsvall++; 36956668Sshin } 37056668Sshin } 37170102Sphk | EPSV check_login_epsv CRLF 37256668Sshin { 37356668Sshin if ($2) 37456668Sshin long_passive("EPSV", PF_UNSPEC); 37556668Sshin } 37671278Sjedgar | TYPE check_login SP type_code CRLF 3771592Srgrimes { 37871278Sjedgar if ($2) { 37971278Sjedgar switch (cmd_type) { 3801592Srgrimes 38171278Sjedgar case TYPE_A: 38271278Sjedgar if (cmd_form == FORM_N) { 38371278Sjedgar reply(200, "Type set to A."); 38471278Sjedgar type = cmd_type; 38571278Sjedgar form = cmd_form; 38671278Sjedgar } else 38771278Sjedgar reply(504, "Form must be N."); 38871278Sjedgar break; 3891592Srgrimes 39071278Sjedgar case TYPE_E: 39171278Sjedgar reply(504, "Type E not implemented."); 39271278Sjedgar break; 3931592Srgrimes 39471278Sjedgar case TYPE_I: 39571278Sjedgar reply(200, "Type set to I."); 39671278Sjedgar type = cmd_type; 39771278Sjedgar break; 3981592Srgrimes 39971278Sjedgar case TYPE_L: 400103949Smike#if CHAR_BIT == 8 40171278Sjedgar if (cmd_bytesz == 8) { 40271278Sjedgar reply(200, 40371278Sjedgar "Type set to L (byte size 8)."); 40471278Sjedgar type = cmd_type; 40571278Sjedgar } else 40671278Sjedgar reply(504, "Byte size must be 8."); 407103949Smike#else /* CHAR_BIT == 8 */ 408103949Smike UNIMPLEMENTED for CHAR_BIT != 8 409103949Smike#endif /* CHAR_BIT == 8 */ 41071278Sjedgar } 4111592Srgrimes } 4121592Srgrimes } 41371278Sjedgar | STRU check_login SP struct_code CRLF 4141592Srgrimes { 41571278Sjedgar if ($2) { 41671278Sjedgar switch ($4) { 4171592Srgrimes 41871278Sjedgar case STRU_F: 41971278Sjedgar reply(200, "STRU F ok."); 42071278Sjedgar break; 4211592Srgrimes 42271278Sjedgar default: 42371278Sjedgar reply(504, "Unimplemented STRU type."); 42471278Sjedgar } 4251592Srgrimes } 4261592Srgrimes } 42771278Sjedgar | MODE check_login SP mode_code CRLF 4281592Srgrimes { 42971278Sjedgar if ($2) { 43071278Sjedgar switch ($4) { 4311592Srgrimes 43271278Sjedgar case MODE_S: 43371278Sjedgar reply(200, "MODE S ok."); 43471278Sjedgar break; 43571278Sjedgar 43671278Sjedgar default: 43771278Sjedgar reply(502, "Unimplemented MODE type."); 43871278Sjedgar } 4391592Srgrimes } 4401592Srgrimes } 44171278Sjedgar | ALLO check_login SP NUMBER CRLF 4421592Srgrimes { 44371278Sjedgar if ($2) { 44471278Sjedgar reply(202, "ALLO command ignored."); 44571278Sjedgar } 4461592Srgrimes } 44771278Sjedgar | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 4481592Srgrimes { 44971278Sjedgar if ($2) { 45071278Sjedgar reply(202, "ALLO command ignored."); 45171278Sjedgar } 4521592Srgrimes } 4531592Srgrimes | RETR check_login SP pathname CRLF 4541592Srgrimes { 45582796Ssheldonh if (noretr || (guest && noguestretr)) 45682460Snik reply(500, "RETR command is disabled"); 45782460Snik else if ($2 && $4 != NULL) 458132931Syar retrieve(NULL, $4); 45982460Snik 4601592Srgrimes if ($4 != NULL) 4611592Srgrimes free($4); 4621592Srgrimes } 46370102Sphk | STOR check_login_ro SP pathname CRLF 4641592Srgrimes { 4651592Srgrimes if ($2 && $4 != NULL) 4661592Srgrimes store($4, "w", 0); 4671592Srgrimes if ($4 != NULL) 4681592Srgrimes free($4); 4691592Srgrimes } 47070102Sphk | APPE check_login_ro SP pathname CRLF 4711592Srgrimes { 4721592Srgrimes if ($2 && $4 != NULL) 4731592Srgrimes store($4, "a", 0); 4741592Srgrimes if ($4 != NULL) 4751592Srgrimes free($4); 4761592Srgrimes } 4771592Srgrimes | NLST check_login CRLF 4781592Srgrimes { 4791592Srgrimes if ($2) 4801592Srgrimes send_file_list("."); 4811592Srgrimes } 482101395Syar | NLST check_login SP pathstring CRLF 4831592Srgrimes { 484101395Syar if ($2) 4851592Srgrimes send_file_list($4); 486101395Syar free($4); 4871592Srgrimes } 4881592Srgrimes | LIST check_login CRLF 4891592Srgrimes { 4901592Srgrimes if ($2) 491109380Syar retrieve(_PATH_LS " -lgA", ""); 4921592Srgrimes } 49375567Speter | LIST check_login SP pathstring CRLF 4941592Srgrimes { 495101395Syar if ($2) 496109380Syar retrieve(_PATH_LS " -lgA %s", $4); 497101395Syar free($4); 4981592Srgrimes } 4991592Srgrimes | STAT check_login SP pathname CRLF 5001592Srgrimes { 5011592Srgrimes if ($2 && $4 != NULL) 5021592Srgrimes statfilecmd($4); 5031592Srgrimes if ($4 != NULL) 5041592Srgrimes free($4); 5051592Srgrimes } 50671278Sjedgar | STAT check_login CRLF 5071592Srgrimes { 50871278Sjedgar if ($2) { 50971278Sjedgar statcmd(); 51071278Sjedgar } 5111592Srgrimes } 51270102Sphk | DELE check_login_ro SP pathname CRLF 5131592Srgrimes { 5141592Srgrimes if ($2 && $4 != NULL) 5151592Srgrimes delete($4); 5161592Srgrimes if ($4 != NULL) 5171592Srgrimes free($4); 5181592Srgrimes } 51970102Sphk | RNTO check_login_ro SP pathname CRLF 5201592Srgrimes { 521101379Syar if ($2 && $4 != NULL) { 52217433Spst if (fromname) { 52317433Spst renamecmd(fromname, $4); 52417433Spst free(fromname); 525132931Syar fromname = NULL; 52617433Spst } else { 52717433Spst reply(503, "Bad sequence of commands."); 52817433Spst } 5291592Srgrimes } 530101379Syar if ($4 != NULL) 531101379Syar free($4); 5321592Srgrimes } 53371278Sjedgar | ABOR check_login CRLF 5341592Srgrimes { 53571278Sjedgar if ($2) 53671278Sjedgar reply(225, "ABOR command successful."); 5371592Srgrimes } 5381592Srgrimes | CWD check_login CRLF 5391592Srgrimes { 54069234Sdanny if ($2) { 541110036Syar cwd(homedir); 54269234Sdanny } 5431592Srgrimes } 5441592Srgrimes | CWD check_login SP pathname CRLF 5451592Srgrimes { 5461592Srgrimes if ($2 && $4 != NULL) 5471592Srgrimes cwd($4); 5481592Srgrimes if ($4 != NULL) 5491592Srgrimes free($4); 5501592Srgrimes } 5511592Srgrimes | HELP CRLF 5521592Srgrimes { 553132931Syar help(cmdtab, NULL); 5541592Srgrimes } 5551592Srgrimes | HELP SP STRING CRLF 5561592Srgrimes { 5571592Srgrimes char *cp = $3; 5581592Srgrimes 5591592Srgrimes if (strncasecmp(cp, "SITE", 4) == 0) { 5601592Srgrimes cp = $3 + 4; 5611592Srgrimes if (*cp == ' ') 5621592Srgrimes cp++; 5631592Srgrimes if (*cp) 5641592Srgrimes help(sitetab, cp); 5651592Srgrimes else 566132931Syar help(sitetab, NULL); 5671592Srgrimes } else 5681592Srgrimes help(cmdtab, $3); 56988935Sdwmalone free($3); 5701592Srgrimes } 5711592Srgrimes | NOOP CRLF 5721592Srgrimes { 5731592Srgrimes reply(200, "NOOP command successful."); 5741592Srgrimes } 57570102Sphk | MKD check_login_ro SP pathname CRLF 5761592Srgrimes { 5771592Srgrimes if ($2 && $4 != NULL) 5781592Srgrimes makedir($4); 5791592Srgrimes if ($4 != NULL) 5801592Srgrimes free($4); 5811592Srgrimes } 58270102Sphk | RMD check_login_ro SP pathname CRLF 5831592Srgrimes { 5841592Srgrimes if ($2 && $4 != NULL) 5851592Srgrimes removedir($4); 5861592Srgrimes if ($4 != NULL) 5871592Srgrimes free($4); 5881592Srgrimes } 5891592Srgrimes | PWD check_login CRLF 5901592Srgrimes { 5911592Srgrimes if ($2) 5921592Srgrimes pwd(); 5931592Srgrimes } 5941592Srgrimes | CDUP check_login CRLF 5951592Srgrimes { 5961592Srgrimes if ($2) 5971592Srgrimes cwd(".."); 5981592Srgrimes } 5991592Srgrimes | SITE SP HELP CRLF 6001592Srgrimes { 601132931Syar help(sitetab, NULL); 6021592Srgrimes } 6031592Srgrimes | SITE SP HELP SP STRING CRLF 6041592Srgrimes { 6051592Srgrimes help(sitetab, $5); 60688935Sdwmalone free($5); 6071592Srgrimes } 60875535Sphk | SITE SP MDFIVE check_login SP pathname CRLF 60975535Sphk { 61075535Sphk char p[64], *q; 61175535Sphk 612101379Syar if ($4 && $6) { 61375535Sphk q = MD5File($6, p); 61475535Sphk if (q != NULL) 61575535Sphk reply(200, "MD5(%s) = %s", $6, p); 61675535Sphk else 61775535Sphk perror_reply(550, $6); 61875535Sphk } 61988935Sdwmalone if ($6) 62088935Sdwmalone free($6); 62175535Sphk } 6221592Srgrimes | SITE SP UMASK check_login CRLF 6231592Srgrimes { 6241592Srgrimes int oldmask; 6251592Srgrimes 6261592Srgrimes if ($4) { 6271592Srgrimes oldmask = umask(0); 6281592Srgrimes (void) umask(oldmask); 6291592Srgrimes reply(200, "Current UMASK is %03o", oldmask); 6301592Srgrimes } 6311592Srgrimes } 6321592Srgrimes | SITE SP UMASK check_login SP octal_number CRLF 6331592Srgrimes { 6341592Srgrimes int oldmask; 6351592Srgrimes 6361592Srgrimes if ($4) { 6371592Srgrimes if (($6 == -1) || ($6 > 0777)) { 6381592Srgrimes reply(501, "Bad UMASK value"); 6391592Srgrimes } else { 6401592Srgrimes oldmask = umask($6); 6411592Srgrimes reply(200, 6421592Srgrimes "UMASK set to %03o (was %03o)", 6431592Srgrimes $6, oldmask); 6441592Srgrimes } 6451592Srgrimes } 6461592Srgrimes } 64770102Sphk | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF 6481592Srgrimes { 6491592Srgrimes if ($4 && ($8 != NULL)) { 650101378Syar if (($6 == -1 ) || ($6 > 0777)) 651101378Syar reply(501, "Bad mode value"); 6521592Srgrimes else if (chmod($8, $6) < 0) 6531592Srgrimes perror_reply(550, $8); 6541592Srgrimes else 6551592Srgrimes reply(200, "CHMOD command successful."); 6561592Srgrimes } 6571592Srgrimes if ($8 != NULL) 6581592Srgrimes free($8); 6591592Srgrimes } 66071278Sjedgar | SITE SP check_login IDLE CRLF 6611592Srgrimes { 66271278Sjedgar if ($3) 66371278Sjedgar reply(200, 66471278Sjedgar "Current IDLE time limit is %d seconds; max %d", 66571278Sjedgar timeout, maxtimeout); 6661592Srgrimes } 66771278Sjedgar | SITE SP check_login IDLE SP NUMBER CRLF 6681592Srgrimes { 66971278Sjedgar if ($3) { 67092272Smaxim if ($6.i < 30 || $6.i > maxtimeout) { 67171278Sjedgar reply(501, 67271278Sjedgar "Maximum IDLE time must be between 30 and %d seconds", 67371278Sjedgar maxtimeout); 67471278Sjedgar } else { 67592272Smaxim timeout = $6.i; 676137659Syar (void) alarm(timeout); 67771278Sjedgar reply(200, 67871278Sjedgar "Maximum IDLE time set to %d seconds", 67971278Sjedgar timeout); 68071278Sjedgar } 6811592Srgrimes } 6821592Srgrimes } 68370102Sphk | STOU check_login_ro SP pathname CRLF 6841592Srgrimes { 6851592Srgrimes if ($2 && $4 != NULL) 6861592Srgrimes store($4, "w", 1); 6871592Srgrimes if ($4 != NULL) 6881592Srgrimes free($4); 6891592Srgrimes } 69071278Sjedgar | SYST check_login CRLF 6911592Srgrimes { 692116439Syar if ($2) { 693116439Syar if (hostinfo) 6941592Srgrimes#ifdef BSD 695116439Syar reply(215, "UNIX Type: L%d Version: BSD-%d", 696116439Syar CHAR_BIT, BSD); 6971592Srgrimes#else /* BSD */ 698116439Syar reply(215, "UNIX Type: L%d", CHAR_BIT); 6991592Srgrimes#endif /* BSD */ 700116439Syar else 701116439Syar reply(215, "UNKNOWN Type: L%d", CHAR_BIT); 702116439Syar } 7031592Srgrimes } 7041592Srgrimes 7051592Srgrimes /* 7061592Srgrimes * SIZE is not in RFC959, but Postel has blessed it and 7071592Srgrimes * it will be in the updated RFC. 7081592Srgrimes * 7091592Srgrimes * Return size of file in a format suitable for 7101592Srgrimes * using with RESTART (we just count bytes). 7111592Srgrimes */ 7121592Srgrimes | SIZE check_login SP pathname CRLF 7131592Srgrimes { 7141592Srgrimes if ($2 && $4 != NULL) 7151592Srgrimes sizecmd($4); 7161592Srgrimes if ($4 != NULL) 7171592Srgrimes free($4); 7181592Srgrimes } 7191592Srgrimes 7201592Srgrimes /* 7211592Srgrimes * MDTM is not in RFC959, but Postel has blessed it and 7221592Srgrimes * it will be in the updated RFC. 7231592Srgrimes * 7241592Srgrimes * Return modification time of file as an ISO 3307 7251592Srgrimes * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 7261592Srgrimes * where xxx is the fractional second (of any precision, 7271592Srgrimes * not necessarily 3 digits) 7281592Srgrimes */ 7291592Srgrimes | MDTM check_login SP pathname CRLF 7301592Srgrimes { 7311592Srgrimes if ($2 && $4 != NULL) { 7321592Srgrimes struct stat stbuf; 7331592Srgrimes if (stat($4, &stbuf) < 0) 7341592Srgrimes reply(550, "%s: %s", 7351592Srgrimes $4, strerror(errno)); 7361592Srgrimes else if (!S_ISREG(stbuf.st_mode)) { 7371592Srgrimes reply(550, "%s: not a plain file.", $4); 7381592Srgrimes } else { 7391592Srgrimes struct tm *t; 7401592Srgrimes t = gmtime(&stbuf.st_mtime); 7411592Srgrimes reply(213, 74217435Spst "%04d%02d%02d%02d%02d%02d", 74317435Spst 1900 + t->tm_year, 74417435Spst t->tm_mon+1, t->tm_mday, 7451592Srgrimes t->tm_hour, t->tm_min, t->tm_sec); 7461592Srgrimes } 7471592Srgrimes } 7481592Srgrimes if ($4 != NULL) 7491592Srgrimes free($4); 7501592Srgrimes } 7511592Srgrimes | QUIT CRLF 7521592Srgrimes { 7531592Srgrimes reply(221, "Goodbye."); 7541592Srgrimes dologout(0); 7551592Srgrimes } 756102565Syar | NOTIMPL 757102565Syar { 758102565Syar nack($1); 759102565Syar } 76089935Syar | error 7611592Srgrimes { 76289935Syar yyclearin; /* discard lookahead data */ 76389935Syar yyerrok; /* clear error condition */ 764102565Syar state = CMD; /* reset lexer state */ 7651592Srgrimes } 7661592Srgrimes ; 7671592Srgrimesrcmd 76870102Sphk : RNFR check_login_ro SP pathname CRLF 7691592Srgrimes { 770132930Syar restart_point = 0; 7711592Srgrimes if ($2 && $4) { 77288935Sdwmalone if (fromname) 77388935Sdwmalone free(fromname); 774132931Syar fromname = NULL; 77588935Sdwmalone if (renamefrom($4)) 77688935Sdwmalone fromname = $4; 77788935Sdwmalone else 7781592Srgrimes free($4); 77988935Sdwmalone } else if ($4) { 78088935Sdwmalone free($4); 7811592Srgrimes } 7821592Srgrimes } 78392272Smaxim | REST check_login SP NUMBER CRLF 7841592Srgrimes { 78571278Sjedgar if ($2) { 78688935Sdwmalone if (fromname) 78788935Sdwmalone free(fromname); 788132931Syar fromname = NULL; 78992272Smaxim restart_point = $4.o; 790132929Syar reply(350, "Restarting at %jd. %s", 791132929Syar (intmax_t)restart_point, 79271278Sjedgar "Send STORE or RETRIEVE to initiate transfer."); 79371278Sjedgar } 7941592Srgrimes } 7951592Srgrimes ; 7961592Srgrimes 7971592Srgrimesusername 7981592Srgrimes : STRING 7991592Srgrimes ; 8001592Srgrimes 8011592Srgrimespassword 8021592Srgrimes : /* empty */ 8031592Srgrimes { 8041592Srgrimes $$ = (char *)calloc(1, sizeof(char)); 8051592Srgrimes } 8061592Srgrimes | STRING 8071592Srgrimes ; 8081592Srgrimes 8091592Srgrimesbyte_size 8101592Srgrimes : NUMBER 81192272Smaxim { 81292272Smaxim $$ = $1.i; 81392272Smaxim } 8141592Srgrimes ; 8151592Srgrimes 8161592Srgrimeshost_port 8171592Srgrimes : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 8181592Srgrimes NUMBER COMMA NUMBER 8191592Srgrimes { 8201592Srgrimes char *a, *p; 8211592Srgrimes 82256668Sshin data_dest.su_len = sizeof(struct sockaddr_in); 82356668Sshin data_dest.su_family = AF_INET; 82456668Sshin p = (char *)&data_dest.su_sin.sin_port; 82592272Smaxim p[0] = $9.i; p[1] = $11.i; 82656668Sshin a = (char *)&data_dest.su_sin.sin_addr; 82792272Smaxim a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; 8281592Srgrimes } 8291592Srgrimes ; 8301592Srgrimes 83156668Sshinhost_long_port 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 COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83756668Sshin NUMBER 83856668Sshin { 83956668Sshin char *a, *p; 84056668Sshin 84156668Sshin memset(&data_dest, 0, sizeof(data_dest)); 84256668Sshin data_dest.su_len = sizeof(struct sockaddr_in6); 84356668Sshin data_dest.su_family = AF_INET6; 84456668Sshin p = (char *)&data_dest.su_port; 84592272Smaxim p[0] = $39.i; p[1] = $41.i; 84656668Sshin a = (char *)&data_dest.su_sin6.sin6_addr; 84792272Smaxim a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 84892272Smaxim a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i; 84992272Smaxim a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i; 85092272Smaxim a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i; 85156668Sshin if (his_addr.su_family == AF_INET6) { 85256668Sshin /* XXX more sanity checks! */ 85356668Sshin data_dest.su_sin6.sin6_scope_id = 85456668Sshin his_addr.su_sin6.sin6_scope_id; 85556668Sshin } 85692272Smaxim if ($1.i != 6 || $3.i != 16 || $37.i != 2) 85756668Sshin memset(&data_dest, 0, sizeof(data_dest)); 85856668Sshin } 85956668Sshin | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 86056668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 86156668Sshin NUMBER 86256668Sshin { 86356668Sshin char *a, *p; 86456668Sshin 86556668Sshin memset(&data_dest, 0, sizeof(data_dest)); 86656668Sshin data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 86756668Sshin data_dest.su_family = AF_INET; 86856668Sshin p = (char *)&data_dest.su_port; 86992272Smaxim p[0] = $15.i; p[1] = $17.i; 87056668Sshin a = (char *)&data_dest.su_sin.sin_addr; 87192272Smaxim a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 87292272Smaxim if ($1.i != 4 || $3.i != 4 || $13.i != 2) 87356668Sshin memset(&data_dest, 0, sizeof(data_dest)); 87456668Sshin } 87556668Sshin ; 87656668Sshin 8771592Srgrimesform_code 8781592Srgrimes : N 8791592Srgrimes { 8801592Srgrimes $$ = FORM_N; 8811592Srgrimes } 8821592Srgrimes | T 8831592Srgrimes { 8841592Srgrimes $$ = FORM_T; 8851592Srgrimes } 8861592Srgrimes | C 8871592Srgrimes { 8881592Srgrimes $$ = FORM_C; 8891592Srgrimes } 8901592Srgrimes ; 8911592Srgrimes 8921592Srgrimestype_code 8931592Srgrimes : A 8941592Srgrimes { 8951592Srgrimes cmd_type = TYPE_A; 8961592Srgrimes cmd_form = FORM_N; 8971592Srgrimes } 8981592Srgrimes | A SP form_code 8991592Srgrimes { 9001592Srgrimes cmd_type = TYPE_A; 9011592Srgrimes cmd_form = $3; 9021592Srgrimes } 9031592Srgrimes | E 9041592Srgrimes { 9051592Srgrimes cmd_type = TYPE_E; 9061592Srgrimes cmd_form = FORM_N; 9071592Srgrimes } 9081592Srgrimes | E SP form_code 9091592Srgrimes { 9101592Srgrimes cmd_type = TYPE_E; 9111592Srgrimes cmd_form = $3; 9121592Srgrimes } 9131592Srgrimes | I 9141592Srgrimes { 9151592Srgrimes cmd_type = TYPE_I; 9161592Srgrimes } 9171592Srgrimes | L 9181592Srgrimes { 9191592Srgrimes cmd_type = TYPE_L; 920103949Smike cmd_bytesz = CHAR_BIT; 9211592Srgrimes } 9221592Srgrimes | L SP byte_size 9231592Srgrimes { 9241592Srgrimes cmd_type = TYPE_L; 9251592Srgrimes cmd_bytesz = $3; 9261592Srgrimes } 9271592Srgrimes /* this is for a bug in the BBN ftp */ 9281592Srgrimes | L byte_size 9291592Srgrimes { 9301592Srgrimes cmd_type = TYPE_L; 9311592Srgrimes cmd_bytesz = $2; 9321592Srgrimes } 9331592Srgrimes ; 9341592Srgrimes 9351592Srgrimesstruct_code 9361592Srgrimes : F 9371592Srgrimes { 9381592Srgrimes $$ = STRU_F; 9391592Srgrimes } 9401592Srgrimes | R 9411592Srgrimes { 9421592Srgrimes $$ = STRU_R; 9431592Srgrimes } 9441592Srgrimes | P 9451592Srgrimes { 9461592Srgrimes $$ = STRU_P; 9471592Srgrimes } 9481592Srgrimes ; 9491592Srgrimes 9501592Srgrimesmode_code 9511592Srgrimes : S 9521592Srgrimes { 9531592Srgrimes $$ = MODE_S; 9541592Srgrimes } 9551592Srgrimes | B 9561592Srgrimes { 9571592Srgrimes $$ = MODE_B; 9581592Srgrimes } 9591592Srgrimes | C 9601592Srgrimes { 9611592Srgrimes $$ = MODE_C; 9621592Srgrimes } 9631592Srgrimes ; 9641592Srgrimes 9651592Srgrimespathname 9661592Srgrimes : pathstring 9671592Srgrimes { 96875567Speter if (logged_in && $1) { 969110340Syar char *p; 9701592Srgrimes 971110340Syar /* 972110340Syar * Expand ~user manually since glob(3) 973110340Syar * will return the unexpanded pathname 974110340Syar * if the corresponding file/directory 975110340Syar * doesn't exist yet. Using sole glob(3) 976110340Syar * would break natural commands like 977110340Syar * MKD ~user/newdir 978110340Syar * or 979110340Syar * RNTO ~/newfile 980110340Syar */ 981110340Syar if ((p = exptilde($1)) != NULL) { 982110340Syar $$ = expglob(p); 983110340Syar free(p); 984110340Syar } else 9851592Srgrimes $$ = NULL; 9861592Srgrimes free($1); 9871592Srgrimes } else 9881592Srgrimes $$ = $1; 9891592Srgrimes } 9901592Srgrimes ; 9911592Srgrimes 9921592Srgrimespathstring 9931592Srgrimes : STRING 9941592Srgrimes ; 9951592Srgrimes 9961592Srgrimesoctal_number 9971592Srgrimes : NUMBER 9981592Srgrimes { 9991592Srgrimes int ret, dec, multby, digit; 10001592Srgrimes 10011592Srgrimes /* 10021592Srgrimes * Convert a number that was read as decimal number 10031592Srgrimes * to what it would be if it had been read as octal. 10041592Srgrimes */ 100592272Smaxim dec = $1.i; 10061592Srgrimes multby = 1; 10071592Srgrimes ret = 0; 10081592Srgrimes while (dec) { 10091592Srgrimes digit = dec%10; 10101592Srgrimes if (digit > 7) { 10111592Srgrimes ret = -1; 10121592Srgrimes break; 10131592Srgrimes } 10141592Srgrimes ret += digit * multby; 10151592Srgrimes multby *= 8; 10161592Srgrimes dec /= 10; 10171592Srgrimes } 10181592Srgrimes $$ = ret; 10191592Srgrimes } 10201592Srgrimes ; 10211592Srgrimes 10221592Srgrimes 10231592Srgrimescheck_login 10241592Srgrimes : /* empty */ 10251592Srgrimes { 102670102Sphk $$ = check_login1(); 10271592Srgrimes } 10281592Srgrimes ; 10291592Srgrimes 103070102Sphkcheck_login_epsv 103170102Sphk : /* empty */ 103270102Sphk { 103370102Sphk if (noepsv) { 103470102Sphk reply(500, "EPSV command disabled"); 103570102Sphk $$ = 0; 103670102Sphk } 103770102Sphk else 103870102Sphk $$ = check_login1(); 103970102Sphk } 104070102Sphk ; 104170102Sphk 104270102Sphkcheck_login_ro 104370102Sphk : /* empty */ 104470102Sphk { 104570102Sphk if (readonly) { 104672710Sdes reply(550, "Permission denied."); 104770102Sphk $$ = 0; 104870102Sphk } 104970102Sphk else 105070102Sphk $$ = check_login1(); 105170102Sphk } 105270102Sphk ; 105370102Sphk 10541592Srgrimes%% 10551592Srgrimes 10561592Srgrimes#define CMD 0 /* beginning of command */ 10571592Srgrimes#define ARGS 1 /* expect miscellaneous arguments */ 10581592Srgrimes#define STR1 2 /* expect SP followed by STRING */ 10591592Srgrimes#define STR2 3 /* expect STRING */ 10601592Srgrimes#define OSTR 4 /* optional SP then STRING */ 106175556Sgreen#define ZSTR1 5 /* optional SP then optional STRING */ 10621592Srgrimes#define ZSTR2 6 /* optional STRING after SP */ 10631592Srgrimes#define SITECMD 7 /* SITE command */ 10641592Srgrimes#define NSTR 8 /* Number followed by a string */ 10651592Srgrimes 106675560Sjedgar#define MAXGLOBARGS 1000 106775560Sjedgar 1068101034Syar#define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */ 1069101034Syar 10701592Srgrimesstruct tab { 10711592Srgrimes char *name; 10721592Srgrimes short token; 10731592Srgrimes short state; 10741592Srgrimes short implemented; /* 1 if command is implemented */ 10751592Srgrimes char *help; 10761592Srgrimes}; 10771592Srgrimes 10781592Srgrimesstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 10791592Srgrimes { "USER", USER, STR1, 1, "<sp> username" }, 108075556Sgreen { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" }, 10811592Srgrimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 10821592Srgrimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 10831592Srgrimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 10841592Srgrimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 1085101806Syar { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" }, 108656668Sshin { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 108756668Sshin { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 10881592Srgrimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 108956668Sshin { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 109056668Sshin { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 1091101806Syar { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" }, 10921592Srgrimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 10931592Srgrimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 10941592Srgrimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 10951592Srgrimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 10961592Srgrimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 10971592Srgrimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 10981592Srgrimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 10991592Srgrimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 11001592Srgrimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 11011592Srgrimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 11021592Srgrimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 11031592Srgrimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 11041592Srgrimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 11051592Srgrimes { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 11061592Srgrimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 11071592Srgrimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 11081592Srgrimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 11091592Srgrimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 11101592Srgrimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 11111592Srgrimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 11121592Srgrimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 11131592Srgrimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 11141592Srgrimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 11151592Srgrimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 11161592Srgrimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 11171592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11181592Srgrimes { "NOOP", NOOP, ARGS, 1, "" }, 11191592Srgrimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 11201592Srgrimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 11211592Srgrimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 11221592Srgrimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 11231592Srgrimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 11241592Srgrimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 11251592Srgrimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11261592Srgrimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11271592Srgrimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 11281592Srgrimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 11291592Srgrimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 11301592Srgrimes { NULL, 0, 0, 0, 0 } 11311592Srgrimes}; 11321592Srgrimes 11331592Srgrimesstruct tab sitetab[] = { 113475535Sphk { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" }, 11351592Srgrimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 11361592Srgrimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 11371592Srgrimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 11381592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11391592Srgrimes { NULL, 0, 0, 0, 0 } 11401592Srgrimes}; 11411592Srgrimes 114290148Simpstatic char *copy(char *); 1143110340Syarstatic char *expglob(char *); 1144110340Syarstatic char *exptilde(char *); 114590148Simpstatic void help(struct tab *, char *); 11461592Srgrimesstatic struct tab * 114790148Simp lookup(struct tab *, char *); 114890148Simpstatic int port_check(const char *); 114990148Simpstatic int port_check_v6(const char *); 115090148Simpstatic void sizecmd(char *); 115190148Simpstatic void toolong(int); 115290148Simpstatic void v4map_data_dest(void); 115390148Simpstatic int yylex(void); 11541592Srgrimes 11551592Srgrimesstatic struct tab * 115690148Simplookup(struct tab *p, char *cmd) 11571592Srgrimes{ 11581592Srgrimes 11591592Srgrimes for (; p->name != NULL; p++) 11601592Srgrimes if (strcmp(cmd, p->name) == 0) 11611592Srgrimes return (p); 11621592Srgrimes return (0); 11631592Srgrimes} 11641592Srgrimes 11651592Srgrimes#include <arpa/telnet.h> 11661592Srgrimes 11671592Srgrimes/* 11681592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes. 11691592Srgrimes */ 11701592Srgrimeschar * 117190148Simpgetline(char *s, int n, FILE *iop) 11721592Srgrimes{ 11731592Srgrimes int c; 11741592Srgrimes register char *cs; 1175117352Syar sigset_t sset, osset; 11761592Srgrimes 11771592Srgrimes cs = s; 11781592Srgrimes/* tmpline may contain saved command from urgent mode interruption */ 11791592Srgrimes for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 11801592Srgrimes *cs++ = tmpline[c]; 11811592Srgrimes if (tmpline[c] == '\n') { 11821592Srgrimes *cs++ = '\0'; 118376096Smarkm if (ftpdebug) 11841592Srgrimes syslog(LOG_DEBUG, "command: %s", s); 11851592Srgrimes tmpline[0] = '\0'; 11861592Srgrimes return(s); 11871592Srgrimes } 11881592Srgrimes if (c == 0) 11891592Srgrimes tmpline[0] = '\0'; 11901592Srgrimes } 1191117352Syar /* SIGURG would interrupt stdio if not blocked during the read loop */ 1192117352Syar sigemptyset(&sset); 1193117352Syar sigaddset(&sset, SIGURG); 1194117352Syar sigprocmask(SIG_BLOCK, &sset, &osset); 11951592Srgrimes while ((c = getc(iop)) != EOF) { 11961592Srgrimes c &= 0377; 11971592Srgrimes if (c == IAC) { 1198117351Syar if ((c = getc(iop)) == EOF) 1199117351Syar goto got_eof; 12001592Srgrimes c &= 0377; 12011592Srgrimes switch (c) { 12021592Srgrimes case WILL: 12031592Srgrimes case WONT: 1204117351Syar if ((c = getc(iop)) == EOF) 1205117351Syar goto got_eof; 12061592Srgrimes printf("%c%c%c", IAC, DONT, 0377&c); 12071592Srgrimes (void) fflush(stdout); 12081592Srgrimes continue; 12091592Srgrimes case DO: 12101592Srgrimes case DONT: 1211117351Syar if ((c = getc(iop)) == EOF) 1212117351Syar goto got_eof; 12131592Srgrimes printf("%c%c%c", IAC, WONT, 0377&c); 12141592Srgrimes (void) fflush(stdout); 12151592Srgrimes continue; 12161592Srgrimes case IAC: 12171592Srgrimes break; 12181592Srgrimes default: 12191592Srgrimes continue; /* ignore command */ 12201592Srgrimes } 12211592Srgrimes } 12221592Srgrimes *cs++ = c; 12231592Srgrimes if (--n <= 0 || c == '\n') 12241592Srgrimes break; 12251592Srgrimes } 1226117351Syargot_eof: 1227117352Syar sigprocmask(SIG_SETMASK, &osset, NULL); 12281592Srgrimes if (c == EOF && cs == s) 12291592Srgrimes return (NULL); 12301592Srgrimes *cs++ = '\0'; 123176096Smarkm if (ftpdebug) { 12321592Srgrimes if (!guest && strncasecmp("pass ", s, 5) == 0) { 12331592Srgrimes /* Don't syslog passwords */ 12341592Srgrimes syslog(LOG_DEBUG, "command: %.5s ???", s); 12351592Srgrimes } else { 12361592Srgrimes register char *cp; 12371592Srgrimes register int len; 12381592Srgrimes 12391592Srgrimes /* Don't syslog trailing CR-LF */ 12401592Srgrimes len = strlen(s); 12411592Srgrimes cp = s + len - 1; 12421592Srgrimes while (cp >= s && (*cp == '\n' || *cp == '\r')) { 12431592Srgrimes --cp; 12441592Srgrimes --len; 12451592Srgrimes } 12461592Srgrimes syslog(LOG_DEBUG, "command: %.*s", len, s); 12471592Srgrimes } 12481592Srgrimes } 12491592Srgrimes return (s); 12501592Srgrimes} 12511592Srgrimes 12521592Srgrimesstatic void 125390148Simptoolong(int signo) 12541592Srgrimes{ 12551592Srgrimes 12561592Srgrimes reply(421, 12571592Srgrimes "Timeout (%d seconds): closing control connection.", timeout); 12581592Srgrimes if (logging) 12591592Srgrimes syslog(LOG_INFO, "User %s timed out after %d seconds", 12601592Srgrimes (pw ? pw -> pw_name : "unknown"), timeout); 12611592Srgrimes dologout(1); 12621592Srgrimes} 12631592Srgrimes 12641592Srgrimesstatic int 126590148Simpyylex(void) 12661592Srgrimes{ 126789935Syar static int cpos; 12681592Srgrimes char *cp, *cp2; 12691592Srgrimes struct tab *p; 12701592Srgrimes int n; 12711592Srgrimes char c; 12721592Srgrimes 12731592Srgrimes for (;;) { 12741592Srgrimes switch (state) { 12751592Srgrimes 12761592Srgrimes case CMD: 12771592Srgrimes (void) signal(SIGALRM, toolong); 1278137659Syar (void) alarm(timeout); 12791592Srgrimes if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 12801592Srgrimes reply(221, "You could at least say goodbye."); 12811592Srgrimes dologout(0); 12821592Srgrimes } 12831592Srgrimes (void) alarm(0); 12841592Srgrimes#ifdef SETPROCTITLE 128529574Sphk if (strncasecmp(cbuf, "PASS", 4) != 0) 12861592Srgrimes setproctitle("%s: %s", proctitle, cbuf); 12871592Srgrimes#endif /* SETPROCTITLE */ 12881592Srgrimes if ((cp = strchr(cbuf, '\r'))) { 12891592Srgrimes *cp++ = '\n'; 12901592Srgrimes *cp = '\0'; 12911592Srgrimes } 12921592Srgrimes if ((cp = strpbrk(cbuf, " \n"))) 12931592Srgrimes cpos = cp - cbuf; 12941592Srgrimes if (cpos == 0) 12951592Srgrimes cpos = 4; 12961592Srgrimes c = cbuf[cpos]; 12971592Srgrimes cbuf[cpos] = '\0'; 12981592Srgrimes upper(cbuf); 12991592Srgrimes p = lookup(cmdtab, cbuf); 13001592Srgrimes cbuf[cpos] = c; 13013776Spst if (p != 0) { 1302102565Syar yylval.s = p->name; 1303102565Syar if (!p->implemented) 1304102565Syar return (NOTIMPL); /* state remains CMD */ 13051592Srgrimes state = p->state; 13061592Srgrimes return (p->token); 13071592Srgrimes } 13081592Srgrimes break; 13091592Srgrimes 13101592Srgrimes case SITECMD: 13111592Srgrimes if (cbuf[cpos] == ' ') { 13121592Srgrimes cpos++; 13131592Srgrimes return (SP); 13141592Srgrimes } 13151592Srgrimes cp = &cbuf[cpos]; 13161592Srgrimes if ((cp2 = strpbrk(cp, " \n"))) 13171592Srgrimes cpos = cp2 - cbuf; 13181592Srgrimes c = cbuf[cpos]; 13191592Srgrimes cbuf[cpos] = '\0'; 13201592Srgrimes upper(cp); 13211592Srgrimes p = lookup(sitetab, cp); 13221592Srgrimes cbuf[cpos] = c; 13233777Spst if (guest == 0 && p != 0) { 1324102565Syar yylval.s = p->name; 1325102565Syar if (!p->implemented) { 13261592Srgrimes state = CMD; 1327102565Syar return (NOTIMPL); 13281592Srgrimes } 13291592Srgrimes state = p->state; 13301592Srgrimes return (p->token); 13311592Srgrimes } 13321592Srgrimes state = CMD; 13331592Srgrimes break; 13341592Srgrimes 133575556Sgreen case ZSTR1: 13361592Srgrimes case OSTR: 13371592Srgrimes if (cbuf[cpos] == '\n') { 13381592Srgrimes state = CMD; 13391592Srgrimes return (CRLF); 13401592Srgrimes } 13411592Srgrimes /* FALLTHROUGH */ 13421592Srgrimes 13431592Srgrimes case STR1: 13441592Srgrimes dostr1: 13451592Srgrimes if (cbuf[cpos] == ' ') { 13461592Srgrimes cpos++; 134751979Salfred state = state == OSTR ? STR2 : state+1; 13481592Srgrimes return (SP); 13491592Srgrimes } 13501592Srgrimes break; 13511592Srgrimes 13521592Srgrimes case ZSTR2: 13531592Srgrimes if (cbuf[cpos] == '\n') { 13541592Srgrimes state = CMD; 13551592Srgrimes return (CRLF); 13561592Srgrimes } 13571592Srgrimes /* FALLTHROUGH */ 13581592Srgrimes 13591592Srgrimes case STR2: 13601592Srgrimes cp = &cbuf[cpos]; 13611592Srgrimes n = strlen(cp); 13621592Srgrimes cpos += n - 1; 13631592Srgrimes /* 13641592Srgrimes * Make sure the string is nonempty and \n terminated. 13651592Srgrimes */ 13661592Srgrimes if (n > 1 && cbuf[cpos] == '\n') { 13671592Srgrimes cbuf[cpos] = '\0'; 13681592Srgrimes yylval.s = copy(cp); 13691592Srgrimes cbuf[cpos] = '\n'; 13701592Srgrimes state = ARGS; 13711592Srgrimes return (STRING); 13721592Srgrimes } 13731592Srgrimes break; 13741592Srgrimes 13751592Srgrimes case NSTR: 13761592Srgrimes if (cbuf[cpos] == ' ') { 13771592Srgrimes cpos++; 13781592Srgrimes return (SP); 13791592Srgrimes } 13801592Srgrimes if (isdigit(cbuf[cpos])) { 13811592Srgrimes cp = &cbuf[cpos]; 13821592Srgrimes while (isdigit(cbuf[++cpos])) 13831592Srgrimes ; 13841592Srgrimes c = cbuf[cpos]; 13851592Srgrimes cbuf[cpos] = '\0'; 138692272Smaxim yylval.u.i = atoi(cp); 13871592Srgrimes cbuf[cpos] = c; 13881592Srgrimes state = STR1; 13891592Srgrimes return (NUMBER); 13901592Srgrimes } 13911592Srgrimes state = STR1; 13921592Srgrimes goto dostr1; 13931592Srgrimes 13941592Srgrimes case ARGS: 13951592Srgrimes if (isdigit(cbuf[cpos])) { 13961592Srgrimes cp = &cbuf[cpos]; 13971592Srgrimes while (isdigit(cbuf[++cpos])) 13981592Srgrimes ; 13991592Srgrimes c = cbuf[cpos]; 14001592Srgrimes cbuf[cpos] = '\0'; 140192272Smaxim yylval.u.i = atoi(cp); 1402137811Syar yylval.u.o = strtoull(cp, NULL, 10); 14031592Srgrimes cbuf[cpos] = c; 14041592Srgrimes return (NUMBER); 14051592Srgrimes } 140656668Sshin if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 140756668Sshin && !isalnum(cbuf[cpos + 3])) { 140856668Sshin cpos += 3; 140956668Sshin return ALL; 141056668Sshin } 14111592Srgrimes switch (cbuf[cpos++]) { 14121592Srgrimes 14131592Srgrimes case '\n': 14141592Srgrimes state = CMD; 14151592Srgrimes return (CRLF); 14161592Srgrimes 14171592Srgrimes case ' ': 14181592Srgrimes return (SP); 14191592Srgrimes 14201592Srgrimes case ',': 14211592Srgrimes return (COMMA); 14221592Srgrimes 14231592Srgrimes case 'A': 14241592Srgrimes case 'a': 14251592Srgrimes return (A); 14261592Srgrimes 14271592Srgrimes case 'B': 14281592Srgrimes case 'b': 14291592Srgrimes return (B); 14301592Srgrimes 14311592Srgrimes case 'C': 14321592Srgrimes case 'c': 14331592Srgrimes return (C); 14341592Srgrimes 14351592Srgrimes case 'E': 14361592Srgrimes case 'e': 14371592Srgrimes return (E); 14381592Srgrimes 14391592Srgrimes case 'F': 14401592Srgrimes case 'f': 14411592Srgrimes return (F); 14421592Srgrimes 14431592Srgrimes case 'I': 14441592Srgrimes case 'i': 14451592Srgrimes return (I); 14461592Srgrimes 14471592Srgrimes case 'L': 14481592Srgrimes case 'l': 14491592Srgrimes return (L); 14501592Srgrimes 14511592Srgrimes case 'N': 14521592Srgrimes case 'n': 14531592Srgrimes return (N); 14541592Srgrimes 14551592Srgrimes case 'P': 14561592Srgrimes case 'p': 14571592Srgrimes return (P); 14581592Srgrimes 14591592Srgrimes case 'R': 14601592Srgrimes case 'r': 14611592Srgrimes return (R); 14621592Srgrimes 14631592Srgrimes case 'S': 14641592Srgrimes case 's': 14651592Srgrimes return (S); 14661592Srgrimes 14671592Srgrimes case 'T': 14681592Srgrimes case 't': 14691592Srgrimes return (T); 14701592Srgrimes 14711592Srgrimes } 14721592Srgrimes break; 14731592Srgrimes 14741592Srgrimes default: 147576096Smarkm fatalerror("Unknown state in scanner."); 14761592Srgrimes } 14771592Srgrimes state = CMD; 147889935Syar return (LEXERR); 14791592Srgrimes } 14801592Srgrimes} 14811592Srgrimes 14821592Srgrimesvoid 148390148Simpupper(char *s) 14841592Srgrimes{ 14851592Srgrimes while (*s != '\0') { 14861592Srgrimes if (islower(*s)) 14871592Srgrimes *s = toupper(*s); 14881592Srgrimes s++; 14891592Srgrimes } 14901592Srgrimes} 14911592Srgrimes 14921592Srgrimesstatic char * 149390148Simpcopy(char *s) 14941592Srgrimes{ 14951592Srgrimes char *p; 14961592Srgrimes 1497137659Syar p = malloc(strlen(s) + 1); 14981592Srgrimes if (p == NULL) 149976096Smarkm fatalerror("Ran out of memory."); 15001592Srgrimes (void) strcpy(p, s); 15011592Srgrimes return (p); 15021592Srgrimes} 15031592Srgrimes 15041592Srgrimesstatic void 150590148Simphelp(struct tab *ctab, char *s) 15061592Srgrimes{ 15071592Srgrimes struct tab *c; 15081592Srgrimes int width, NCMDS; 15091592Srgrimes char *type; 15101592Srgrimes 15111592Srgrimes if (ctab == sitetab) 15121592Srgrimes type = "SITE "; 15131592Srgrimes else 15141592Srgrimes type = ""; 15151592Srgrimes width = 0, NCMDS = 0; 15161592Srgrimes for (c = ctab; c->name != NULL; c++) { 15171592Srgrimes int len = strlen(c->name); 15181592Srgrimes 15191592Srgrimes if (len > width) 15201592Srgrimes width = len; 15211592Srgrimes NCMDS++; 15221592Srgrimes } 15231592Srgrimes width = (width + 8) &~ 7; 15241592Srgrimes if (s == 0) { 15251592Srgrimes int i, j, w; 15261592Srgrimes int columns, lines; 15271592Srgrimes 15281592Srgrimes lreply(214, "The following %scommands are recognized %s.", 15291592Srgrimes type, "(* =>'s unimplemented)"); 15301592Srgrimes columns = 76 / width; 15311592Srgrimes if (columns == 0) 15321592Srgrimes columns = 1; 15331592Srgrimes lines = (NCMDS + columns - 1) / columns; 15341592Srgrimes for (i = 0; i < lines; i++) { 15351592Srgrimes printf(" "); 15361592Srgrimes for (j = 0; j < columns; j++) { 15371592Srgrimes c = ctab + j * lines + i; 15381592Srgrimes printf("%s%c", c->name, 15391592Srgrimes c->implemented ? ' ' : '*'); 15401592Srgrimes if (c + lines >= &ctab[NCMDS]) 15411592Srgrimes break; 15421592Srgrimes w = strlen(c->name) + 1; 15431592Srgrimes while (w < width) { 15441592Srgrimes putchar(' '); 15451592Srgrimes w++; 15461592Srgrimes } 15471592Srgrimes } 15481592Srgrimes printf("\r\n"); 15491592Srgrimes } 15501592Srgrimes (void) fflush(stdout); 1551110037Syar if (hostinfo) 1552110037Syar reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1553110037Syar else 1554110037Syar reply(214, "End."); 15551592Srgrimes return; 15561592Srgrimes } 15571592Srgrimes upper(s); 15581592Srgrimes c = lookup(ctab, s); 1559132931Syar if (c == NULL) { 15601592Srgrimes reply(502, "Unknown command %s.", s); 15611592Srgrimes return; 15621592Srgrimes } 15631592Srgrimes if (c->implemented) 15641592Srgrimes reply(214, "Syntax: %s%s %s", type, c->name, c->help); 15651592Srgrimes else 15661592Srgrimes reply(214, "%s%-*s\t%s; unimplemented.", type, width, 15671592Srgrimes c->name, c->help); 15681592Srgrimes} 15691592Srgrimes 15701592Srgrimesstatic void 157190148Simpsizecmd(char *filename) 15721592Srgrimes{ 15731592Srgrimes switch (type) { 15741592Srgrimes case TYPE_L: 15751592Srgrimes case TYPE_I: { 15761592Srgrimes struct stat stbuf; 157763350Sdes if (stat(filename, &stbuf) < 0) 157863350Sdes perror_reply(550, filename); 157963350Sdes else if (!S_ISREG(stbuf.st_mode)) 15801592Srgrimes reply(550, "%s: not a plain file.", filename); 15811592Srgrimes else 1582132929Syar reply(213, "%jd", (intmax_t)stbuf.st_size); 15831592Srgrimes break; } 15841592Srgrimes case TYPE_A: { 15851592Srgrimes FILE *fin; 15861592Srgrimes int c; 15871592Srgrimes off_t count; 15881592Srgrimes struct stat stbuf; 15891592Srgrimes fin = fopen(filename, "r"); 15901592Srgrimes if (fin == NULL) { 15911592Srgrimes perror_reply(550, filename); 15921592Srgrimes return; 15931592Srgrimes } 159463350Sdes if (fstat(fileno(fin), &stbuf) < 0) { 159563350Sdes perror_reply(550, filename); 159663350Sdes (void) fclose(fin); 159763350Sdes return; 159863350Sdes } else if (!S_ISREG(stbuf.st_mode)) { 15991592Srgrimes reply(550, "%s: not a plain file.", filename); 16001592Srgrimes (void) fclose(fin); 16011592Srgrimes return; 1602101034Syar } else if (stbuf.st_size > MAXASIZE) { 1603101034Syar reply(550, "%s: too large for type A SIZE.", filename); 1604101034Syar (void) fclose(fin); 1605101034Syar return; 16061592Srgrimes } 16071592Srgrimes 16081592Srgrimes count = 0; 16091592Srgrimes while((c=getc(fin)) != EOF) { 16101592Srgrimes if (c == '\n') /* will get expanded to \r\n */ 16111592Srgrimes count++; 16121592Srgrimes count++; 16131592Srgrimes } 16141592Srgrimes (void) fclose(fin); 16151592Srgrimes 1616132929Syar reply(213, "%jd", (intmax_t)count); 16171592Srgrimes break; } 16181592Srgrimes default: 1619100684Syar reply(504, "SIZE not implemented for type %s.", 1620100684Syar typenames[type]); 16211592Srgrimes } 16221592Srgrimes} 162356668Sshin 162456668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 162556668Sshinstatic int 162690148Simpport_check(const char *pcmd) 162756668Sshin{ 162856668Sshin if (his_addr.su_family == AF_INET) { 162956668Sshin if (data_dest.su_family != AF_INET) { 163056668Sshin usedefault = 1; 163156668Sshin reply(500, "Invalid address rejected."); 163256668Sshin return 1; 163356668Sshin } 163456668Sshin if (paranoid && 163556668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 163656668Sshin memcmp(&data_dest.su_sin.sin_addr, 163756668Sshin &his_addr.su_sin.sin_addr, 163856668Sshin sizeof(data_dest.su_sin.sin_addr)))) { 163956668Sshin usedefault = 1; 164056668Sshin reply(500, "Illegal PORT range rejected."); 164156668Sshin } else { 164256668Sshin usedefault = 0; 164356668Sshin if (pdata >= 0) { 164456668Sshin (void) close(pdata); 164556668Sshin pdata = -1; 164656668Sshin } 164756668Sshin reply(200, "%s command successful.", pcmd); 164856668Sshin } 164956668Sshin return 1; 165056668Sshin } 165156668Sshin return 0; 165256668Sshin} 165356668Sshin 165470102Sphkstatic int 165590148Simpcheck_login1(void) 165670102Sphk{ 165770102Sphk if (logged_in) 165870102Sphk return 1; 165970102Sphk else { 166070102Sphk reply(530, "Please login with USER and PASS."); 166170102Sphk return 0; 166270102Sphk } 166370102Sphk} 166470102Sphk 1665110340Syar/* 1666110340Syar * Replace leading "~user" in a pathname by the user's login directory. 1667110340Syar * Returned string will be in a freshly malloced buffer unless it's NULL. 1668110340Syar */ 1669110340Syarstatic char * 1670110340Syarexptilde(char *s) 1671110340Syar{ 1672110340Syar char *p, *q; 1673110340Syar char *path, *user; 1674110340Syar struct passwd *ppw; 1675110340Syar 1676110340Syar if ((p = strdup(s)) == NULL) 1677110340Syar return (NULL); 1678110340Syar if (*p != '~') 1679110340Syar return (p); 1680110340Syar 1681110340Syar user = p + 1; /* skip tilde */ 1682110340Syar if ((path = strchr(p, '/')) != NULL) 1683110340Syar *(path++) = '\0'; /* separate ~user from the rest of path */ 1684110378Syar if (*user == '\0') /* no user specified, use the current user */ 1685110378Syar user = pw->pw_name; 1686110378Syar /* read passwd even for the current user since we may be chrooted */ 1687110378Syar if ((ppw = getpwnam(user)) != NULL) { 1688110340Syar /* user found, substitute login directory for ~user */ 1689110340Syar if (path) 1690110340Syar asprintf(&q, "%s/%s", ppw->pw_dir, path); 1691110340Syar else 1692110340Syar q = strdup(ppw->pw_dir); 1693110340Syar free(p); 1694110340Syar p = q; 1695110340Syar } else { 1696110340Syar /* user not found, undo the damage */ 1697110340Syar if (path) 1698110340Syar path[-1] = '/'; 1699110340Syar } 1700110340Syar return (p); 1701110340Syar} 1702110340Syar 1703110340Syar/* 1704110340Syar * Expand glob(3) patterns possibly present in a pathname. 1705110340Syar * Avoid expanding to a pathname including '\r' or '\n' in order to 1706110340Syar * not disrupt the FTP protocol. 1707110340Syar * The expansion found must be unique. 1708110340Syar * Return the result as a malloced string, or NULL if an error occured. 1709110340Syar * 1710110340Syar * Problem: this production is used for all pathname 1711110340Syar * processing, but only gives a 550 error reply. 1712110340Syar * This is a valid reply in some cases but not in others. 1713110340Syar */ 1714110340Syarstatic char * 1715110340Syarexpglob(char *s) 1716110340Syar{ 1717110340Syar char *p, **pp, *rval; 1718110340Syar int flags = GLOB_BRACE | GLOB_NOCHECK; 1719110340Syar int n; 1720110340Syar glob_t gl; 1721110340Syar 1722110340Syar memset(&gl, 0, sizeof(gl)); 1723110340Syar flags |= GLOB_LIMIT; 1724110340Syar gl.gl_matchc = MAXGLOBARGS; 1725110340Syar if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) { 1726110340Syar for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++) 1727110340Syar if (*(*pp + strcspn(*pp, "\r\n")) == '\0') { 1728110340Syar p = *pp; 1729110340Syar n++; 1730110340Syar } 1731110340Syar if (n == 0) 1732110340Syar rval = strdup(s); 1733110340Syar else if (n == 1) 1734110340Syar rval = strdup(p); 1735110340Syar else { 1736110340Syar reply(550, "ambiguous"); 1737110340Syar rval = NULL; 1738110340Syar } 1739110340Syar } else { 1740110340Syar reply(550, "wildcard expansion error"); 1741110340Syar rval = NULL; 1742110340Syar } 1743110340Syar globfree(&gl); 1744110340Syar return (rval); 1745110340Syar} 1746110340Syar 174756668Sshin#ifdef INET6 174856668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 174956668Sshinstatic int 175090148Simpport_check_v6(const char *pcmd) 175156668Sshin{ 175256668Sshin if (his_addr.su_family == AF_INET6) { 175356668Sshin if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 175456668Sshin /* Convert data_dest into v4 mapped sockaddr.*/ 175556668Sshin v4map_data_dest(); 175656668Sshin if (data_dest.su_family != AF_INET6) { 175756668Sshin usedefault = 1; 175856668Sshin reply(500, "Invalid address rejected."); 175956668Sshin return 1; 176056668Sshin } 176156668Sshin if (paranoid && 176256668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 176356668Sshin memcmp(&data_dest.su_sin6.sin6_addr, 176456668Sshin &his_addr.su_sin6.sin6_addr, 176556668Sshin sizeof(data_dest.su_sin6.sin6_addr)))) { 176656668Sshin usedefault = 1; 176756668Sshin reply(500, "Illegal PORT range rejected."); 176856668Sshin } else { 176956668Sshin usedefault = 0; 177056668Sshin if (pdata >= 0) { 177156668Sshin (void) close(pdata); 177256668Sshin pdata = -1; 177356668Sshin } 177456668Sshin reply(200, "%s command successful.", pcmd); 177556668Sshin } 177656668Sshin return 1; 177756668Sshin } 177856668Sshin return 0; 177956668Sshin} 178056668Sshin 178156668Sshinstatic void 178290148Simpv4map_data_dest(void) 178356668Sshin{ 178456668Sshin struct in_addr savedaddr; 178556668Sshin int savedport; 178656668Sshin 178756668Sshin if (data_dest.su_family != AF_INET) { 178856668Sshin usedefault = 1; 178956668Sshin reply(500, "Invalid address rejected."); 179056668Sshin return; 179156668Sshin } 179256668Sshin 179356668Sshin savedaddr = data_dest.su_sin.sin_addr; 179456668Sshin savedport = data_dest.su_port; 179556668Sshin 179656668Sshin memset(&data_dest, 0, sizeof(data_dest)); 179756668Sshin data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 179856668Sshin data_dest.su_sin6.sin6_family = AF_INET6; 179956668Sshin data_dest.su_sin6.sin6_port = savedport; 180056668Sshin memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 180156668Sshin memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 180256668Sshin (caddr_t)&savedaddr, sizeof(savedaddr)); 180356668Sshin} 180456668Sshin#endif 1805