ftpcmd.y revision 137852
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 137852 2004-11-18 11:45:13Z 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) { 186137852Syar 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) { 209137852Syar 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) { 239137852Syar 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) 332137852Syar reply(501, "No PASV allowed after EPSV ALL."); 33356668Sshin else if ($2) 33417433Spst passive(); 3351592Srgrimes } 33656668Sshin | LPSV check_login CRLF 33756668Sshin { 33856668Sshin if (epsvall) 339137852Syar 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) { 366137852Syar reply(200, "EPSV ALL command successful."); 36756668Sshin epsvall++; 36856668Sshin } 36956668Sshin } 37070102Sphk | EPSV check_login_epsv CRLF 37156668Sshin { 37256668Sshin if ($2) 37356668Sshin long_passive("EPSV", PF_UNSPEC); 37456668Sshin } 37571278Sjedgar | TYPE check_login SP type_code CRLF 3761592Srgrimes { 37771278Sjedgar if ($2) { 37871278Sjedgar switch (cmd_type) { 3791592Srgrimes 38071278Sjedgar case TYPE_A: 38171278Sjedgar if (cmd_form == FORM_N) { 38271278Sjedgar reply(200, "Type set to A."); 38371278Sjedgar type = cmd_type; 38471278Sjedgar form = cmd_form; 38571278Sjedgar } else 38671278Sjedgar reply(504, "Form must be N."); 38771278Sjedgar break; 3881592Srgrimes 38971278Sjedgar case TYPE_E: 39071278Sjedgar reply(504, "Type E not implemented."); 39171278Sjedgar break; 3921592Srgrimes 39371278Sjedgar case TYPE_I: 39471278Sjedgar reply(200, "Type set to I."); 39571278Sjedgar type = cmd_type; 39671278Sjedgar break; 3971592Srgrimes 39871278Sjedgar case TYPE_L: 399103949Smike#if CHAR_BIT == 8 40071278Sjedgar if (cmd_bytesz == 8) { 40171278Sjedgar reply(200, 40271278Sjedgar "Type set to L (byte size 8)."); 40371278Sjedgar type = cmd_type; 40471278Sjedgar } else 40571278Sjedgar reply(504, "Byte size must be 8."); 406103949Smike#else /* CHAR_BIT == 8 */ 407103949Smike UNIMPLEMENTED for CHAR_BIT != 8 408103949Smike#endif /* CHAR_BIT == 8 */ 40971278Sjedgar } 4101592Srgrimes } 4111592Srgrimes } 41271278Sjedgar | STRU check_login SP struct_code CRLF 4131592Srgrimes { 41471278Sjedgar if ($2) { 41571278Sjedgar switch ($4) { 4161592Srgrimes 41771278Sjedgar case STRU_F: 418137852Syar reply(200, "STRU F accepted."); 41971278Sjedgar break; 4201592Srgrimes 42171278Sjedgar default: 42271278Sjedgar reply(504, "Unimplemented STRU type."); 42371278Sjedgar } 4241592Srgrimes } 4251592Srgrimes } 42671278Sjedgar | MODE check_login SP mode_code CRLF 4271592Srgrimes { 42871278Sjedgar if ($2) { 42971278Sjedgar switch ($4) { 4301592Srgrimes 43171278Sjedgar case MODE_S: 432137852Syar reply(200, "MODE S accepted."); 43371278Sjedgar break; 43471278Sjedgar 43571278Sjedgar default: 43671278Sjedgar reply(502, "Unimplemented MODE type."); 43771278Sjedgar } 4381592Srgrimes } 4391592Srgrimes } 44071278Sjedgar | ALLO check_login SP NUMBER CRLF 4411592Srgrimes { 44271278Sjedgar if ($2) { 44371278Sjedgar reply(202, "ALLO command ignored."); 44471278Sjedgar } 4451592Srgrimes } 44671278Sjedgar | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 4471592Srgrimes { 44871278Sjedgar if ($2) { 44971278Sjedgar reply(202, "ALLO command ignored."); 45071278Sjedgar } 4511592Srgrimes } 4521592Srgrimes | RETR check_login SP pathname CRLF 4531592Srgrimes { 45482796Ssheldonh if (noretr || (guest && noguestretr)) 455137852Syar reply(500, "RETR command disabled."); 45682460Snik else if ($2 && $4 != NULL) 457132931Syar retrieve(NULL, $4); 45882460Snik 4591592Srgrimes if ($4 != NULL) 4601592Srgrimes free($4); 4611592Srgrimes } 46270102Sphk | STOR check_login_ro SP pathname CRLF 4631592Srgrimes { 4641592Srgrimes if ($2 && $4 != NULL) 4651592Srgrimes store($4, "w", 0); 4661592Srgrimes if ($4 != NULL) 4671592Srgrimes free($4); 4681592Srgrimes } 46970102Sphk | APPE check_login_ro SP pathname CRLF 4701592Srgrimes { 4711592Srgrimes if ($2 && $4 != NULL) 4721592Srgrimes store($4, "a", 0); 4731592Srgrimes if ($4 != NULL) 4741592Srgrimes free($4); 4751592Srgrimes } 4761592Srgrimes | NLST check_login CRLF 4771592Srgrimes { 4781592Srgrimes if ($2) 4791592Srgrimes send_file_list("."); 4801592Srgrimes } 481101395Syar | NLST check_login SP pathstring CRLF 4821592Srgrimes { 483101395Syar if ($2) 4841592Srgrimes send_file_list($4); 485101395Syar free($4); 4861592Srgrimes } 4871592Srgrimes | LIST check_login CRLF 4881592Srgrimes { 4891592Srgrimes if ($2) 490109380Syar retrieve(_PATH_LS " -lgA", ""); 4911592Srgrimes } 49275567Speter | LIST check_login SP pathstring CRLF 4931592Srgrimes { 494101395Syar if ($2) 495109380Syar retrieve(_PATH_LS " -lgA %s", $4); 496101395Syar free($4); 4971592Srgrimes } 4981592Srgrimes | STAT check_login SP pathname CRLF 4991592Srgrimes { 5001592Srgrimes if ($2 && $4 != NULL) 5011592Srgrimes statfilecmd($4); 5021592Srgrimes if ($4 != NULL) 5031592Srgrimes free($4); 5041592Srgrimes } 50571278Sjedgar | STAT check_login CRLF 5061592Srgrimes { 50771278Sjedgar if ($2) { 50871278Sjedgar statcmd(); 50971278Sjedgar } 5101592Srgrimes } 51170102Sphk | DELE check_login_ro SP pathname CRLF 5121592Srgrimes { 5131592Srgrimes if ($2 && $4 != NULL) 5141592Srgrimes delete($4); 5151592Srgrimes if ($4 != NULL) 5161592Srgrimes free($4); 5171592Srgrimes } 51870102Sphk | RNTO check_login_ro SP pathname CRLF 5191592Srgrimes { 520101379Syar if ($2 && $4 != NULL) { 52117433Spst if (fromname) { 52217433Spst renamecmd(fromname, $4); 52317433Spst free(fromname); 524132931Syar fromname = NULL; 52517433Spst } else { 52617433Spst reply(503, "Bad sequence of commands."); 52717433Spst } 5281592Srgrimes } 529101379Syar if ($4 != NULL) 530101379Syar free($4); 5311592Srgrimes } 53271278Sjedgar | ABOR check_login CRLF 5331592Srgrimes { 53471278Sjedgar if ($2) 53571278Sjedgar reply(225, "ABOR command successful."); 5361592Srgrimes } 5371592Srgrimes | CWD check_login CRLF 5381592Srgrimes { 53969234Sdanny if ($2) { 540110036Syar cwd(homedir); 54169234Sdanny } 5421592Srgrimes } 5431592Srgrimes | CWD check_login SP pathname CRLF 5441592Srgrimes { 5451592Srgrimes if ($2 && $4 != NULL) 5461592Srgrimes cwd($4); 5471592Srgrimes if ($4 != NULL) 5481592Srgrimes free($4); 5491592Srgrimes } 5501592Srgrimes | HELP CRLF 5511592Srgrimes { 552132931Syar help(cmdtab, NULL); 5531592Srgrimes } 5541592Srgrimes | HELP SP STRING CRLF 5551592Srgrimes { 5561592Srgrimes char *cp = $3; 5571592Srgrimes 5581592Srgrimes if (strncasecmp(cp, "SITE", 4) == 0) { 5591592Srgrimes cp = $3 + 4; 5601592Srgrimes if (*cp == ' ') 5611592Srgrimes cp++; 5621592Srgrimes if (*cp) 5631592Srgrimes help(sitetab, cp); 5641592Srgrimes else 565132931Syar help(sitetab, NULL); 5661592Srgrimes } else 5671592Srgrimes help(cmdtab, $3); 56888935Sdwmalone free($3); 5691592Srgrimes } 5701592Srgrimes | NOOP CRLF 5711592Srgrimes { 5721592Srgrimes reply(200, "NOOP command successful."); 5731592Srgrimes } 57470102Sphk | MKD check_login_ro SP pathname CRLF 5751592Srgrimes { 5761592Srgrimes if ($2 && $4 != NULL) 5771592Srgrimes makedir($4); 5781592Srgrimes if ($4 != NULL) 5791592Srgrimes free($4); 5801592Srgrimes } 58170102Sphk | RMD check_login_ro SP pathname CRLF 5821592Srgrimes { 5831592Srgrimes if ($2 && $4 != NULL) 5841592Srgrimes removedir($4); 5851592Srgrimes if ($4 != NULL) 5861592Srgrimes free($4); 5871592Srgrimes } 5881592Srgrimes | PWD check_login CRLF 5891592Srgrimes { 5901592Srgrimes if ($2) 5911592Srgrimes pwd(); 5921592Srgrimes } 5931592Srgrimes | CDUP check_login CRLF 5941592Srgrimes { 5951592Srgrimes if ($2) 5961592Srgrimes cwd(".."); 5971592Srgrimes } 5981592Srgrimes | SITE SP HELP CRLF 5991592Srgrimes { 600132931Syar help(sitetab, NULL); 6011592Srgrimes } 6021592Srgrimes | SITE SP HELP SP STRING CRLF 6031592Srgrimes { 6041592Srgrimes help(sitetab, $5); 60588935Sdwmalone free($5); 6061592Srgrimes } 60775535Sphk | SITE SP MDFIVE check_login SP pathname CRLF 60875535Sphk { 60975535Sphk char p[64], *q; 61075535Sphk 611101379Syar if ($4 && $6) { 61275535Sphk q = MD5File($6, p); 61375535Sphk if (q != NULL) 61475535Sphk reply(200, "MD5(%s) = %s", $6, p); 61575535Sphk else 61675535Sphk perror_reply(550, $6); 61775535Sphk } 61888935Sdwmalone if ($6) 61988935Sdwmalone free($6); 62075535Sphk } 6211592Srgrimes | SITE SP UMASK check_login CRLF 6221592Srgrimes { 6231592Srgrimes int oldmask; 6241592Srgrimes 6251592Srgrimes if ($4) { 6261592Srgrimes oldmask = umask(0); 6271592Srgrimes (void) umask(oldmask); 628137852Syar reply(200, "Current UMASK is %03o.", oldmask); 6291592Srgrimes } 6301592Srgrimes } 6311592Srgrimes | SITE SP UMASK check_login SP octal_number CRLF 6321592Srgrimes { 6331592Srgrimes int oldmask; 6341592Srgrimes 6351592Srgrimes if ($4) { 6361592Srgrimes if (($6 == -1) || ($6 > 0777)) { 637137852Syar reply(501, "Bad UMASK value."); 6381592Srgrimes } else { 6391592Srgrimes oldmask = umask($6); 6401592Srgrimes reply(200, 641137852Syar "UMASK set to %03o (was %03o).", 6421592Srgrimes $6, oldmask); 6431592Srgrimes } 6441592Srgrimes } 6451592Srgrimes } 64670102Sphk | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF 6471592Srgrimes { 6481592Srgrimes if ($4 && ($8 != NULL)) { 649101378Syar if (($6 == -1 ) || ($6 > 0777)) 650137852Syar reply(501, "Bad mode value."); 6511592Srgrimes else if (chmod($8, $6) < 0) 6521592Srgrimes perror_reply(550, $8); 6531592Srgrimes else 6541592Srgrimes reply(200, "CHMOD command successful."); 6551592Srgrimes } 6561592Srgrimes if ($8 != NULL) 6571592Srgrimes free($8); 6581592Srgrimes } 65971278Sjedgar | SITE SP check_login IDLE CRLF 6601592Srgrimes { 66171278Sjedgar if ($3) 66271278Sjedgar reply(200, 663137852Syar "Current IDLE time limit is %d seconds; max %d.", 66471278Sjedgar timeout, maxtimeout); 6651592Srgrimes } 66671278Sjedgar | SITE SP check_login IDLE SP NUMBER CRLF 6671592Srgrimes { 66871278Sjedgar if ($3) { 66992272Smaxim if ($6.i < 30 || $6.i > maxtimeout) { 67071278Sjedgar reply(501, 671137852Syar "Maximum IDLE time must be between 30 and %d seconds.", 67271278Sjedgar maxtimeout); 67371278Sjedgar } else { 67492272Smaxim timeout = $6.i; 675137659Syar (void) alarm(timeout); 67671278Sjedgar reply(200, 677137852Syar "Maximum IDLE time set to %d seconds.", 67871278Sjedgar timeout); 67971278Sjedgar } 6801592Srgrimes } 6811592Srgrimes } 68270102Sphk | STOU check_login_ro SP pathname CRLF 6831592Srgrimes { 6841592Srgrimes if ($2 && $4 != NULL) 6851592Srgrimes store($4, "w", 1); 6861592Srgrimes if ($4 != NULL) 6871592Srgrimes free($4); 6881592Srgrimes } 68971278Sjedgar | SYST check_login CRLF 6901592Srgrimes { 691116439Syar if ($2) { 692116439Syar if (hostinfo) 6931592Srgrimes#ifdef BSD 694116439Syar reply(215, "UNIX Type: L%d Version: BSD-%d", 695116439Syar CHAR_BIT, BSD); 6961592Srgrimes#else /* BSD */ 697116439Syar reply(215, "UNIX Type: L%d", CHAR_BIT); 6981592Srgrimes#endif /* BSD */ 699116439Syar else 700116439Syar reply(215, "UNKNOWN Type: L%d", CHAR_BIT); 701116439Syar } 7021592Srgrimes } 7031592Srgrimes 7041592Srgrimes /* 7051592Srgrimes * SIZE is not in RFC959, but Postel has blessed it and 7061592Srgrimes * it will be in the updated RFC. 7071592Srgrimes * 7081592Srgrimes * Return size of file in a format suitable for 7091592Srgrimes * using with RESTART (we just count bytes). 7101592Srgrimes */ 7111592Srgrimes | SIZE check_login SP pathname CRLF 7121592Srgrimes { 7131592Srgrimes if ($2 && $4 != NULL) 7141592Srgrimes sizecmd($4); 7151592Srgrimes if ($4 != NULL) 7161592Srgrimes free($4); 7171592Srgrimes } 7181592Srgrimes 7191592Srgrimes /* 7201592Srgrimes * MDTM is not in RFC959, but Postel has blessed it and 7211592Srgrimes * it will be in the updated RFC. 7221592Srgrimes * 7231592Srgrimes * Return modification time of file as an ISO 3307 7241592Srgrimes * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 7251592Srgrimes * where xxx is the fractional second (of any precision, 7261592Srgrimes * not necessarily 3 digits) 7271592Srgrimes */ 7281592Srgrimes | MDTM check_login SP pathname CRLF 7291592Srgrimes { 7301592Srgrimes if ($2 && $4 != NULL) { 7311592Srgrimes struct stat stbuf; 7321592Srgrimes if (stat($4, &stbuf) < 0) 733137850Syar perror_reply(550, $4); 7341592Srgrimes else if (!S_ISREG(stbuf.st_mode)) { 7351592Srgrimes reply(550, "%s: not a plain file.", $4); 7361592Srgrimes } else { 7371592Srgrimes struct tm *t; 7381592Srgrimes t = gmtime(&stbuf.st_mtime); 7391592Srgrimes reply(213, 74017435Spst "%04d%02d%02d%02d%02d%02d", 74117435Spst 1900 + t->tm_year, 74217435Spst t->tm_mon+1, t->tm_mday, 7431592Srgrimes t->tm_hour, t->tm_min, t->tm_sec); 7441592Srgrimes } 7451592Srgrimes } 7461592Srgrimes if ($4 != NULL) 7471592Srgrimes free($4); 7481592Srgrimes } 7491592Srgrimes | QUIT CRLF 7501592Srgrimes { 7511592Srgrimes reply(221, "Goodbye."); 7521592Srgrimes dologout(0); 7531592Srgrimes } 754102565Syar | NOTIMPL 755102565Syar { 756102565Syar nack($1); 757102565Syar } 75889935Syar | error 7591592Srgrimes { 76089935Syar yyclearin; /* discard lookahead data */ 76189935Syar yyerrok; /* clear error condition */ 762102565Syar state = CMD; /* reset lexer state */ 7631592Srgrimes } 7641592Srgrimes ; 7651592Srgrimesrcmd 76670102Sphk : RNFR check_login_ro SP pathname CRLF 7671592Srgrimes { 768132930Syar restart_point = 0; 7691592Srgrimes if ($2 && $4) { 77088935Sdwmalone if (fromname) 77188935Sdwmalone free(fromname); 772132931Syar fromname = NULL; 77388935Sdwmalone if (renamefrom($4)) 77488935Sdwmalone fromname = $4; 77588935Sdwmalone else 7761592Srgrimes free($4); 77788935Sdwmalone } else if ($4) { 77888935Sdwmalone free($4); 7791592Srgrimes } 7801592Srgrimes } 78192272Smaxim | REST check_login SP NUMBER CRLF 7821592Srgrimes { 78371278Sjedgar if ($2) { 78488935Sdwmalone if (fromname) 78588935Sdwmalone free(fromname); 786132931Syar fromname = NULL; 78792272Smaxim restart_point = $4.o; 788132929Syar reply(350, "Restarting at %jd. %s", 789132929Syar (intmax_t)restart_point, 79071278Sjedgar "Send STORE or RETRIEVE to initiate transfer."); 79171278Sjedgar } 7921592Srgrimes } 7931592Srgrimes ; 7941592Srgrimes 7951592Srgrimesusername 7961592Srgrimes : STRING 7971592Srgrimes ; 7981592Srgrimes 7991592Srgrimespassword 8001592Srgrimes : /* empty */ 8011592Srgrimes { 8021592Srgrimes $$ = (char *)calloc(1, sizeof(char)); 8031592Srgrimes } 8041592Srgrimes | STRING 8051592Srgrimes ; 8061592Srgrimes 8071592Srgrimesbyte_size 8081592Srgrimes : NUMBER 80992272Smaxim { 81092272Smaxim $$ = $1.i; 81192272Smaxim } 8121592Srgrimes ; 8131592Srgrimes 8141592Srgrimeshost_port 8151592Srgrimes : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 8161592Srgrimes NUMBER COMMA NUMBER 8171592Srgrimes { 8181592Srgrimes char *a, *p; 8191592Srgrimes 82056668Sshin data_dest.su_len = sizeof(struct sockaddr_in); 82156668Sshin data_dest.su_family = AF_INET; 82256668Sshin p = (char *)&data_dest.su_sin.sin_port; 82392272Smaxim p[0] = $9.i; p[1] = $11.i; 82456668Sshin a = (char *)&data_dest.su_sin.sin_addr; 82592272Smaxim a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; 8261592Srgrimes } 8271592Srgrimes ; 8281592Srgrimes 82956668Sshinhost_long_port 83056668Sshin : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83156668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83256668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83356668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83456668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83556668Sshin NUMBER 83656668Sshin { 83756668Sshin char *a, *p; 83856668Sshin 83956668Sshin memset(&data_dest, 0, sizeof(data_dest)); 84056668Sshin data_dest.su_len = sizeof(struct sockaddr_in6); 84156668Sshin data_dest.su_family = AF_INET6; 84256668Sshin p = (char *)&data_dest.su_port; 84392272Smaxim p[0] = $39.i; p[1] = $41.i; 84456668Sshin a = (char *)&data_dest.su_sin6.sin6_addr; 84592272Smaxim a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 84692272Smaxim a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i; 84792272Smaxim a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i; 84892272Smaxim a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i; 84956668Sshin if (his_addr.su_family == AF_INET6) { 85056668Sshin /* XXX more sanity checks! */ 85156668Sshin data_dest.su_sin6.sin6_scope_id = 85256668Sshin his_addr.su_sin6.sin6_scope_id; 85356668Sshin } 85492272Smaxim if ($1.i != 6 || $3.i != 16 || $37.i != 2) 85556668Sshin memset(&data_dest, 0, sizeof(data_dest)); 85656668Sshin } 85756668Sshin | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 85856668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 85956668Sshin NUMBER 86056668Sshin { 86156668Sshin char *a, *p; 86256668Sshin 86356668Sshin memset(&data_dest, 0, sizeof(data_dest)); 86456668Sshin data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 86556668Sshin data_dest.su_family = AF_INET; 86656668Sshin p = (char *)&data_dest.su_port; 86792272Smaxim p[0] = $15.i; p[1] = $17.i; 86856668Sshin a = (char *)&data_dest.su_sin.sin_addr; 86992272Smaxim a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 87092272Smaxim if ($1.i != 4 || $3.i != 4 || $13.i != 2) 87156668Sshin memset(&data_dest, 0, sizeof(data_dest)); 87256668Sshin } 87356668Sshin ; 87456668Sshin 8751592Srgrimesform_code 8761592Srgrimes : N 8771592Srgrimes { 8781592Srgrimes $$ = FORM_N; 8791592Srgrimes } 8801592Srgrimes | T 8811592Srgrimes { 8821592Srgrimes $$ = FORM_T; 8831592Srgrimes } 8841592Srgrimes | C 8851592Srgrimes { 8861592Srgrimes $$ = FORM_C; 8871592Srgrimes } 8881592Srgrimes ; 8891592Srgrimes 8901592Srgrimestype_code 8911592Srgrimes : A 8921592Srgrimes { 8931592Srgrimes cmd_type = TYPE_A; 8941592Srgrimes cmd_form = FORM_N; 8951592Srgrimes } 8961592Srgrimes | A SP form_code 8971592Srgrimes { 8981592Srgrimes cmd_type = TYPE_A; 8991592Srgrimes cmd_form = $3; 9001592Srgrimes } 9011592Srgrimes | E 9021592Srgrimes { 9031592Srgrimes cmd_type = TYPE_E; 9041592Srgrimes cmd_form = FORM_N; 9051592Srgrimes } 9061592Srgrimes | E SP form_code 9071592Srgrimes { 9081592Srgrimes cmd_type = TYPE_E; 9091592Srgrimes cmd_form = $3; 9101592Srgrimes } 9111592Srgrimes | I 9121592Srgrimes { 9131592Srgrimes cmd_type = TYPE_I; 9141592Srgrimes } 9151592Srgrimes | L 9161592Srgrimes { 9171592Srgrimes cmd_type = TYPE_L; 918103949Smike cmd_bytesz = CHAR_BIT; 9191592Srgrimes } 9201592Srgrimes | L SP byte_size 9211592Srgrimes { 9221592Srgrimes cmd_type = TYPE_L; 9231592Srgrimes cmd_bytesz = $3; 9241592Srgrimes } 9251592Srgrimes /* this is for a bug in the BBN ftp */ 9261592Srgrimes | L byte_size 9271592Srgrimes { 9281592Srgrimes cmd_type = TYPE_L; 9291592Srgrimes cmd_bytesz = $2; 9301592Srgrimes } 9311592Srgrimes ; 9321592Srgrimes 9331592Srgrimesstruct_code 9341592Srgrimes : F 9351592Srgrimes { 9361592Srgrimes $$ = STRU_F; 9371592Srgrimes } 9381592Srgrimes | R 9391592Srgrimes { 9401592Srgrimes $$ = STRU_R; 9411592Srgrimes } 9421592Srgrimes | P 9431592Srgrimes { 9441592Srgrimes $$ = STRU_P; 9451592Srgrimes } 9461592Srgrimes ; 9471592Srgrimes 9481592Srgrimesmode_code 9491592Srgrimes : S 9501592Srgrimes { 9511592Srgrimes $$ = MODE_S; 9521592Srgrimes } 9531592Srgrimes | B 9541592Srgrimes { 9551592Srgrimes $$ = MODE_B; 9561592Srgrimes } 9571592Srgrimes | C 9581592Srgrimes { 9591592Srgrimes $$ = MODE_C; 9601592Srgrimes } 9611592Srgrimes ; 9621592Srgrimes 9631592Srgrimespathname 9641592Srgrimes : pathstring 9651592Srgrimes { 96675567Speter if (logged_in && $1) { 967110340Syar char *p; 9681592Srgrimes 969110340Syar /* 970110340Syar * Expand ~user manually since glob(3) 971110340Syar * will return the unexpanded pathname 972110340Syar * if the corresponding file/directory 973110340Syar * doesn't exist yet. Using sole glob(3) 974110340Syar * would break natural commands like 975110340Syar * MKD ~user/newdir 976110340Syar * or 977110340Syar * RNTO ~/newfile 978110340Syar */ 979110340Syar if ((p = exptilde($1)) != NULL) { 980110340Syar $$ = expglob(p); 981110340Syar free(p); 982110340Syar } else 9831592Srgrimes $$ = NULL; 9841592Srgrimes free($1); 9851592Srgrimes } else 9861592Srgrimes $$ = $1; 9871592Srgrimes } 9881592Srgrimes ; 9891592Srgrimes 9901592Srgrimespathstring 9911592Srgrimes : STRING 9921592Srgrimes ; 9931592Srgrimes 9941592Srgrimesoctal_number 9951592Srgrimes : NUMBER 9961592Srgrimes { 9971592Srgrimes int ret, dec, multby, digit; 9981592Srgrimes 9991592Srgrimes /* 10001592Srgrimes * Convert a number that was read as decimal number 10011592Srgrimes * to what it would be if it had been read as octal. 10021592Srgrimes */ 100392272Smaxim dec = $1.i; 10041592Srgrimes multby = 1; 10051592Srgrimes ret = 0; 10061592Srgrimes while (dec) { 10071592Srgrimes digit = dec%10; 10081592Srgrimes if (digit > 7) { 10091592Srgrimes ret = -1; 10101592Srgrimes break; 10111592Srgrimes } 10121592Srgrimes ret += digit * multby; 10131592Srgrimes multby *= 8; 10141592Srgrimes dec /= 10; 10151592Srgrimes } 10161592Srgrimes $$ = ret; 10171592Srgrimes } 10181592Srgrimes ; 10191592Srgrimes 10201592Srgrimes 10211592Srgrimescheck_login 10221592Srgrimes : /* empty */ 10231592Srgrimes { 102470102Sphk $$ = check_login1(); 10251592Srgrimes } 10261592Srgrimes ; 10271592Srgrimes 102870102Sphkcheck_login_epsv 102970102Sphk : /* empty */ 103070102Sphk { 103170102Sphk if (noepsv) { 1032137852Syar reply(500, "EPSV command disabled."); 103370102Sphk $$ = 0; 103470102Sphk } 103570102Sphk else 103670102Sphk $$ = check_login1(); 103770102Sphk } 103870102Sphk ; 103970102Sphk 104070102Sphkcheck_login_ro 104170102Sphk : /* empty */ 104270102Sphk { 104370102Sphk if (readonly) { 104472710Sdes reply(550, "Permission denied."); 104570102Sphk $$ = 0; 104670102Sphk } 104770102Sphk else 104870102Sphk $$ = check_login1(); 104970102Sphk } 105070102Sphk ; 105170102Sphk 10521592Srgrimes%% 10531592Srgrimes 10541592Srgrimes#define CMD 0 /* beginning of command */ 10551592Srgrimes#define ARGS 1 /* expect miscellaneous arguments */ 10561592Srgrimes#define STR1 2 /* expect SP followed by STRING */ 10571592Srgrimes#define STR2 3 /* expect STRING */ 10581592Srgrimes#define OSTR 4 /* optional SP then STRING */ 105975556Sgreen#define ZSTR1 5 /* optional SP then optional STRING */ 10601592Srgrimes#define ZSTR2 6 /* optional STRING after SP */ 10611592Srgrimes#define SITECMD 7 /* SITE command */ 10621592Srgrimes#define NSTR 8 /* Number followed by a string */ 10631592Srgrimes 106475560Sjedgar#define MAXGLOBARGS 1000 106575560Sjedgar 1066101034Syar#define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */ 1067101034Syar 10681592Srgrimesstruct tab { 10691592Srgrimes char *name; 10701592Srgrimes short token; 10711592Srgrimes short state; 10721592Srgrimes short implemented; /* 1 if command is implemented */ 10731592Srgrimes char *help; 10741592Srgrimes}; 10751592Srgrimes 10761592Srgrimesstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 10771592Srgrimes { "USER", USER, STR1, 1, "<sp> username" }, 107875556Sgreen { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" }, 10791592Srgrimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 10801592Srgrimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 10811592Srgrimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 10821592Srgrimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 1083101806Syar { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" }, 108456668Sshin { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 108556668Sshin { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 10861592Srgrimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 108756668Sshin { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 108856668Sshin { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 1089101806Syar { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" }, 10901592Srgrimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 10911592Srgrimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 10921592Srgrimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 10931592Srgrimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 10941592Srgrimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 10951592Srgrimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 10961592Srgrimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 10971592Srgrimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 10981592Srgrimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 10991592Srgrimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 11001592Srgrimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 11011592Srgrimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 11021592Srgrimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 11031592Srgrimes { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 11041592Srgrimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 11051592Srgrimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 11061592Srgrimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 11071592Srgrimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 11081592Srgrimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 11091592Srgrimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 11101592Srgrimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 11111592Srgrimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 11121592Srgrimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 11131592Srgrimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 11141592Srgrimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 11151592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11161592Srgrimes { "NOOP", NOOP, ARGS, 1, "" }, 11171592Srgrimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 11181592Srgrimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 11191592Srgrimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 11201592Srgrimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 11211592Srgrimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 11221592Srgrimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 11231592Srgrimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11241592Srgrimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11251592Srgrimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 11261592Srgrimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 11271592Srgrimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 11281592Srgrimes { NULL, 0, 0, 0, 0 } 11291592Srgrimes}; 11301592Srgrimes 11311592Srgrimesstruct tab sitetab[] = { 113275535Sphk { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" }, 11331592Srgrimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 11341592Srgrimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 11351592Srgrimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 11361592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11371592Srgrimes { NULL, 0, 0, 0, 0 } 11381592Srgrimes}; 11391592Srgrimes 114090148Simpstatic char *copy(char *); 1141110340Syarstatic char *expglob(char *); 1142110340Syarstatic char *exptilde(char *); 114390148Simpstatic void help(struct tab *, char *); 11441592Srgrimesstatic struct tab * 114590148Simp lookup(struct tab *, char *); 114690148Simpstatic int port_check(const char *); 114790148Simpstatic int port_check_v6(const char *); 114890148Simpstatic void sizecmd(char *); 114990148Simpstatic void toolong(int); 115090148Simpstatic void v4map_data_dest(void); 115190148Simpstatic int yylex(void); 11521592Srgrimes 11531592Srgrimesstatic struct tab * 115490148Simplookup(struct tab *p, char *cmd) 11551592Srgrimes{ 11561592Srgrimes 11571592Srgrimes for (; p->name != NULL; p++) 11581592Srgrimes if (strcmp(cmd, p->name) == 0) 11591592Srgrimes return (p); 11601592Srgrimes return (0); 11611592Srgrimes} 11621592Srgrimes 11631592Srgrimes#include <arpa/telnet.h> 11641592Srgrimes 11651592Srgrimes/* 11661592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes. 11671592Srgrimes */ 11681592Srgrimeschar * 116990148Simpgetline(char *s, int n, FILE *iop) 11701592Srgrimes{ 11711592Srgrimes int c; 11721592Srgrimes register char *cs; 1173117352Syar sigset_t sset, osset; 11741592Srgrimes 11751592Srgrimes cs = s; 11761592Srgrimes/* tmpline may contain saved command from urgent mode interruption */ 11771592Srgrimes for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 11781592Srgrimes *cs++ = tmpline[c]; 11791592Srgrimes if (tmpline[c] == '\n') { 11801592Srgrimes *cs++ = '\0'; 118176096Smarkm if (ftpdebug) 11821592Srgrimes syslog(LOG_DEBUG, "command: %s", s); 11831592Srgrimes tmpline[0] = '\0'; 11841592Srgrimes return(s); 11851592Srgrimes } 11861592Srgrimes if (c == 0) 11871592Srgrimes tmpline[0] = '\0'; 11881592Srgrimes } 1189117352Syar /* SIGURG would interrupt stdio if not blocked during the read loop */ 1190117352Syar sigemptyset(&sset); 1191117352Syar sigaddset(&sset, SIGURG); 1192117352Syar sigprocmask(SIG_BLOCK, &sset, &osset); 11931592Srgrimes while ((c = getc(iop)) != EOF) { 11941592Srgrimes c &= 0377; 11951592Srgrimes if (c == IAC) { 1196117351Syar if ((c = getc(iop)) == EOF) 1197117351Syar goto got_eof; 11981592Srgrimes c &= 0377; 11991592Srgrimes switch (c) { 12001592Srgrimes case WILL: 12011592Srgrimes case WONT: 1202117351Syar if ((c = getc(iop)) == EOF) 1203117351Syar goto got_eof; 12041592Srgrimes printf("%c%c%c", IAC, DONT, 0377&c); 12051592Srgrimes (void) fflush(stdout); 12061592Srgrimes continue; 12071592Srgrimes case DO: 12081592Srgrimes case DONT: 1209117351Syar if ((c = getc(iop)) == EOF) 1210117351Syar goto got_eof; 12111592Srgrimes printf("%c%c%c", IAC, WONT, 0377&c); 12121592Srgrimes (void) fflush(stdout); 12131592Srgrimes continue; 12141592Srgrimes case IAC: 12151592Srgrimes break; 12161592Srgrimes default: 12171592Srgrimes continue; /* ignore command */ 12181592Srgrimes } 12191592Srgrimes } 12201592Srgrimes *cs++ = c; 12211592Srgrimes if (--n <= 0 || c == '\n') 12221592Srgrimes break; 12231592Srgrimes } 1224117351Syargot_eof: 1225117352Syar sigprocmask(SIG_SETMASK, &osset, NULL); 12261592Srgrimes if (c == EOF && cs == s) 12271592Srgrimes return (NULL); 12281592Srgrimes *cs++ = '\0'; 122976096Smarkm if (ftpdebug) { 12301592Srgrimes if (!guest && strncasecmp("pass ", s, 5) == 0) { 12311592Srgrimes /* Don't syslog passwords */ 12321592Srgrimes syslog(LOG_DEBUG, "command: %.5s ???", s); 12331592Srgrimes } else { 12341592Srgrimes register char *cp; 12351592Srgrimes register int len; 12361592Srgrimes 12371592Srgrimes /* Don't syslog trailing CR-LF */ 12381592Srgrimes len = strlen(s); 12391592Srgrimes cp = s + len - 1; 12401592Srgrimes while (cp >= s && (*cp == '\n' || *cp == '\r')) { 12411592Srgrimes --cp; 12421592Srgrimes --len; 12431592Srgrimes } 12441592Srgrimes syslog(LOG_DEBUG, "command: %.*s", len, s); 12451592Srgrimes } 12461592Srgrimes } 12471592Srgrimes return (s); 12481592Srgrimes} 12491592Srgrimes 12501592Srgrimesstatic void 125190148Simptoolong(int signo) 12521592Srgrimes{ 12531592Srgrimes 12541592Srgrimes reply(421, 12551592Srgrimes "Timeout (%d seconds): closing control connection.", timeout); 12561592Srgrimes if (logging) 12571592Srgrimes syslog(LOG_INFO, "User %s timed out after %d seconds", 12581592Srgrimes (pw ? pw -> pw_name : "unknown"), timeout); 12591592Srgrimes dologout(1); 12601592Srgrimes} 12611592Srgrimes 12621592Srgrimesstatic int 126390148Simpyylex(void) 12641592Srgrimes{ 126589935Syar static int cpos; 12661592Srgrimes char *cp, *cp2; 12671592Srgrimes struct tab *p; 12681592Srgrimes int n; 12691592Srgrimes char c; 12701592Srgrimes 12711592Srgrimes for (;;) { 12721592Srgrimes switch (state) { 12731592Srgrimes 12741592Srgrimes case CMD: 12751592Srgrimes (void) signal(SIGALRM, toolong); 1276137659Syar (void) alarm(timeout); 12771592Srgrimes if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 12781592Srgrimes reply(221, "You could at least say goodbye."); 12791592Srgrimes dologout(0); 12801592Srgrimes } 12811592Srgrimes (void) alarm(0); 12821592Srgrimes#ifdef SETPROCTITLE 128329574Sphk if (strncasecmp(cbuf, "PASS", 4) != 0) 12841592Srgrimes setproctitle("%s: %s", proctitle, cbuf); 12851592Srgrimes#endif /* SETPROCTITLE */ 12861592Srgrimes if ((cp = strchr(cbuf, '\r'))) { 12871592Srgrimes *cp++ = '\n'; 12881592Srgrimes *cp = '\0'; 12891592Srgrimes } 12901592Srgrimes if ((cp = strpbrk(cbuf, " \n"))) 12911592Srgrimes cpos = cp - cbuf; 12921592Srgrimes if (cpos == 0) 12931592Srgrimes cpos = 4; 12941592Srgrimes c = cbuf[cpos]; 12951592Srgrimes cbuf[cpos] = '\0'; 12961592Srgrimes upper(cbuf); 12971592Srgrimes p = lookup(cmdtab, cbuf); 12981592Srgrimes cbuf[cpos] = c; 12993776Spst if (p != 0) { 1300102565Syar yylval.s = p->name; 1301102565Syar if (!p->implemented) 1302102565Syar return (NOTIMPL); /* state remains CMD */ 13031592Srgrimes state = p->state; 13041592Srgrimes return (p->token); 13051592Srgrimes } 13061592Srgrimes break; 13071592Srgrimes 13081592Srgrimes case SITECMD: 13091592Srgrimes if (cbuf[cpos] == ' ') { 13101592Srgrimes cpos++; 13111592Srgrimes return (SP); 13121592Srgrimes } 13131592Srgrimes cp = &cbuf[cpos]; 13141592Srgrimes if ((cp2 = strpbrk(cp, " \n"))) 13151592Srgrimes cpos = cp2 - cbuf; 13161592Srgrimes c = cbuf[cpos]; 13171592Srgrimes cbuf[cpos] = '\0'; 13181592Srgrimes upper(cp); 13191592Srgrimes p = lookup(sitetab, cp); 13201592Srgrimes cbuf[cpos] = c; 13213777Spst if (guest == 0 && p != 0) { 1322102565Syar yylval.s = p->name; 1323102565Syar if (!p->implemented) { 13241592Srgrimes state = CMD; 1325102565Syar return (NOTIMPL); 13261592Srgrimes } 13271592Srgrimes state = p->state; 13281592Srgrimes return (p->token); 13291592Srgrimes } 13301592Srgrimes state = CMD; 13311592Srgrimes break; 13321592Srgrimes 133375556Sgreen case ZSTR1: 13341592Srgrimes case OSTR: 13351592Srgrimes if (cbuf[cpos] == '\n') { 13361592Srgrimes state = CMD; 13371592Srgrimes return (CRLF); 13381592Srgrimes } 13391592Srgrimes /* FALLTHROUGH */ 13401592Srgrimes 13411592Srgrimes case STR1: 13421592Srgrimes dostr1: 13431592Srgrimes if (cbuf[cpos] == ' ') { 13441592Srgrimes cpos++; 134551979Salfred state = state == OSTR ? STR2 : state+1; 13461592Srgrimes return (SP); 13471592Srgrimes } 13481592Srgrimes break; 13491592Srgrimes 13501592Srgrimes case ZSTR2: 13511592Srgrimes if (cbuf[cpos] == '\n') { 13521592Srgrimes state = CMD; 13531592Srgrimes return (CRLF); 13541592Srgrimes } 13551592Srgrimes /* FALLTHROUGH */ 13561592Srgrimes 13571592Srgrimes case STR2: 13581592Srgrimes cp = &cbuf[cpos]; 13591592Srgrimes n = strlen(cp); 13601592Srgrimes cpos += n - 1; 13611592Srgrimes /* 13621592Srgrimes * Make sure the string is nonempty and \n terminated. 13631592Srgrimes */ 13641592Srgrimes if (n > 1 && cbuf[cpos] == '\n') { 13651592Srgrimes cbuf[cpos] = '\0'; 13661592Srgrimes yylval.s = copy(cp); 13671592Srgrimes cbuf[cpos] = '\n'; 13681592Srgrimes state = ARGS; 13691592Srgrimes return (STRING); 13701592Srgrimes } 13711592Srgrimes break; 13721592Srgrimes 13731592Srgrimes case NSTR: 13741592Srgrimes if (cbuf[cpos] == ' ') { 13751592Srgrimes cpos++; 13761592Srgrimes return (SP); 13771592Srgrimes } 13781592Srgrimes if (isdigit(cbuf[cpos])) { 13791592Srgrimes cp = &cbuf[cpos]; 13801592Srgrimes while (isdigit(cbuf[++cpos])) 13811592Srgrimes ; 13821592Srgrimes c = cbuf[cpos]; 13831592Srgrimes cbuf[cpos] = '\0'; 138492272Smaxim yylval.u.i = atoi(cp); 13851592Srgrimes cbuf[cpos] = c; 13861592Srgrimes state = STR1; 13871592Srgrimes return (NUMBER); 13881592Srgrimes } 13891592Srgrimes state = STR1; 13901592Srgrimes goto dostr1; 13911592Srgrimes 13921592Srgrimes case ARGS: 13931592Srgrimes if (isdigit(cbuf[cpos])) { 13941592Srgrimes cp = &cbuf[cpos]; 13951592Srgrimes while (isdigit(cbuf[++cpos])) 13961592Srgrimes ; 13971592Srgrimes c = cbuf[cpos]; 13981592Srgrimes cbuf[cpos] = '\0'; 139992272Smaxim yylval.u.i = atoi(cp); 1400137811Syar yylval.u.o = strtoull(cp, NULL, 10); 14011592Srgrimes cbuf[cpos] = c; 14021592Srgrimes return (NUMBER); 14031592Srgrimes } 140456668Sshin if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 140556668Sshin && !isalnum(cbuf[cpos + 3])) { 140656668Sshin cpos += 3; 140756668Sshin return ALL; 140856668Sshin } 14091592Srgrimes switch (cbuf[cpos++]) { 14101592Srgrimes 14111592Srgrimes case '\n': 14121592Srgrimes state = CMD; 14131592Srgrimes return (CRLF); 14141592Srgrimes 14151592Srgrimes case ' ': 14161592Srgrimes return (SP); 14171592Srgrimes 14181592Srgrimes case ',': 14191592Srgrimes return (COMMA); 14201592Srgrimes 14211592Srgrimes case 'A': 14221592Srgrimes case 'a': 14231592Srgrimes return (A); 14241592Srgrimes 14251592Srgrimes case 'B': 14261592Srgrimes case 'b': 14271592Srgrimes return (B); 14281592Srgrimes 14291592Srgrimes case 'C': 14301592Srgrimes case 'c': 14311592Srgrimes return (C); 14321592Srgrimes 14331592Srgrimes case 'E': 14341592Srgrimes case 'e': 14351592Srgrimes return (E); 14361592Srgrimes 14371592Srgrimes case 'F': 14381592Srgrimes case 'f': 14391592Srgrimes return (F); 14401592Srgrimes 14411592Srgrimes case 'I': 14421592Srgrimes case 'i': 14431592Srgrimes return (I); 14441592Srgrimes 14451592Srgrimes case 'L': 14461592Srgrimes case 'l': 14471592Srgrimes return (L); 14481592Srgrimes 14491592Srgrimes case 'N': 14501592Srgrimes case 'n': 14511592Srgrimes return (N); 14521592Srgrimes 14531592Srgrimes case 'P': 14541592Srgrimes case 'p': 14551592Srgrimes return (P); 14561592Srgrimes 14571592Srgrimes case 'R': 14581592Srgrimes case 'r': 14591592Srgrimes return (R); 14601592Srgrimes 14611592Srgrimes case 'S': 14621592Srgrimes case 's': 14631592Srgrimes return (S); 14641592Srgrimes 14651592Srgrimes case 'T': 14661592Srgrimes case 't': 14671592Srgrimes return (T); 14681592Srgrimes 14691592Srgrimes } 14701592Srgrimes break; 14711592Srgrimes 14721592Srgrimes default: 147376096Smarkm fatalerror("Unknown state in scanner."); 14741592Srgrimes } 14751592Srgrimes state = CMD; 147689935Syar return (LEXERR); 14771592Srgrimes } 14781592Srgrimes} 14791592Srgrimes 14801592Srgrimesvoid 148190148Simpupper(char *s) 14821592Srgrimes{ 14831592Srgrimes while (*s != '\0') { 14841592Srgrimes if (islower(*s)) 14851592Srgrimes *s = toupper(*s); 14861592Srgrimes s++; 14871592Srgrimes } 14881592Srgrimes} 14891592Srgrimes 14901592Srgrimesstatic char * 149190148Simpcopy(char *s) 14921592Srgrimes{ 14931592Srgrimes char *p; 14941592Srgrimes 1495137659Syar p = malloc(strlen(s) + 1); 14961592Srgrimes if (p == NULL) 149776096Smarkm fatalerror("Ran out of memory."); 14981592Srgrimes (void) strcpy(p, s); 14991592Srgrimes return (p); 15001592Srgrimes} 15011592Srgrimes 15021592Srgrimesstatic void 150390148Simphelp(struct tab *ctab, char *s) 15041592Srgrimes{ 15051592Srgrimes struct tab *c; 15061592Srgrimes int width, NCMDS; 15071592Srgrimes char *type; 15081592Srgrimes 15091592Srgrimes if (ctab == sitetab) 15101592Srgrimes type = "SITE "; 15111592Srgrimes else 15121592Srgrimes type = ""; 15131592Srgrimes width = 0, NCMDS = 0; 15141592Srgrimes for (c = ctab; c->name != NULL; c++) { 15151592Srgrimes int len = strlen(c->name); 15161592Srgrimes 15171592Srgrimes if (len > width) 15181592Srgrimes width = len; 15191592Srgrimes NCMDS++; 15201592Srgrimes } 15211592Srgrimes width = (width + 8) &~ 7; 15221592Srgrimes if (s == 0) { 15231592Srgrimes int i, j, w; 15241592Srgrimes int columns, lines; 15251592Srgrimes 15261592Srgrimes lreply(214, "The following %scommands are recognized %s.", 15271592Srgrimes type, "(* =>'s unimplemented)"); 15281592Srgrimes columns = 76 / width; 15291592Srgrimes if (columns == 0) 15301592Srgrimes columns = 1; 15311592Srgrimes lines = (NCMDS + columns - 1) / columns; 15321592Srgrimes for (i = 0; i < lines; i++) { 15331592Srgrimes printf(" "); 15341592Srgrimes for (j = 0; j < columns; j++) { 15351592Srgrimes c = ctab + j * lines + i; 15361592Srgrimes printf("%s%c", c->name, 15371592Srgrimes c->implemented ? ' ' : '*'); 15381592Srgrimes if (c + lines >= &ctab[NCMDS]) 15391592Srgrimes break; 15401592Srgrimes w = strlen(c->name) + 1; 15411592Srgrimes while (w < width) { 15421592Srgrimes putchar(' '); 15431592Srgrimes w++; 15441592Srgrimes } 15451592Srgrimes } 15461592Srgrimes printf("\r\n"); 15471592Srgrimes } 15481592Srgrimes (void) fflush(stdout); 1549110037Syar if (hostinfo) 1550110037Syar reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1551110037Syar else 1552110037Syar reply(214, "End."); 15531592Srgrimes return; 15541592Srgrimes } 15551592Srgrimes upper(s); 15561592Srgrimes c = lookup(ctab, s); 1557132931Syar if (c == NULL) { 15581592Srgrimes reply(502, "Unknown command %s.", s); 15591592Srgrimes return; 15601592Srgrimes } 15611592Srgrimes if (c->implemented) 15621592Srgrimes reply(214, "Syntax: %s%s %s", type, c->name, c->help); 15631592Srgrimes else 15641592Srgrimes reply(214, "%s%-*s\t%s; unimplemented.", type, width, 15651592Srgrimes c->name, c->help); 15661592Srgrimes} 15671592Srgrimes 15681592Srgrimesstatic void 156990148Simpsizecmd(char *filename) 15701592Srgrimes{ 15711592Srgrimes switch (type) { 15721592Srgrimes case TYPE_L: 15731592Srgrimes case TYPE_I: { 15741592Srgrimes struct stat stbuf; 157563350Sdes if (stat(filename, &stbuf) < 0) 157663350Sdes perror_reply(550, filename); 157763350Sdes else if (!S_ISREG(stbuf.st_mode)) 15781592Srgrimes reply(550, "%s: not a plain file.", filename); 15791592Srgrimes else 1580132929Syar reply(213, "%jd", (intmax_t)stbuf.st_size); 15811592Srgrimes break; } 15821592Srgrimes case TYPE_A: { 15831592Srgrimes FILE *fin; 15841592Srgrimes int c; 15851592Srgrimes off_t count; 15861592Srgrimes struct stat stbuf; 15871592Srgrimes fin = fopen(filename, "r"); 15881592Srgrimes if (fin == NULL) { 15891592Srgrimes perror_reply(550, filename); 15901592Srgrimes return; 15911592Srgrimes } 159263350Sdes if (fstat(fileno(fin), &stbuf) < 0) { 159363350Sdes perror_reply(550, filename); 159463350Sdes (void) fclose(fin); 159563350Sdes return; 159663350Sdes } else if (!S_ISREG(stbuf.st_mode)) { 15971592Srgrimes reply(550, "%s: not a plain file.", filename); 15981592Srgrimes (void) fclose(fin); 15991592Srgrimes return; 1600101034Syar } else if (stbuf.st_size > MAXASIZE) { 1601101034Syar reply(550, "%s: too large for type A SIZE.", filename); 1602101034Syar (void) fclose(fin); 1603101034Syar return; 16041592Srgrimes } 16051592Srgrimes 16061592Srgrimes count = 0; 16071592Srgrimes while((c=getc(fin)) != EOF) { 16081592Srgrimes if (c == '\n') /* will get expanded to \r\n */ 16091592Srgrimes count++; 16101592Srgrimes count++; 16111592Srgrimes } 16121592Srgrimes (void) fclose(fin); 16131592Srgrimes 1614132929Syar reply(213, "%jd", (intmax_t)count); 16151592Srgrimes break; } 16161592Srgrimes default: 1617100684Syar reply(504, "SIZE not implemented for type %s.", 1618100684Syar typenames[type]); 16191592Srgrimes } 16201592Srgrimes} 162156668Sshin 162256668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 162356668Sshinstatic int 162490148Simpport_check(const char *pcmd) 162556668Sshin{ 162656668Sshin if (his_addr.su_family == AF_INET) { 162756668Sshin if (data_dest.su_family != AF_INET) { 162856668Sshin usedefault = 1; 162956668Sshin reply(500, "Invalid address rejected."); 163056668Sshin return 1; 163156668Sshin } 163256668Sshin if (paranoid && 163356668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 163456668Sshin memcmp(&data_dest.su_sin.sin_addr, 163556668Sshin &his_addr.su_sin.sin_addr, 163656668Sshin sizeof(data_dest.su_sin.sin_addr)))) { 163756668Sshin usedefault = 1; 163856668Sshin reply(500, "Illegal PORT range rejected."); 163956668Sshin } else { 164056668Sshin usedefault = 0; 164156668Sshin if (pdata >= 0) { 164256668Sshin (void) close(pdata); 164356668Sshin pdata = -1; 164456668Sshin } 164556668Sshin reply(200, "%s command successful.", pcmd); 164656668Sshin } 164756668Sshin return 1; 164856668Sshin } 164956668Sshin return 0; 165056668Sshin} 165156668Sshin 165270102Sphkstatic int 165390148Simpcheck_login1(void) 165470102Sphk{ 165570102Sphk if (logged_in) 165670102Sphk return 1; 165770102Sphk else { 165870102Sphk reply(530, "Please login with USER and PASS."); 165970102Sphk return 0; 166070102Sphk } 166170102Sphk} 166270102Sphk 1663110340Syar/* 1664110340Syar * Replace leading "~user" in a pathname by the user's login directory. 1665110340Syar * Returned string will be in a freshly malloced buffer unless it's NULL. 1666110340Syar */ 1667110340Syarstatic char * 1668110340Syarexptilde(char *s) 1669110340Syar{ 1670110340Syar char *p, *q; 1671110340Syar char *path, *user; 1672110340Syar struct passwd *ppw; 1673110340Syar 1674110340Syar if ((p = strdup(s)) == NULL) 1675110340Syar return (NULL); 1676110340Syar if (*p != '~') 1677110340Syar return (p); 1678110340Syar 1679110340Syar user = p + 1; /* skip tilde */ 1680110340Syar if ((path = strchr(p, '/')) != NULL) 1681110340Syar *(path++) = '\0'; /* separate ~user from the rest of path */ 1682110378Syar if (*user == '\0') /* no user specified, use the current user */ 1683110378Syar user = pw->pw_name; 1684110378Syar /* read passwd even for the current user since we may be chrooted */ 1685110378Syar if ((ppw = getpwnam(user)) != NULL) { 1686110340Syar /* user found, substitute login directory for ~user */ 1687110340Syar if (path) 1688110340Syar asprintf(&q, "%s/%s", ppw->pw_dir, path); 1689110340Syar else 1690110340Syar q = strdup(ppw->pw_dir); 1691110340Syar free(p); 1692110340Syar p = q; 1693110340Syar } else { 1694110340Syar /* user not found, undo the damage */ 1695110340Syar if (path) 1696110340Syar path[-1] = '/'; 1697110340Syar } 1698110340Syar return (p); 1699110340Syar} 1700110340Syar 1701110340Syar/* 1702110340Syar * Expand glob(3) patterns possibly present in a pathname. 1703110340Syar * Avoid expanding to a pathname including '\r' or '\n' in order to 1704110340Syar * not disrupt the FTP protocol. 1705110340Syar * The expansion found must be unique. 1706110340Syar * Return the result as a malloced string, or NULL if an error occured. 1707110340Syar * 1708110340Syar * Problem: this production is used for all pathname 1709110340Syar * processing, but only gives a 550 error reply. 1710110340Syar * This is a valid reply in some cases but not in others. 1711110340Syar */ 1712110340Syarstatic char * 1713110340Syarexpglob(char *s) 1714110340Syar{ 1715110340Syar char *p, **pp, *rval; 1716110340Syar int flags = GLOB_BRACE | GLOB_NOCHECK; 1717110340Syar int n; 1718110340Syar glob_t gl; 1719110340Syar 1720110340Syar memset(&gl, 0, sizeof(gl)); 1721110340Syar flags |= GLOB_LIMIT; 1722110340Syar gl.gl_matchc = MAXGLOBARGS; 1723110340Syar if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) { 1724110340Syar for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++) 1725110340Syar if (*(*pp + strcspn(*pp, "\r\n")) == '\0') { 1726110340Syar p = *pp; 1727110340Syar n++; 1728110340Syar } 1729110340Syar if (n == 0) 1730110340Syar rval = strdup(s); 1731110340Syar else if (n == 1) 1732110340Syar rval = strdup(p); 1733110340Syar else { 1734137852Syar reply(550, "Wildcard is ambiguous."); 1735110340Syar rval = NULL; 1736110340Syar } 1737110340Syar } else { 1738137852Syar reply(550, "Wildcard expansion error."); 1739110340Syar rval = NULL; 1740110340Syar } 1741110340Syar globfree(&gl); 1742110340Syar return (rval); 1743110340Syar} 1744110340Syar 174556668Sshin#ifdef INET6 174656668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 174756668Sshinstatic int 174890148Simpport_check_v6(const char *pcmd) 174956668Sshin{ 175056668Sshin if (his_addr.su_family == AF_INET6) { 175156668Sshin if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 175256668Sshin /* Convert data_dest into v4 mapped sockaddr.*/ 175356668Sshin v4map_data_dest(); 175456668Sshin if (data_dest.su_family != AF_INET6) { 175556668Sshin usedefault = 1; 175656668Sshin reply(500, "Invalid address rejected."); 175756668Sshin return 1; 175856668Sshin } 175956668Sshin if (paranoid && 176056668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 176156668Sshin memcmp(&data_dest.su_sin6.sin6_addr, 176256668Sshin &his_addr.su_sin6.sin6_addr, 176356668Sshin sizeof(data_dest.su_sin6.sin6_addr)))) { 176456668Sshin usedefault = 1; 176556668Sshin reply(500, "Illegal PORT range rejected."); 176656668Sshin } else { 176756668Sshin usedefault = 0; 176856668Sshin if (pdata >= 0) { 176956668Sshin (void) close(pdata); 177056668Sshin pdata = -1; 177156668Sshin } 177256668Sshin reply(200, "%s command successful.", pcmd); 177356668Sshin } 177456668Sshin return 1; 177556668Sshin } 177656668Sshin return 0; 177756668Sshin} 177856668Sshin 177956668Sshinstatic void 178090148Simpv4map_data_dest(void) 178156668Sshin{ 178256668Sshin struct in_addr savedaddr; 178356668Sshin int savedport; 178456668Sshin 178556668Sshin if (data_dest.su_family != AF_INET) { 178656668Sshin usedefault = 1; 178756668Sshin reply(500, "Invalid address rejected."); 178856668Sshin return; 178956668Sshin } 179056668Sshin 179156668Sshin savedaddr = data_dest.su_sin.sin_addr; 179256668Sshin savedport = data_dest.su_port; 179356668Sshin 179456668Sshin memset(&data_dest, 0, sizeof(data_dest)); 179556668Sshin data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 179656668Sshin data_dest.su_sin6.sin6_family = AF_INET6; 179756668Sshin data_dest.su_sin6.sin6_port = savedport; 179856668Sshin memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 179956668Sshin memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 180056668Sshin (caddr_t)&savedaddr, sizeof(savedaddr)); 180156668Sshin} 180256668Sshin#endif 1803