ftpcmd.y revision 137859
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 471592Srgrimes#endif /* not lint */ 481592Srgrimes 49137859Syar#include <sys/cdefs.h> 50137859Syar__FBSDID("$FreeBSD: head/libexec/ftpd/ftpcmd.y 137859 2004-11-18 13:46:29Z yar $"); 51137859Syar 521592Srgrimes#include <sys/param.h> 531592Srgrimes#include <sys/socket.h> 541592Srgrimes#include <sys/stat.h> 551592Srgrimes 561592Srgrimes#include <netinet/in.h> 571592Srgrimes#include <arpa/ftp.h> 581592Srgrimes 591592Srgrimes#include <ctype.h> 601592Srgrimes#include <errno.h> 611592Srgrimes#include <glob.h> 6292090Smaxim#include <libutil.h> 6392272Smaxim#include <limits.h> 6492090Smaxim#include <md5.h> 6556668Sshin#include <netdb.h> 661592Srgrimes#include <pwd.h> 671592Srgrimes#include <signal.h> 68132929Syar#include <stdint.h> 691592Srgrimes#include <stdio.h> 701592Srgrimes#include <stdlib.h> 711592Srgrimes#include <string.h> 721592Srgrimes#include <syslog.h> 731592Srgrimes#include <time.h> 741592Srgrimes#include <unistd.h> 751592Srgrimes 761592Srgrimes#include "extern.h" 77109380Syar#include "pathnames.h" 781592Srgrimes 7956668Sshinextern union sockunion data_dest, his_addr; 80110037Syarextern int hostinfo; 811592Srgrimesextern int logged_in; 821592Srgrimesextern struct passwd *pw; 831592Srgrimesextern int guest; 84110036Syarextern char *homedir; 8517435Spstextern int paranoid; 861592Srgrimesextern int logging; 871592Srgrimesextern int type; 881592Srgrimesextern int form; 8976096Smarkmextern int ftpdebug; 901592Srgrimesextern int timeout; 911592Srgrimesextern int maxtimeout; 921592Srgrimesextern int pdata; 9327650Sdavidnextern char *hostname; 941592Srgrimesextern char proctitle[]; 951592Srgrimesextern int usedefault; 961592Srgrimesextern char tmpline[]; 9770102Sphkextern int readonly; 9870102Sphkextern int noepsv; 9982460Snikextern int noretr; 10082796Ssheldonhextern int noguestretr; 101100684Syarextern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */ 1021592Srgrimes 1031592Srgrimesoff_t restart_point; 1041592Srgrimes 1051592Srgrimesstatic int cmd_type; 1061592Srgrimesstatic int cmd_form; 1071592Srgrimesstatic int cmd_bytesz; 10889935Syarstatic int state; 1091592Srgrimeschar cbuf[512]; 110132931Syarchar *fromname = NULL; 1111592Srgrimes 11256668Sshinextern int epsvall; 11356668Sshin 1141592Srgrimes%} 1151592Srgrimes 1161592Srgrimes%union { 11792272Smaxim struct { 11892272Smaxim off_t o; 11992272Smaxim int i; 12092272Smaxim } u; 1211592Srgrimes char *s; 1221592Srgrimes} 1231592Srgrimes 1241592Srgrimes%token 1251592Srgrimes A B C E F I 1261592Srgrimes L N P R S T 12756668Sshin ALL 1281592Srgrimes 1291592Srgrimes SP CRLF COMMA 1301592Srgrimes 1311592Srgrimes USER PASS ACCT REIN QUIT PORT 1321592Srgrimes PASV TYPE STRU MODE RETR STOR 1331592Srgrimes APPE MLFL MAIL MSND MSOM MSAM 1341592Srgrimes MRSQ MRCP ALLO REST RNFR RNTO 1351592Srgrimes ABOR DELE CWD LIST NLST SITE 1361592Srgrimes STAT HELP NOOP MKD RMD PWD 1371592Srgrimes CDUP STOU SMNT SYST SIZE MDTM 13856668Sshin LPRT LPSV EPRT EPSV 1391592Srgrimes 14075535Sphk UMASK IDLE CHMOD MDFIVE 1411592Srgrimes 142102565Syar LEXERR NOTIMPL 1431592Srgrimes 1441592Srgrimes%token <s> STRING 14592272Smaxim%token <u> NUMBER 1461592Srgrimes 14792272Smaxim%type <u.i> check_login octal_number byte_size 14892272Smaxim%type <u.i> check_login_ro check_login_epsv 14992272Smaxim%type <u.i> struct_code mode_code type_code form_code 15075567Speter%type <s> pathstring pathname password username 151102565Syar%type <s> ALL NOTIMPL 1521592Srgrimes 1531592Srgrimes%start cmd_list 1541592Srgrimes 1551592Srgrimes%% 1561592Srgrimes 1571592Srgrimescmd_list 1581592Srgrimes : /* empty */ 1591592Srgrimes | cmd_list cmd 1601592Srgrimes { 16188935Sdwmalone if (fromname) 16288935Sdwmalone free(fromname); 163132931Syar fromname = NULL; 164132930Syar restart_point = 0; 1651592Srgrimes } 1661592Srgrimes | cmd_list rcmd 1671592Srgrimes ; 1681592Srgrimes 1691592Srgrimescmd 1701592Srgrimes : USER SP username CRLF 1711592Srgrimes { 1721592Srgrimes user($3); 1731592Srgrimes free($3); 1741592Srgrimes } 1751592Srgrimes | PASS SP password CRLF 1761592Srgrimes { 1771592Srgrimes pass($3); 1781592Srgrimes free($3); 1791592Srgrimes } 18075556Sgreen | PASS CRLF 18175556Sgreen { 18275556Sgreen pass(""); 18375556Sgreen } 18417433Spst | PORT check_login SP host_port CRLF 1851592Srgrimes { 18656668Sshin if (epsvall) { 187137852Syar reply(501, "No PORT allowed after EPSV ALL."); 18856668Sshin goto port_done; 18956668Sshin } 19056668Sshin if (!$2) 19156668Sshin goto port_done; 19256668Sshin if (port_check("PORT") == 1) 19356668Sshin goto port_done; 19456668Sshin#ifdef INET6 19556668Sshin if ((his_addr.su_family != AF_INET6 || 19656668Sshin !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { 19756668Sshin /* shoud never happen */ 19856668Sshin usedefault = 1; 19956668Sshin reply(500, "Invalid address rejected."); 20056668Sshin goto port_done; 20156668Sshin } 20256668Sshin port_check_v6("pcmd"); 20356668Sshin#endif 20456668Sshin port_done: 205132925Syar ; 20656668Sshin } 20756668Sshin | LPRT check_login SP host_long_port CRLF 20856668Sshin { 20956668Sshin if (epsvall) { 210137852Syar reply(501, "No LPRT allowed after EPSV ALL."); 21156668Sshin goto lprt_done; 21256668Sshin } 21356668Sshin if (!$2) 21456668Sshin goto lprt_done; 21556668Sshin if (port_check("LPRT") == 1) 21656668Sshin goto lprt_done; 21756668Sshin#ifdef INET6 21856668Sshin if (his_addr.su_family != AF_INET6) { 21956668Sshin usedefault = 1; 22056668Sshin reply(500, "Invalid address rejected."); 22156668Sshin goto lprt_done; 22256668Sshin } 22356668Sshin if (port_check_v6("LPRT") == 1) 22456668Sshin goto lprt_done; 22556668Sshin#endif 22656668Sshin lprt_done: 227132925Syar ; 22856668Sshin } 22956668Sshin | EPRT check_login SP STRING CRLF 23056668Sshin { 23156668Sshin char delim; 23256668Sshin char *tmp = NULL; 23356668Sshin char *p, *q; 23456668Sshin char *result[3]; 23556668Sshin struct addrinfo hints; 23656668Sshin struct addrinfo *res; 23756668Sshin int i; 23856668Sshin 23956668Sshin if (epsvall) { 240137852Syar reply(501, "No EPRT allowed after EPSV ALL."); 24156668Sshin goto eprt_done; 24256668Sshin } 24356668Sshin if (!$2) 24456668Sshin goto eprt_done; 24556668Sshin 24656668Sshin memset(&data_dest, 0, sizeof(data_dest)); 24756668Sshin tmp = strdup($4); 24876096Smarkm if (ftpdebug) 24956668Sshin syslog(LOG_DEBUG, "%s", tmp); 25056668Sshin if (!tmp) { 25176096Smarkm fatalerror("not enough core"); 25256668Sshin /*NOTREACHED*/ 25356668Sshin } 25456668Sshin p = tmp; 25556668Sshin delim = p[0]; 25656668Sshin p++; 25756668Sshin memset(result, 0, sizeof(result)); 25856668Sshin for (i = 0; i < 3; i++) { 25956668Sshin q = strchr(p, delim); 26056668Sshin if (!q || *q != delim) { 26156668Sshin parsefail: 26256668Sshin reply(500, 26356668Sshin "Invalid argument, rejected."); 26456668Sshin if (tmp) 26556668Sshin free(tmp); 26617433Spst usedefault = 1; 26756668Sshin goto eprt_done; 26817433Spst } 26956668Sshin *q++ = '\0'; 27056668Sshin result[i] = p; 27176096Smarkm if (ftpdebug) 27256668Sshin syslog(LOG_DEBUG, "%d: %s", i, p); 27356668Sshin p = q; 2741592Srgrimes } 27556668Sshin 27656668Sshin /* some more sanity check */ 27756668Sshin p = result[0]; 27856668Sshin while (*p) { 27956668Sshin if (!isdigit(*p)) 28056668Sshin goto parsefail; 28156668Sshin p++; 28256668Sshin } 28356668Sshin p = result[2]; 28456668Sshin while (*p) { 28556668Sshin if (!isdigit(*p)) 28656668Sshin goto parsefail; 28756668Sshin p++; 28856668Sshin } 28956668Sshin 29056668Sshin /* grab address */ 29156668Sshin memset(&hints, 0, sizeof(hints)); 29256668Sshin if (atoi(result[0]) == 1) 29356668Sshin hints.ai_family = PF_INET; 29456668Sshin#ifdef INET6 29556668Sshin else if (atoi(result[0]) == 2) 29656668Sshin hints.ai_family = PF_INET6; 29756668Sshin#endif 29856668Sshin else 29956668Sshin hints.ai_family = PF_UNSPEC; /*XXX*/ 30056668Sshin hints.ai_socktype = SOCK_STREAM; 30156668Sshin i = getaddrinfo(result[1], result[2], &hints, &res); 30256668Sshin if (i) 30356668Sshin goto parsefail; 30456668Sshin memcpy(&data_dest, res->ai_addr, res->ai_addrlen); 30556668Sshin#ifdef INET6 30656668Sshin if (his_addr.su_family == AF_INET6 30756668Sshin && data_dest.su_family == AF_INET6) { 30856668Sshin /* XXX more sanity checks! */ 30956668Sshin data_dest.su_sin6.sin6_scope_id = 31056668Sshin his_addr.su_sin6.sin6_scope_id; 31156668Sshin } 31256668Sshin#endif 31356668Sshin free(tmp); 31456668Sshin tmp = NULL; 31556668Sshin 31656668Sshin if (port_check("EPRT") == 1) 31756668Sshin goto eprt_done; 31856668Sshin#ifdef INET6 31956668Sshin if (his_addr.su_family != AF_INET6) { 32056668Sshin usedefault = 1; 32156668Sshin reply(500, "Invalid address rejected."); 32256668Sshin goto eprt_done; 32356668Sshin } 32456668Sshin if (port_check_v6("EPRT") == 1) 32556668Sshin goto eprt_done; 32656668Sshin#endif 32788935Sdwmalone eprt_done: 32888935Sdwmalone free($4); 3291592Srgrimes } 33017433Spst | PASV check_login CRLF 3311592Srgrimes { 33256668Sshin if (epsvall) 333137852Syar reply(501, "No PASV allowed after EPSV ALL."); 33456668Sshin else if ($2) 33517433Spst passive(); 3361592Srgrimes } 33756668Sshin | LPSV check_login CRLF 33856668Sshin { 33956668Sshin if (epsvall) 340137852Syar reply(501, "No LPSV allowed after EPSV ALL."); 34156668Sshin else if ($2) 34256668Sshin long_passive("LPSV", PF_UNSPEC); 34356668Sshin } 34470102Sphk | EPSV check_login_epsv SP NUMBER CRLF 34556668Sshin { 34656668Sshin if ($2) { 34756668Sshin int pf; 34892272Smaxim switch ($4.i) { 34956668Sshin case 1: 35056668Sshin pf = PF_INET; 35156668Sshin break; 35256668Sshin#ifdef INET6 35356668Sshin case 2: 35456668Sshin pf = PF_INET6; 35556668Sshin break; 35656668Sshin#endif 35756668Sshin default: 35856668Sshin pf = -1; /*junk value*/ 35956668Sshin break; 36056668Sshin } 36156668Sshin long_passive("EPSV", pf); 36256668Sshin } 36356668Sshin } 36470102Sphk | EPSV check_login_epsv SP ALL CRLF 36556668Sshin { 36656668Sshin if ($2) { 367137852Syar reply(200, "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: 419137852Syar reply(200, "STRU F accepted."); 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: 433137852Syar reply(200, "MODE S accepted."); 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)) 456137852Syar reply(500, "RETR command 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); 629137852Syar 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)) { 638137852Syar reply(501, "Bad UMASK value."); 6391592Srgrimes } else { 6401592Srgrimes oldmask = umask($6); 6411592Srgrimes reply(200, 642137852Syar "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)) 651137852Syar 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, 664137852Syar "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, 672137852Syar "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, 678137852Syar "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) 734137850Syar perror_reply(550, $4); 7351592Srgrimes else if (!S_ISREG(stbuf.st_mode)) { 7361592Srgrimes reply(550, "%s: not a plain file.", $4); 7371592Srgrimes } else { 7381592Srgrimes struct tm *t; 7391592Srgrimes t = gmtime(&stbuf.st_mtime); 7401592Srgrimes reply(213, 74117435Spst "%04d%02d%02d%02d%02d%02d", 74217435Spst 1900 + t->tm_year, 74317435Spst t->tm_mon+1, t->tm_mday, 7441592Srgrimes t->tm_hour, t->tm_min, t->tm_sec); 7451592Srgrimes } 7461592Srgrimes } 7471592Srgrimes if ($4 != NULL) 7481592Srgrimes free($4); 7491592Srgrimes } 7501592Srgrimes | QUIT CRLF 7511592Srgrimes { 7521592Srgrimes reply(221, "Goodbye."); 7531592Srgrimes dologout(0); 7541592Srgrimes } 755102565Syar | NOTIMPL 756102565Syar { 757102565Syar nack($1); 758102565Syar } 75989935Syar | error 7601592Srgrimes { 76189935Syar yyclearin; /* discard lookahead data */ 76289935Syar yyerrok; /* clear error condition */ 763102565Syar state = CMD; /* reset lexer state */ 7641592Srgrimes } 7651592Srgrimes ; 7661592Srgrimesrcmd 76770102Sphk : RNFR check_login_ro SP pathname CRLF 7681592Srgrimes { 769132930Syar restart_point = 0; 7701592Srgrimes if ($2 && $4) { 77188935Sdwmalone if (fromname) 77288935Sdwmalone free(fromname); 773132931Syar fromname = NULL; 77488935Sdwmalone if (renamefrom($4)) 77588935Sdwmalone fromname = $4; 77688935Sdwmalone else 7771592Srgrimes free($4); 77888935Sdwmalone } else if ($4) { 77988935Sdwmalone free($4); 7801592Srgrimes } 7811592Srgrimes } 78292272Smaxim | REST check_login SP NUMBER CRLF 7831592Srgrimes { 78471278Sjedgar if ($2) { 78588935Sdwmalone if (fromname) 78688935Sdwmalone free(fromname); 787132931Syar fromname = NULL; 78892272Smaxim restart_point = $4.o; 789132929Syar reply(350, "Restarting at %jd. %s", 790132929Syar (intmax_t)restart_point, 79171278Sjedgar "Send STORE or RETRIEVE to initiate transfer."); 79271278Sjedgar } 7931592Srgrimes } 7941592Srgrimes ; 7951592Srgrimes 7961592Srgrimesusername 7971592Srgrimes : STRING 7981592Srgrimes ; 7991592Srgrimes 8001592Srgrimespassword 8011592Srgrimes : /* empty */ 8021592Srgrimes { 8031592Srgrimes $$ = (char *)calloc(1, sizeof(char)); 8041592Srgrimes } 8051592Srgrimes | STRING 8061592Srgrimes ; 8071592Srgrimes 8081592Srgrimesbyte_size 8091592Srgrimes : NUMBER 81092272Smaxim { 81192272Smaxim $$ = $1.i; 81292272Smaxim } 8131592Srgrimes ; 8141592Srgrimes 8151592Srgrimeshost_port 8161592Srgrimes : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 8171592Srgrimes NUMBER COMMA NUMBER 8181592Srgrimes { 8191592Srgrimes char *a, *p; 8201592Srgrimes 82156668Sshin data_dest.su_len = sizeof(struct sockaddr_in); 82256668Sshin data_dest.su_family = AF_INET; 82356668Sshin p = (char *)&data_dest.su_sin.sin_port; 82492272Smaxim p[0] = $9.i; p[1] = $11.i; 82556668Sshin a = (char *)&data_dest.su_sin.sin_addr; 82692272Smaxim a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; 8271592Srgrimes } 8281592Srgrimes ; 8291592Srgrimes 83056668Sshinhost_long_port 83156668Sshin : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83256668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83356668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83456668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83556668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83656668Sshin NUMBER 83756668Sshin { 83856668Sshin char *a, *p; 83956668Sshin 84056668Sshin memset(&data_dest, 0, sizeof(data_dest)); 84156668Sshin data_dest.su_len = sizeof(struct sockaddr_in6); 84256668Sshin data_dest.su_family = AF_INET6; 84356668Sshin p = (char *)&data_dest.su_port; 84492272Smaxim p[0] = $39.i; p[1] = $41.i; 84556668Sshin a = (char *)&data_dest.su_sin6.sin6_addr; 84692272Smaxim a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 84792272Smaxim a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i; 84892272Smaxim a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i; 84992272Smaxim a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i; 85056668Sshin if (his_addr.su_family == AF_INET6) { 85156668Sshin /* XXX more sanity checks! */ 85256668Sshin data_dest.su_sin6.sin6_scope_id = 85356668Sshin his_addr.su_sin6.sin6_scope_id; 85456668Sshin } 85592272Smaxim if ($1.i != 6 || $3.i != 16 || $37.i != 2) 85656668Sshin memset(&data_dest, 0, sizeof(data_dest)); 85756668Sshin } 85856668Sshin | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 85956668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 86056668Sshin NUMBER 86156668Sshin { 86256668Sshin char *a, *p; 86356668Sshin 86456668Sshin memset(&data_dest, 0, sizeof(data_dest)); 86556668Sshin data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 86656668Sshin data_dest.su_family = AF_INET; 86756668Sshin p = (char *)&data_dest.su_port; 86892272Smaxim p[0] = $15.i; p[1] = $17.i; 86956668Sshin a = (char *)&data_dest.su_sin.sin_addr; 87092272Smaxim a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 87192272Smaxim if ($1.i != 4 || $3.i != 4 || $13.i != 2) 87256668Sshin memset(&data_dest, 0, sizeof(data_dest)); 87356668Sshin } 87456668Sshin ; 87556668Sshin 8761592Srgrimesform_code 8771592Srgrimes : N 8781592Srgrimes { 8791592Srgrimes $$ = FORM_N; 8801592Srgrimes } 8811592Srgrimes | T 8821592Srgrimes { 8831592Srgrimes $$ = FORM_T; 8841592Srgrimes } 8851592Srgrimes | C 8861592Srgrimes { 8871592Srgrimes $$ = FORM_C; 8881592Srgrimes } 8891592Srgrimes ; 8901592Srgrimes 8911592Srgrimestype_code 8921592Srgrimes : A 8931592Srgrimes { 8941592Srgrimes cmd_type = TYPE_A; 8951592Srgrimes cmd_form = FORM_N; 8961592Srgrimes } 8971592Srgrimes | A SP form_code 8981592Srgrimes { 8991592Srgrimes cmd_type = TYPE_A; 9001592Srgrimes cmd_form = $3; 9011592Srgrimes } 9021592Srgrimes | E 9031592Srgrimes { 9041592Srgrimes cmd_type = TYPE_E; 9051592Srgrimes cmd_form = FORM_N; 9061592Srgrimes } 9071592Srgrimes | E SP form_code 9081592Srgrimes { 9091592Srgrimes cmd_type = TYPE_E; 9101592Srgrimes cmd_form = $3; 9111592Srgrimes } 9121592Srgrimes | I 9131592Srgrimes { 9141592Srgrimes cmd_type = TYPE_I; 9151592Srgrimes } 9161592Srgrimes | L 9171592Srgrimes { 9181592Srgrimes cmd_type = TYPE_L; 919103949Smike cmd_bytesz = CHAR_BIT; 9201592Srgrimes } 9211592Srgrimes | L SP byte_size 9221592Srgrimes { 9231592Srgrimes cmd_type = TYPE_L; 9241592Srgrimes cmd_bytesz = $3; 9251592Srgrimes } 9261592Srgrimes /* this is for a bug in the BBN ftp */ 9271592Srgrimes | L byte_size 9281592Srgrimes { 9291592Srgrimes cmd_type = TYPE_L; 9301592Srgrimes cmd_bytesz = $2; 9311592Srgrimes } 9321592Srgrimes ; 9331592Srgrimes 9341592Srgrimesstruct_code 9351592Srgrimes : F 9361592Srgrimes { 9371592Srgrimes $$ = STRU_F; 9381592Srgrimes } 9391592Srgrimes | R 9401592Srgrimes { 9411592Srgrimes $$ = STRU_R; 9421592Srgrimes } 9431592Srgrimes | P 9441592Srgrimes { 9451592Srgrimes $$ = STRU_P; 9461592Srgrimes } 9471592Srgrimes ; 9481592Srgrimes 9491592Srgrimesmode_code 9501592Srgrimes : S 9511592Srgrimes { 9521592Srgrimes $$ = MODE_S; 9531592Srgrimes } 9541592Srgrimes | B 9551592Srgrimes { 9561592Srgrimes $$ = MODE_B; 9571592Srgrimes } 9581592Srgrimes | C 9591592Srgrimes { 9601592Srgrimes $$ = MODE_C; 9611592Srgrimes } 9621592Srgrimes ; 9631592Srgrimes 9641592Srgrimespathname 9651592Srgrimes : pathstring 9661592Srgrimes { 96775567Speter if (logged_in && $1) { 968110340Syar char *p; 9691592Srgrimes 970110340Syar /* 971110340Syar * Expand ~user manually since glob(3) 972110340Syar * will return the unexpanded pathname 973110340Syar * if the corresponding file/directory 974110340Syar * doesn't exist yet. Using sole glob(3) 975110340Syar * would break natural commands like 976110340Syar * MKD ~user/newdir 977110340Syar * or 978110340Syar * RNTO ~/newfile 979110340Syar */ 980110340Syar if ((p = exptilde($1)) != NULL) { 981110340Syar $$ = expglob(p); 982110340Syar free(p); 983110340Syar } else 9841592Srgrimes $$ = NULL; 9851592Srgrimes free($1); 9861592Srgrimes } else 9871592Srgrimes $$ = $1; 9881592Srgrimes } 9891592Srgrimes ; 9901592Srgrimes 9911592Srgrimespathstring 9921592Srgrimes : STRING 9931592Srgrimes ; 9941592Srgrimes 9951592Srgrimesoctal_number 9961592Srgrimes : NUMBER 9971592Srgrimes { 9981592Srgrimes int ret, dec, multby, digit; 9991592Srgrimes 10001592Srgrimes /* 10011592Srgrimes * Convert a number that was read as decimal number 10021592Srgrimes * to what it would be if it had been read as octal. 10031592Srgrimes */ 100492272Smaxim dec = $1.i; 10051592Srgrimes multby = 1; 10061592Srgrimes ret = 0; 10071592Srgrimes while (dec) { 10081592Srgrimes digit = dec%10; 10091592Srgrimes if (digit > 7) { 10101592Srgrimes ret = -1; 10111592Srgrimes break; 10121592Srgrimes } 10131592Srgrimes ret += digit * multby; 10141592Srgrimes multby *= 8; 10151592Srgrimes dec /= 10; 10161592Srgrimes } 10171592Srgrimes $$ = ret; 10181592Srgrimes } 10191592Srgrimes ; 10201592Srgrimes 10211592Srgrimes 10221592Srgrimescheck_login 10231592Srgrimes : /* empty */ 10241592Srgrimes { 102570102Sphk $$ = check_login1(); 10261592Srgrimes } 10271592Srgrimes ; 10281592Srgrimes 102970102Sphkcheck_login_epsv 103070102Sphk : /* empty */ 103170102Sphk { 103270102Sphk if (noepsv) { 1033137852Syar reply(500, "EPSV command disabled."); 103470102Sphk $$ = 0; 103570102Sphk } 103670102Sphk else 103770102Sphk $$ = check_login1(); 103870102Sphk } 103970102Sphk ; 104070102Sphk 104170102Sphkcheck_login_ro 104270102Sphk : /* empty */ 104370102Sphk { 104470102Sphk if (readonly) { 104572710Sdes reply(550, "Permission denied."); 104670102Sphk $$ = 0; 104770102Sphk } 104870102Sphk else 104970102Sphk $$ = check_login1(); 105070102Sphk } 105170102Sphk ; 105270102Sphk 10531592Srgrimes%% 10541592Srgrimes 10551592Srgrimes#define CMD 0 /* beginning of command */ 10561592Srgrimes#define ARGS 1 /* expect miscellaneous arguments */ 10571592Srgrimes#define STR1 2 /* expect SP followed by STRING */ 10581592Srgrimes#define STR2 3 /* expect STRING */ 10591592Srgrimes#define OSTR 4 /* optional SP then STRING */ 106075556Sgreen#define ZSTR1 5 /* optional SP then optional STRING */ 10611592Srgrimes#define ZSTR2 6 /* optional STRING after SP */ 10621592Srgrimes#define SITECMD 7 /* SITE command */ 10631592Srgrimes#define NSTR 8 /* Number followed by a string */ 10641592Srgrimes 106575560Sjedgar#define MAXGLOBARGS 1000 106675560Sjedgar 1067101034Syar#define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */ 1068101034Syar 10691592Srgrimesstruct tab { 10701592Srgrimes char *name; 10711592Srgrimes short token; 10721592Srgrimes short state; 10731592Srgrimes short implemented; /* 1 if command is implemented */ 10741592Srgrimes char *help; 10751592Srgrimes}; 10761592Srgrimes 10771592Srgrimesstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 10781592Srgrimes { "USER", USER, STR1, 1, "<sp> username" }, 107975556Sgreen { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" }, 10801592Srgrimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 10811592Srgrimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 10821592Srgrimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 10831592Srgrimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 1084101806Syar { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" }, 108556668Sshin { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 108656668Sshin { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 10871592Srgrimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 108856668Sshin { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 108956668Sshin { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 1090101806Syar { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" }, 10911592Srgrimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 10921592Srgrimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 10931592Srgrimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 10941592Srgrimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 10951592Srgrimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 10961592Srgrimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 10971592Srgrimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 10981592Srgrimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 10991592Srgrimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 11001592Srgrimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 11011592Srgrimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 11021592Srgrimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 11031592Srgrimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 11041592Srgrimes { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 11051592Srgrimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 11061592Srgrimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 11071592Srgrimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 11081592Srgrimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 11091592Srgrimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 11101592Srgrimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 11111592Srgrimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 11121592Srgrimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 11131592Srgrimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 11141592Srgrimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 11151592Srgrimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 11161592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11171592Srgrimes { "NOOP", NOOP, ARGS, 1, "" }, 11181592Srgrimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 11191592Srgrimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 11201592Srgrimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 11211592Srgrimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 11221592Srgrimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 11231592Srgrimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 11241592Srgrimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11251592Srgrimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11261592Srgrimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 11271592Srgrimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 11281592Srgrimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 11291592Srgrimes { NULL, 0, 0, 0, 0 } 11301592Srgrimes}; 11311592Srgrimes 11321592Srgrimesstruct tab sitetab[] = { 113375535Sphk { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" }, 11341592Srgrimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 11351592Srgrimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 11361592Srgrimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 11371592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11381592Srgrimes { NULL, 0, 0, 0, 0 } 11391592Srgrimes}; 11401592Srgrimes 114190148Simpstatic char *copy(char *); 1142110340Syarstatic char *expglob(char *); 1143110340Syarstatic char *exptilde(char *); 114490148Simpstatic void help(struct tab *, char *); 11451592Srgrimesstatic struct tab * 114690148Simp lookup(struct tab *, char *); 114790148Simpstatic int port_check(const char *); 114890148Simpstatic int port_check_v6(const char *); 114990148Simpstatic void sizecmd(char *); 115090148Simpstatic void toolong(int); 115190148Simpstatic void v4map_data_dest(void); 115290148Simpstatic int yylex(void); 11531592Srgrimes 11541592Srgrimesstatic struct tab * 115590148Simplookup(struct tab *p, char *cmd) 11561592Srgrimes{ 11571592Srgrimes 11581592Srgrimes for (; p->name != NULL; p++) 11591592Srgrimes if (strcmp(cmd, p->name) == 0) 11601592Srgrimes return (p); 11611592Srgrimes return (0); 11621592Srgrimes} 11631592Srgrimes 11641592Srgrimes#include <arpa/telnet.h> 11651592Srgrimes 11661592Srgrimes/* 11671592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes. 11681592Srgrimes */ 11691592Srgrimeschar * 117090148Simpgetline(char *s, int n, FILE *iop) 11711592Srgrimes{ 11721592Srgrimes int c; 11731592Srgrimes register char *cs; 1174117352Syar sigset_t sset, osset; 11751592Srgrimes 11761592Srgrimes cs = s; 11771592Srgrimes/* tmpline may contain saved command from urgent mode interruption */ 11781592Srgrimes for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 11791592Srgrimes *cs++ = tmpline[c]; 11801592Srgrimes if (tmpline[c] == '\n') { 11811592Srgrimes *cs++ = '\0'; 118276096Smarkm if (ftpdebug) 11831592Srgrimes syslog(LOG_DEBUG, "command: %s", s); 11841592Srgrimes tmpline[0] = '\0'; 11851592Srgrimes return(s); 11861592Srgrimes } 11871592Srgrimes if (c == 0) 11881592Srgrimes tmpline[0] = '\0'; 11891592Srgrimes } 1190117352Syar /* SIGURG would interrupt stdio if not blocked during the read loop */ 1191117352Syar sigemptyset(&sset); 1192117352Syar sigaddset(&sset, SIGURG); 1193117352Syar sigprocmask(SIG_BLOCK, &sset, &osset); 11941592Srgrimes while ((c = getc(iop)) != EOF) { 11951592Srgrimes c &= 0377; 11961592Srgrimes if (c == IAC) { 1197117351Syar if ((c = getc(iop)) == EOF) 1198117351Syar goto got_eof; 11991592Srgrimes c &= 0377; 12001592Srgrimes switch (c) { 12011592Srgrimes case WILL: 12021592Srgrimes case WONT: 1203117351Syar if ((c = getc(iop)) == EOF) 1204117351Syar goto got_eof; 12051592Srgrimes printf("%c%c%c", IAC, DONT, 0377&c); 12061592Srgrimes (void) fflush(stdout); 12071592Srgrimes continue; 12081592Srgrimes case DO: 12091592Srgrimes case DONT: 1210117351Syar if ((c = getc(iop)) == EOF) 1211117351Syar goto got_eof; 12121592Srgrimes printf("%c%c%c", IAC, WONT, 0377&c); 12131592Srgrimes (void) fflush(stdout); 12141592Srgrimes continue; 12151592Srgrimes case IAC: 12161592Srgrimes break; 12171592Srgrimes default: 12181592Srgrimes continue; /* ignore command */ 12191592Srgrimes } 12201592Srgrimes } 12211592Srgrimes *cs++ = c; 12221592Srgrimes if (--n <= 0 || c == '\n') 12231592Srgrimes break; 12241592Srgrimes } 1225117351Syargot_eof: 1226117352Syar sigprocmask(SIG_SETMASK, &osset, NULL); 12271592Srgrimes if (c == EOF && cs == s) 12281592Srgrimes return (NULL); 12291592Srgrimes *cs++ = '\0'; 123076096Smarkm if (ftpdebug) { 12311592Srgrimes if (!guest && strncasecmp("pass ", s, 5) == 0) { 12321592Srgrimes /* Don't syslog passwords */ 12331592Srgrimes syslog(LOG_DEBUG, "command: %.5s ???", s); 12341592Srgrimes } else { 12351592Srgrimes register char *cp; 12361592Srgrimes register int len; 12371592Srgrimes 12381592Srgrimes /* Don't syslog trailing CR-LF */ 12391592Srgrimes len = strlen(s); 12401592Srgrimes cp = s + len - 1; 12411592Srgrimes while (cp >= s && (*cp == '\n' || *cp == '\r')) { 12421592Srgrimes --cp; 12431592Srgrimes --len; 12441592Srgrimes } 12451592Srgrimes syslog(LOG_DEBUG, "command: %.*s", len, s); 12461592Srgrimes } 12471592Srgrimes } 12481592Srgrimes return (s); 12491592Srgrimes} 12501592Srgrimes 12511592Srgrimesstatic void 125290148Simptoolong(int signo) 12531592Srgrimes{ 12541592Srgrimes 12551592Srgrimes reply(421, 12561592Srgrimes "Timeout (%d seconds): closing control connection.", timeout); 12571592Srgrimes if (logging) 12581592Srgrimes syslog(LOG_INFO, "User %s timed out after %d seconds", 12591592Srgrimes (pw ? pw -> pw_name : "unknown"), timeout); 12601592Srgrimes dologout(1); 12611592Srgrimes} 12621592Srgrimes 12631592Srgrimesstatic int 126490148Simpyylex(void) 12651592Srgrimes{ 126689935Syar static int cpos; 12671592Srgrimes char *cp, *cp2; 12681592Srgrimes struct tab *p; 12691592Srgrimes int n; 12701592Srgrimes char c; 12711592Srgrimes 12721592Srgrimes for (;;) { 12731592Srgrimes switch (state) { 12741592Srgrimes 12751592Srgrimes case CMD: 12761592Srgrimes (void) signal(SIGALRM, toolong); 1277137659Syar (void) alarm(timeout); 12781592Srgrimes if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 12791592Srgrimes reply(221, "You could at least say goodbye."); 12801592Srgrimes dologout(0); 12811592Srgrimes } 12821592Srgrimes (void) alarm(0); 12831592Srgrimes#ifdef SETPROCTITLE 128429574Sphk if (strncasecmp(cbuf, "PASS", 4) != 0) 12851592Srgrimes setproctitle("%s: %s", proctitle, cbuf); 12861592Srgrimes#endif /* SETPROCTITLE */ 12871592Srgrimes if ((cp = strchr(cbuf, '\r'))) { 12881592Srgrimes *cp++ = '\n'; 12891592Srgrimes *cp = '\0'; 12901592Srgrimes } 12911592Srgrimes if ((cp = strpbrk(cbuf, " \n"))) 12921592Srgrimes cpos = cp - cbuf; 12931592Srgrimes if (cpos == 0) 12941592Srgrimes cpos = 4; 12951592Srgrimes c = cbuf[cpos]; 12961592Srgrimes cbuf[cpos] = '\0'; 12971592Srgrimes upper(cbuf); 12981592Srgrimes p = lookup(cmdtab, cbuf); 12991592Srgrimes cbuf[cpos] = c; 13003776Spst if (p != 0) { 1301102565Syar yylval.s = p->name; 1302102565Syar if (!p->implemented) 1303102565Syar return (NOTIMPL); /* state remains CMD */ 13041592Srgrimes state = p->state; 13051592Srgrimes return (p->token); 13061592Srgrimes } 13071592Srgrimes break; 13081592Srgrimes 13091592Srgrimes case SITECMD: 13101592Srgrimes if (cbuf[cpos] == ' ') { 13111592Srgrimes cpos++; 13121592Srgrimes return (SP); 13131592Srgrimes } 13141592Srgrimes cp = &cbuf[cpos]; 13151592Srgrimes if ((cp2 = strpbrk(cp, " \n"))) 13161592Srgrimes cpos = cp2 - cbuf; 13171592Srgrimes c = cbuf[cpos]; 13181592Srgrimes cbuf[cpos] = '\0'; 13191592Srgrimes upper(cp); 13201592Srgrimes p = lookup(sitetab, cp); 13211592Srgrimes cbuf[cpos] = c; 13223777Spst if (guest == 0 && p != 0) { 1323102565Syar yylval.s = p->name; 1324102565Syar if (!p->implemented) { 13251592Srgrimes state = CMD; 1326102565Syar return (NOTIMPL); 13271592Srgrimes } 13281592Srgrimes state = p->state; 13291592Srgrimes return (p->token); 13301592Srgrimes } 13311592Srgrimes state = CMD; 13321592Srgrimes break; 13331592Srgrimes 133475556Sgreen case ZSTR1: 13351592Srgrimes case OSTR: 13361592Srgrimes if (cbuf[cpos] == '\n') { 13371592Srgrimes state = CMD; 13381592Srgrimes return (CRLF); 13391592Srgrimes } 13401592Srgrimes /* FALLTHROUGH */ 13411592Srgrimes 13421592Srgrimes case STR1: 13431592Srgrimes dostr1: 13441592Srgrimes if (cbuf[cpos] == ' ') { 13451592Srgrimes cpos++; 134651979Salfred state = state == OSTR ? STR2 : state+1; 13471592Srgrimes return (SP); 13481592Srgrimes } 13491592Srgrimes break; 13501592Srgrimes 13511592Srgrimes case ZSTR2: 13521592Srgrimes if (cbuf[cpos] == '\n') { 13531592Srgrimes state = CMD; 13541592Srgrimes return (CRLF); 13551592Srgrimes } 13561592Srgrimes /* FALLTHROUGH */ 13571592Srgrimes 13581592Srgrimes case STR2: 13591592Srgrimes cp = &cbuf[cpos]; 13601592Srgrimes n = strlen(cp); 13611592Srgrimes cpos += n - 1; 13621592Srgrimes /* 13631592Srgrimes * Make sure the string is nonempty and \n terminated. 13641592Srgrimes */ 13651592Srgrimes if (n > 1 && cbuf[cpos] == '\n') { 13661592Srgrimes cbuf[cpos] = '\0'; 13671592Srgrimes yylval.s = copy(cp); 13681592Srgrimes cbuf[cpos] = '\n'; 13691592Srgrimes state = ARGS; 13701592Srgrimes return (STRING); 13711592Srgrimes } 13721592Srgrimes break; 13731592Srgrimes 13741592Srgrimes case NSTR: 13751592Srgrimes if (cbuf[cpos] == ' ') { 13761592Srgrimes cpos++; 13771592Srgrimes return (SP); 13781592Srgrimes } 13791592Srgrimes if (isdigit(cbuf[cpos])) { 13801592Srgrimes cp = &cbuf[cpos]; 13811592Srgrimes while (isdigit(cbuf[++cpos])) 13821592Srgrimes ; 13831592Srgrimes c = cbuf[cpos]; 13841592Srgrimes cbuf[cpos] = '\0'; 138592272Smaxim yylval.u.i = atoi(cp); 13861592Srgrimes cbuf[cpos] = c; 13871592Srgrimes state = STR1; 13881592Srgrimes return (NUMBER); 13891592Srgrimes } 13901592Srgrimes state = STR1; 13911592Srgrimes goto dostr1; 13921592Srgrimes 13931592Srgrimes case ARGS: 13941592Srgrimes if (isdigit(cbuf[cpos])) { 13951592Srgrimes cp = &cbuf[cpos]; 13961592Srgrimes while (isdigit(cbuf[++cpos])) 13971592Srgrimes ; 13981592Srgrimes c = cbuf[cpos]; 13991592Srgrimes cbuf[cpos] = '\0'; 140092272Smaxim yylval.u.i = atoi(cp); 1401137811Syar yylval.u.o = strtoull(cp, NULL, 10); 14021592Srgrimes cbuf[cpos] = c; 14031592Srgrimes return (NUMBER); 14041592Srgrimes } 140556668Sshin if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 140656668Sshin && !isalnum(cbuf[cpos + 3])) { 140756668Sshin cpos += 3; 140856668Sshin return ALL; 140956668Sshin } 14101592Srgrimes switch (cbuf[cpos++]) { 14111592Srgrimes 14121592Srgrimes case '\n': 14131592Srgrimes state = CMD; 14141592Srgrimes return (CRLF); 14151592Srgrimes 14161592Srgrimes case ' ': 14171592Srgrimes return (SP); 14181592Srgrimes 14191592Srgrimes case ',': 14201592Srgrimes return (COMMA); 14211592Srgrimes 14221592Srgrimes case 'A': 14231592Srgrimes case 'a': 14241592Srgrimes return (A); 14251592Srgrimes 14261592Srgrimes case 'B': 14271592Srgrimes case 'b': 14281592Srgrimes return (B); 14291592Srgrimes 14301592Srgrimes case 'C': 14311592Srgrimes case 'c': 14321592Srgrimes return (C); 14331592Srgrimes 14341592Srgrimes case 'E': 14351592Srgrimes case 'e': 14361592Srgrimes return (E); 14371592Srgrimes 14381592Srgrimes case 'F': 14391592Srgrimes case 'f': 14401592Srgrimes return (F); 14411592Srgrimes 14421592Srgrimes case 'I': 14431592Srgrimes case 'i': 14441592Srgrimes return (I); 14451592Srgrimes 14461592Srgrimes case 'L': 14471592Srgrimes case 'l': 14481592Srgrimes return (L); 14491592Srgrimes 14501592Srgrimes case 'N': 14511592Srgrimes case 'n': 14521592Srgrimes return (N); 14531592Srgrimes 14541592Srgrimes case 'P': 14551592Srgrimes case 'p': 14561592Srgrimes return (P); 14571592Srgrimes 14581592Srgrimes case 'R': 14591592Srgrimes case 'r': 14601592Srgrimes return (R); 14611592Srgrimes 14621592Srgrimes case 'S': 14631592Srgrimes case 's': 14641592Srgrimes return (S); 14651592Srgrimes 14661592Srgrimes case 'T': 14671592Srgrimes case 't': 14681592Srgrimes return (T); 14691592Srgrimes 14701592Srgrimes } 14711592Srgrimes break; 14721592Srgrimes 14731592Srgrimes default: 147476096Smarkm fatalerror("Unknown state in scanner."); 14751592Srgrimes } 14761592Srgrimes state = CMD; 147789935Syar return (LEXERR); 14781592Srgrimes } 14791592Srgrimes} 14801592Srgrimes 14811592Srgrimesvoid 148290148Simpupper(char *s) 14831592Srgrimes{ 14841592Srgrimes while (*s != '\0') { 14851592Srgrimes if (islower(*s)) 14861592Srgrimes *s = toupper(*s); 14871592Srgrimes s++; 14881592Srgrimes } 14891592Srgrimes} 14901592Srgrimes 14911592Srgrimesstatic char * 149290148Simpcopy(char *s) 14931592Srgrimes{ 14941592Srgrimes char *p; 14951592Srgrimes 1496137659Syar p = malloc(strlen(s) + 1); 14971592Srgrimes if (p == NULL) 149876096Smarkm fatalerror("Ran out of memory."); 14991592Srgrimes (void) strcpy(p, s); 15001592Srgrimes return (p); 15011592Srgrimes} 15021592Srgrimes 15031592Srgrimesstatic void 150490148Simphelp(struct tab *ctab, char *s) 15051592Srgrimes{ 15061592Srgrimes struct tab *c; 15071592Srgrimes int width, NCMDS; 15081592Srgrimes char *type; 15091592Srgrimes 15101592Srgrimes if (ctab == sitetab) 15111592Srgrimes type = "SITE "; 15121592Srgrimes else 15131592Srgrimes type = ""; 15141592Srgrimes width = 0, NCMDS = 0; 15151592Srgrimes for (c = ctab; c->name != NULL; c++) { 15161592Srgrimes int len = strlen(c->name); 15171592Srgrimes 15181592Srgrimes if (len > width) 15191592Srgrimes width = len; 15201592Srgrimes NCMDS++; 15211592Srgrimes } 15221592Srgrimes width = (width + 8) &~ 7; 15231592Srgrimes if (s == 0) { 15241592Srgrimes int i, j, w; 15251592Srgrimes int columns, lines; 15261592Srgrimes 15271592Srgrimes lreply(214, "The following %scommands are recognized %s.", 15281592Srgrimes type, "(* =>'s unimplemented)"); 15291592Srgrimes columns = 76 / width; 15301592Srgrimes if (columns == 0) 15311592Srgrimes columns = 1; 15321592Srgrimes lines = (NCMDS + columns - 1) / columns; 15331592Srgrimes for (i = 0; i < lines; i++) { 15341592Srgrimes printf(" "); 15351592Srgrimes for (j = 0; j < columns; j++) { 15361592Srgrimes c = ctab + j * lines + i; 15371592Srgrimes printf("%s%c", c->name, 15381592Srgrimes c->implemented ? ' ' : '*'); 15391592Srgrimes if (c + lines >= &ctab[NCMDS]) 15401592Srgrimes break; 15411592Srgrimes w = strlen(c->name) + 1; 15421592Srgrimes while (w < width) { 15431592Srgrimes putchar(' '); 15441592Srgrimes w++; 15451592Srgrimes } 15461592Srgrimes } 15471592Srgrimes printf("\r\n"); 15481592Srgrimes } 15491592Srgrimes (void) fflush(stdout); 1550110037Syar if (hostinfo) 1551110037Syar reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1552110037Syar else 1553110037Syar reply(214, "End."); 15541592Srgrimes return; 15551592Srgrimes } 15561592Srgrimes upper(s); 15571592Srgrimes c = lookup(ctab, s); 1558132931Syar if (c == NULL) { 15591592Srgrimes reply(502, "Unknown command %s.", s); 15601592Srgrimes return; 15611592Srgrimes } 15621592Srgrimes if (c->implemented) 15631592Srgrimes reply(214, "Syntax: %s%s %s", type, c->name, c->help); 15641592Srgrimes else 15651592Srgrimes reply(214, "%s%-*s\t%s; unimplemented.", type, width, 15661592Srgrimes c->name, c->help); 15671592Srgrimes} 15681592Srgrimes 15691592Srgrimesstatic void 157090148Simpsizecmd(char *filename) 15711592Srgrimes{ 15721592Srgrimes switch (type) { 15731592Srgrimes case TYPE_L: 15741592Srgrimes case TYPE_I: { 15751592Srgrimes struct stat stbuf; 157663350Sdes if (stat(filename, &stbuf) < 0) 157763350Sdes perror_reply(550, filename); 157863350Sdes else if (!S_ISREG(stbuf.st_mode)) 15791592Srgrimes reply(550, "%s: not a plain file.", filename); 15801592Srgrimes else 1581132929Syar reply(213, "%jd", (intmax_t)stbuf.st_size); 15821592Srgrimes break; } 15831592Srgrimes case TYPE_A: { 15841592Srgrimes FILE *fin; 15851592Srgrimes int c; 15861592Srgrimes off_t count; 15871592Srgrimes struct stat stbuf; 15881592Srgrimes fin = fopen(filename, "r"); 15891592Srgrimes if (fin == NULL) { 15901592Srgrimes perror_reply(550, filename); 15911592Srgrimes return; 15921592Srgrimes } 159363350Sdes if (fstat(fileno(fin), &stbuf) < 0) { 159463350Sdes perror_reply(550, filename); 159563350Sdes (void) fclose(fin); 159663350Sdes return; 159763350Sdes } else if (!S_ISREG(stbuf.st_mode)) { 15981592Srgrimes reply(550, "%s: not a plain file.", filename); 15991592Srgrimes (void) fclose(fin); 16001592Srgrimes return; 1601101034Syar } else if (stbuf.st_size > MAXASIZE) { 1602101034Syar reply(550, "%s: too large for type A SIZE.", filename); 1603101034Syar (void) fclose(fin); 1604101034Syar return; 16051592Srgrimes } 16061592Srgrimes 16071592Srgrimes count = 0; 16081592Srgrimes while((c=getc(fin)) != EOF) { 16091592Srgrimes if (c == '\n') /* will get expanded to \r\n */ 16101592Srgrimes count++; 16111592Srgrimes count++; 16121592Srgrimes } 16131592Srgrimes (void) fclose(fin); 16141592Srgrimes 1615132929Syar reply(213, "%jd", (intmax_t)count); 16161592Srgrimes break; } 16171592Srgrimes default: 1618100684Syar reply(504, "SIZE not implemented for type %s.", 1619100684Syar typenames[type]); 16201592Srgrimes } 16211592Srgrimes} 162256668Sshin 162356668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 162456668Sshinstatic int 162590148Simpport_check(const char *pcmd) 162656668Sshin{ 162756668Sshin if (his_addr.su_family == AF_INET) { 162856668Sshin if (data_dest.su_family != AF_INET) { 162956668Sshin usedefault = 1; 163056668Sshin reply(500, "Invalid address rejected."); 163156668Sshin return 1; 163256668Sshin } 163356668Sshin if (paranoid && 163456668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 163556668Sshin memcmp(&data_dest.su_sin.sin_addr, 163656668Sshin &his_addr.su_sin.sin_addr, 163756668Sshin sizeof(data_dest.su_sin.sin_addr)))) { 163856668Sshin usedefault = 1; 163956668Sshin reply(500, "Illegal PORT range rejected."); 164056668Sshin } else { 164156668Sshin usedefault = 0; 164256668Sshin if (pdata >= 0) { 164356668Sshin (void) close(pdata); 164456668Sshin pdata = -1; 164556668Sshin } 164656668Sshin reply(200, "%s command successful.", pcmd); 164756668Sshin } 164856668Sshin return 1; 164956668Sshin } 165056668Sshin return 0; 165156668Sshin} 165256668Sshin 165370102Sphkstatic int 165490148Simpcheck_login1(void) 165570102Sphk{ 165670102Sphk if (logged_in) 165770102Sphk return 1; 165870102Sphk else { 165970102Sphk reply(530, "Please login with USER and PASS."); 166070102Sphk return 0; 166170102Sphk } 166270102Sphk} 166370102Sphk 1664110340Syar/* 1665110340Syar * Replace leading "~user" in a pathname by the user's login directory. 1666110340Syar * Returned string will be in a freshly malloced buffer unless it's NULL. 1667110340Syar */ 1668110340Syarstatic char * 1669110340Syarexptilde(char *s) 1670110340Syar{ 1671110340Syar char *p, *q; 1672110340Syar char *path, *user; 1673110340Syar struct passwd *ppw; 1674110340Syar 1675110340Syar if ((p = strdup(s)) == NULL) 1676110340Syar return (NULL); 1677110340Syar if (*p != '~') 1678110340Syar return (p); 1679110340Syar 1680110340Syar user = p + 1; /* skip tilde */ 1681110340Syar if ((path = strchr(p, '/')) != NULL) 1682110340Syar *(path++) = '\0'; /* separate ~user from the rest of path */ 1683110378Syar if (*user == '\0') /* no user specified, use the current user */ 1684110378Syar user = pw->pw_name; 1685110378Syar /* read passwd even for the current user since we may be chrooted */ 1686110378Syar if ((ppw = getpwnam(user)) != NULL) { 1687110340Syar /* user found, substitute login directory for ~user */ 1688110340Syar if (path) 1689110340Syar asprintf(&q, "%s/%s", ppw->pw_dir, path); 1690110340Syar else 1691110340Syar q = strdup(ppw->pw_dir); 1692110340Syar free(p); 1693110340Syar p = q; 1694110340Syar } else { 1695110340Syar /* user not found, undo the damage */ 1696110340Syar if (path) 1697110340Syar path[-1] = '/'; 1698110340Syar } 1699110340Syar return (p); 1700110340Syar} 1701110340Syar 1702110340Syar/* 1703110340Syar * Expand glob(3) patterns possibly present in a pathname. 1704110340Syar * Avoid expanding to a pathname including '\r' or '\n' in order to 1705110340Syar * not disrupt the FTP protocol. 1706110340Syar * The expansion found must be unique. 1707110340Syar * Return the result as a malloced string, or NULL if an error occured. 1708110340Syar * 1709110340Syar * Problem: this production is used for all pathname 1710110340Syar * processing, but only gives a 550 error reply. 1711110340Syar * This is a valid reply in some cases but not in others. 1712110340Syar */ 1713110340Syarstatic char * 1714110340Syarexpglob(char *s) 1715110340Syar{ 1716110340Syar char *p, **pp, *rval; 1717110340Syar int flags = GLOB_BRACE | GLOB_NOCHECK; 1718110340Syar int n; 1719110340Syar glob_t gl; 1720110340Syar 1721110340Syar memset(&gl, 0, sizeof(gl)); 1722110340Syar flags |= GLOB_LIMIT; 1723110340Syar gl.gl_matchc = MAXGLOBARGS; 1724110340Syar if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) { 1725110340Syar for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++) 1726110340Syar if (*(*pp + strcspn(*pp, "\r\n")) == '\0') { 1727110340Syar p = *pp; 1728110340Syar n++; 1729110340Syar } 1730110340Syar if (n == 0) 1731110340Syar rval = strdup(s); 1732110340Syar else if (n == 1) 1733110340Syar rval = strdup(p); 1734110340Syar else { 1735137852Syar reply(550, "Wildcard is ambiguous."); 1736110340Syar rval = NULL; 1737110340Syar } 1738110340Syar } else { 1739137852Syar reply(550, "Wildcard expansion error."); 1740110340Syar rval = NULL; 1741110340Syar } 1742110340Syar globfree(&gl); 1743110340Syar return (rval); 1744110340Syar} 1745110340Syar 174656668Sshin#ifdef INET6 174756668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 174856668Sshinstatic int 174990148Simpport_check_v6(const char *pcmd) 175056668Sshin{ 175156668Sshin if (his_addr.su_family == AF_INET6) { 175256668Sshin if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 175356668Sshin /* Convert data_dest into v4 mapped sockaddr.*/ 175456668Sshin v4map_data_dest(); 175556668Sshin if (data_dest.su_family != AF_INET6) { 175656668Sshin usedefault = 1; 175756668Sshin reply(500, "Invalid address rejected."); 175856668Sshin return 1; 175956668Sshin } 176056668Sshin if (paranoid && 176156668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 176256668Sshin memcmp(&data_dest.su_sin6.sin6_addr, 176356668Sshin &his_addr.su_sin6.sin6_addr, 176456668Sshin sizeof(data_dest.su_sin6.sin6_addr)))) { 176556668Sshin usedefault = 1; 176656668Sshin reply(500, "Illegal PORT range rejected."); 176756668Sshin } else { 176856668Sshin usedefault = 0; 176956668Sshin if (pdata >= 0) { 177056668Sshin (void) close(pdata); 177156668Sshin pdata = -1; 177256668Sshin } 177356668Sshin reply(200, "%s command successful.", pcmd); 177456668Sshin } 177556668Sshin return 1; 177656668Sshin } 177756668Sshin return 0; 177856668Sshin} 177956668Sshin 178056668Sshinstatic void 178190148Simpv4map_data_dest(void) 178256668Sshin{ 178356668Sshin struct in_addr savedaddr; 178456668Sshin int savedport; 178556668Sshin 178656668Sshin if (data_dest.su_family != AF_INET) { 178756668Sshin usedefault = 1; 178856668Sshin reply(500, "Invalid address rejected."); 178956668Sshin return; 179056668Sshin } 179156668Sshin 179256668Sshin savedaddr = data_dest.su_sin.sin_addr; 179356668Sshin savedport = data_dest.su_port; 179456668Sshin 179556668Sshin memset(&data_dest, 0, sizeof(data_dest)); 179656668Sshin data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 179756668Sshin data_dest.su_sin6.sin6_family = AF_INET6; 179856668Sshin data_dest.su_sin6.sin6_port = savedport; 179956668Sshin memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 180056668Sshin memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 180156668Sshin (caddr_t)&savedaddr, sizeof(savedaddr)); 180256668Sshin} 180356668Sshin#endif 1804