1331722Seadler/* 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. 13262136Sbrueffer * 3. Neither the name of the University nor the names of its contributors 141592Srgrimes * may be used to endorse or promote products derived from this software 151592Srgrimes * without specific prior written permission. 161592Srgrimes * 171592Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181592Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191592Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201592Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211592Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221592Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231592Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241592Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251592Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261592Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271592Srgrimes * SUCH DAMAGE. 281592Srgrimes * 291592Srgrimes * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 301592Srgrimes */ 311592Srgrimes 321592Srgrimes/* 331592Srgrimes * Grammar for FTP commands. 341592Srgrimes * See RFC 959. 351592Srgrimes */ 361592Srgrimes 371592Srgrimes%{ 381592Srgrimes 391592Srgrimes#ifndef lint 4031329Scharnier#if 0 411592Srgrimesstatic char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; 4231329Scharnier#endif 431592Srgrimes#endif /* not lint */ 441592Srgrimes 45137859Syar#include <sys/cdefs.h> 46137859Syar__FBSDID("$FreeBSD: stable/11/libexec/ftpd/ftpcmd.y 363536 2020-07-25 23:08:51Z truckman $"); 47137859Syar 481592Srgrimes#include <sys/param.h> 491592Srgrimes#include <sys/socket.h> 501592Srgrimes#include <sys/stat.h> 511592Srgrimes 521592Srgrimes#include <netinet/in.h> 531592Srgrimes#include <arpa/ftp.h> 541592Srgrimes 551592Srgrimes#include <ctype.h> 561592Srgrimes#include <errno.h> 571592Srgrimes#include <glob.h> 5892090Smaxim#include <libutil.h> 5992272Smaxim#include <limits.h> 6092090Smaxim#include <md5.h> 6156668Sshin#include <netdb.h> 621592Srgrimes#include <pwd.h> 631592Srgrimes#include <signal.h> 64132929Syar#include <stdint.h> 651592Srgrimes#include <stdio.h> 661592Srgrimes#include <stdlib.h> 671592Srgrimes#include <string.h> 681592Srgrimes#include <syslog.h> 691592Srgrimes#include <time.h> 701592Srgrimes#include <unistd.h> 711592Srgrimes 721592Srgrimes#include "extern.h" 73109380Syar#include "pathnames.h" 741592Srgrimes 75363536Struckman#define yylex ftpcmd_yylex 76363536Struckman 771592Srgrimesoff_t restart_point; 781592Srgrimes 791592Srgrimesstatic int cmd_type; 801592Srgrimesstatic int cmd_form; 811592Srgrimesstatic int cmd_bytesz; 8289935Syarstatic int state; 831592Srgrimeschar cbuf[512]; 84132931Syarchar *fromname = NULL; 851592Srgrimes 861592Srgrimes%} 871592Srgrimes 881592Srgrimes%union { 8992272Smaxim struct { 9092272Smaxim off_t o; 9192272Smaxim int i; 9292272Smaxim } u; 931592Srgrimes char *s; 941592Srgrimes} 951592Srgrimes 961592Srgrimes%token 971592Srgrimes A B C E F I 981592Srgrimes L N P R S T 9956668Sshin ALL 1001592Srgrimes 1011592Srgrimes SP CRLF COMMA 1021592Srgrimes 1031592Srgrimes USER PASS ACCT REIN QUIT PORT 1041592Srgrimes PASV TYPE STRU MODE RETR STOR 1051592Srgrimes APPE MLFL MAIL MSND MSOM MSAM 1061592Srgrimes MRSQ MRCP ALLO REST RNFR RNTO 1071592Srgrimes ABOR DELE CWD LIST NLST SITE 1081592Srgrimes STAT HELP NOOP MKD RMD PWD 1091592Srgrimes CDUP STOU SMNT SYST SIZE MDTM 110168849Syar LPRT LPSV EPRT EPSV FEAT 1111592Srgrimes 11275535Sphk UMASK IDLE CHMOD MDFIVE 1131592Srgrimes 114102565Syar LEXERR NOTIMPL 1151592Srgrimes 1161592Srgrimes%token <s> STRING 11792272Smaxim%token <u> NUMBER 1181592Srgrimes 11992272Smaxim%type <u.i> check_login octal_number byte_size 12092272Smaxim%type <u.i> check_login_ro check_login_epsv 12192272Smaxim%type <u.i> struct_code mode_code type_code form_code 12275567Speter%type <s> pathstring pathname password username 123102565Syar%type <s> ALL NOTIMPL 1241592Srgrimes 1251592Srgrimes%start cmd_list 1261592Srgrimes 1271592Srgrimes%% 1281592Srgrimes 1291592Srgrimescmd_list 1301592Srgrimes : /* empty */ 1311592Srgrimes | cmd_list cmd 1321592Srgrimes { 13388935Sdwmalone if (fromname) 13488935Sdwmalone free(fromname); 135132931Syar fromname = NULL; 136132930Syar restart_point = 0; 1371592Srgrimes } 1381592Srgrimes | cmd_list rcmd 1391592Srgrimes ; 1401592Srgrimes 1411592Srgrimescmd 1421592Srgrimes : USER SP username CRLF 1431592Srgrimes { 1441592Srgrimes user($3); 1451592Srgrimes free($3); 1461592Srgrimes } 1471592Srgrimes | PASS SP password CRLF 1481592Srgrimes { 1491592Srgrimes pass($3); 1501592Srgrimes free($3); 1511592Srgrimes } 15275556Sgreen | PASS CRLF 15375556Sgreen { 15475556Sgreen pass(""); 15575556Sgreen } 15617433Spst | PORT check_login SP host_port CRLF 1571592Srgrimes { 15856668Sshin if (epsvall) { 159137852Syar reply(501, "No PORT allowed after EPSV ALL."); 16056668Sshin goto port_done; 16156668Sshin } 16256668Sshin if (!$2) 16356668Sshin goto port_done; 16456668Sshin if (port_check("PORT") == 1) 16556668Sshin goto port_done; 16656668Sshin#ifdef INET6 16756668Sshin if ((his_addr.su_family != AF_INET6 || 16856668Sshin !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { 16956668Sshin /* shoud never happen */ 17056668Sshin usedefault = 1; 17156668Sshin reply(500, "Invalid address rejected."); 17256668Sshin goto port_done; 17356668Sshin } 17456668Sshin port_check_v6("pcmd"); 17556668Sshin#endif 17656668Sshin port_done: 177132925Syar ; 17856668Sshin } 17956668Sshin | LPRT check_login SP host_long_port CRLF 18056668Sshin { 18156668Sshin if (epsvall) { 182137852Syar reply(501, "No LPRT allowed after EPSV ALL."); 18356668Sshin goto lprt_done; 18456668Sshin } 18556668Sshin if (!$2) 18656668Sshin goto lprt_done; 18756668Sshin if (port_check("LPRT") == 1) 18856668Sshin goto lprt_done; 18956668Sshin#ifdef INET6 19056668Sshin if (his_addr.su_family != AF_INET6) { 19156668Sshin usedefault = 1; 19256668Sshin reply(500, "Invalid address rejected."); 19356668Sshin goto lprt_done; 19456668Sshin } 19556668Sshin if (port_check_v6("LPRT") == 1) 19656668Sshin goto lprt_done; 19756668Sshin#endif 19856668Sshin lprt_done: 199132925Syar ; 20056668Sshin } 20156668Sshin | EPRT check_login SP STRING CRLF 20256668Sshin { 20356668Sshin char delim; 20456668Sshin char *tmp = NULL; 20556668Sshin char *p, *q; 20656668Sshin char *result[3]; 20756668Sshin struct addrinfo hints; 20856668Sshin struct addrinfo *res; 20956668Sshin int i; 21056668Sshin 21156668Sshin if (epsvall) { 212137852Syar reply(501, "No EPRT allowed after EPSV ALL."); 21356668Sshin goto eprt_done; 21456668Sshin } 21556668Sshin if (!$2) 21656668Sshin goto eprt_done; 21756668Sshin 21856668Sshin memset(&data_dest, 0, sizeof(data_dest)); 21956668Sshin tmp = strdup($4); 22076096Smarkm if (ftpdebug) 22156668Sshin syslog(LOG_DEBUG, "%s", tmp); 22256668Sshin if (!tmp) { 22376096Smarkm fatalerror("not enough core"); 22456668Sshin /*NOTREACHED*/ 22556668Sshin } 22656668Sshin p = tmp; 22756668Sshin delim = p[0]; 22856668Sshin p++; 22956668Sshin memset(result, 0, sizeof(result)); 23056668Sshin for (i = 0; i < 3; i++) { 23156668Sshin q = strchr(p, delim); 23256668Sshin if (!q || *q != delim) { 23356668Sshin parsefail: 23456668Sshin reply(500, 23556668Sshin "Invalid argument, rejected."); 23656668Sshin if (tmp) 23756668Sshin free(tmp); 23817433Spst usedefault = 1; 23956668Sshin goto eprt_done; 24017433Spst } 24156668Sshin *q++ = '\0'; 24256668Sshin result[i] = p; 24376096Smarkm if (ftpdebug) 24456668Sshin syslog(LOG_DEBUG, "%d: %s", i, p); 24556668Sshin p = q; 2461592Srgrimes } 24756668Sshin 24856668Sshin /* some more sanity check */ 24956668Sshin p = result[0]; 25056668Sshin while (*p) { 25156668Sshin if (!isdigit(*p)) 25256668Sshin goto parsefail; 25356668Sshin p++; 25456668Sshin } 25556668Sshin p = result[2]; 25656668Sshin while (*p) { 25756668Sshin if (!isdigit(*p)) 25856668Sshin goto parsefail; 25956668Sshin p++; 26056668Sshin } 26156668Sshin 26256668Sshin /* grab address */ 26356668Sshin memset(&hints, 0, sizeof(hints)); 26456668Sshin if (atoi(result[0]) == 1) 26556668Sshin hints.ai_family = PF_INET; 26656668Sshin#ifdef INET6 26756668Sshin else if (atoi(result[0]) == 2) 26856668Sshin hints.ai_family = PF_INET6; 26956668Sshin#endif 27056668Sshin else 27156668Sshin hints.ai_family = PF_UNSPEC; /*XXX*/ 27256668Sshin hints.ai_socktype = SOCK_STREAM; 27356668Sshin i = getaddrinfo(result[1], result[2], &hints, &res); 27456668Sshin if (i) 27556668Sshin goto parsefail; 27656668Sshin memcpy(&data_dest, res->ai_addr, res->ai_addrlen); 27756668Sshin#ifdef INET6 27856668Sshin if (his_addr.su_family == AF_INET6 27956668Sshin && data_dest.su_family == AF_INET6) { 28056668Sshin /* XXX more sanity checks! */ 28156668Sshin data_dest.su_sin6.sin6_scope_id = 28256668Sshin his_addr.su_sin6.sin6_scope_id; 28356668Sshin } 28456668Sshin#endif 28556668Sshin free(tmp); 28656668Sshin tmp = NULL; 28756668Sshin 28856668Sshin if (port_check("EPRT") == 1) 28956668Sshin goto eprt_done; 29056668Sshin#ifdef INET6 29156668Sshin if (his_addr.su_family != AF_INET6) { 29256668Sshin usedefault = 1; 29356668Sshin reply(500, "Invalid address rejected."); 29456668Sshin goto eprt_done; 29556668Sshin } 29656668Sshin if (port_check_v6("EPRT") == 1) 29756668Sshin goto eprt_done; 29856668Sshin#endif 29988935Sdwmalone eprt_done: 30088935Sdwmalone free($4); 3011592Srgrimes } 30217433Spst | PASV check_login CRLF 3031592Srgrimes { 30456668Sshin if (epsvall) 305137852Syar reply(501, "No PASV allowed after EPSV ALL."); 30656668Sshin else if ($2) 30717433Spst passive(); 3081592Srgrimes } 30956668Sshin | LPSV check_login CRLF 31056668Sshin { 31156668Sshin if (epsvall) 312137852Syar reply(501, "No LPSV allowed after EPSV ALL."); 31356668Sshin else if ($2) 31456668Sshin long_passive("LPSV", PF_UNSPEC); 31556668Sshin } 31670102Sphk | EPSV check_login_epsv SP NUMBER CRLF 31756668Sshin { 31856668Sshin if ($2) { 31956668Sshin int pf; 32092272Smaxim switch ($4.i) { 32156668Sshin case 1: 32256668Sshin pf = PF_INET; 32356668Sshin break; 32456668Sshin#ifdef INET6 32556668Sshin case 2: 32656668Sshin pf = PF_INET6; 32756668Sshin break; 32856668Sshin#endif 32956668Sshin default: 33056668Sshin pf = -1; /*junk value*/ 33156668Sshin break; 33256668Sshin } 33356668Sshin long_passive("EPSV", pf); 33456668Sshin } 33556668Sshin } 33670102Sphk | EPSV check_login_epsv SP ALL CRLF 33756668Sshin { 33856668Sshin if ($2) { 339137852Syar reply(200, "EPSV ALL command successful."); 34056668Sshin epsvall++; 34156668Sshin } 34256668Sshin } 34370102Sphk | EPSV check_login_epsv CRLF 34456668Sshin { 34556668Sshin if ($2) 34656668Sshin long_passive("EPSV", PF_UNSPEC); 34756668Sshin } 34871278Sjedgar | TYPE check_login SP type_code CRLF 3491592Srgrimes { 35071278Sjedgar if ($2) { 35171278Sjedgar switch (cmd_type) { 3521592Srgrimes 35371278Sjedgar case TYPE_A: 35471278Sjedgar if (cmd_form == FORM_N) { 35571278Sjedgar reply(200, "Type set to A."); 35671278Sjedgar type = cmd_type; 35771278Sjedgar form = cmd_form; 35871278Sjedgar } else 35971278Sjedgar reply(504, "Form must be N."); 36071278Sjedgar break; 3611592Srgrimes 36271278Sjedgar case TYPE_E: 36371278Sjedgar reply(504, "Type E not implemented."); 36471278Sjedgar break; 3651592Srgrimes 36671278Sjedgar case TYPE_I: 36771278Sjedgar reply(200, "Type set to I."); 36871278Sjedgar type = cmd_type; 36971278Sjedgar break; 3701592Srgrimes 37171278Sjedgar case TYPE_L: 372103949Smike#if CHAR_BIT == 8 37371278Sjedgar if (cmd_bytesz == 8) { 37471278Sjedgar reply(200, 37571278Sjedgar "Type set to L (byte size 8)."); 37671278Sjedgar type = cmd_type; 37771278Sjedgar } else 37871278Sjedgar reply(504, "Byte size must be 8."); 379103949Smike#else /* CHAR_BIT == 8 */ 380103949Smike UNIMPLEMENTED for CHAR_BIT != 8 381103949Smike#endif /* CHAR_BIT == 8 */ 38271278Sjedgar } 3831592Srgrimes } 3841592Srgrimes } 38571278Sjedgar | STRU check_login SP struct_code CRLF 3861592Srgrimes { 38771278Sjedgar if ($2) { 38871278Sjedgar switch ($4) { 3891592Srgrimes 39071278Sjedgar case STRU_F: 391137852Syar reply(200, "STRU F accepted."); 39271278Sjedgar break; 3931592Srgrimes 39471278Sjedgar default: 39571278Sjedgar reply(504, "Unimplemented STRU type."); 39671278Sjedgar } 3971592Srgrimes } 3981592Srgrimes } 39971278Sjedgar | MODE check_login SP mode_code CRLF 4001592Srgrimes { 40171278Sjedgar if ($2) { 40271278Sjedgar switch ($4) { 4031592Srgrimes 40471278Sjedgar case MODE_S: 405137852Syar reply(200, "MODE S accepted."); 40671278Sjedgar break; 40771278Sjedgar 40871278Sjedgar default: 40971278Sjedgar reply(502, "Unimplemented MODE type."); 41071278Sjedgar } 4111592Srgrimes } 4121592Srgrimes } 41371278Sjedgar | ALLO check_login SP NUMBER CRLF 4141592Srgrimes { 41571278Sjedgar if ($2) { 41671278Sjedgar reply(202, "ALLO command ignored."); 41771278Sjedgar } 4181592Srgrimes } 41971278Sjedgar | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 4201592Srgrimes { 42171278Sjedgar if ($2) { 42271278Sjedgar reply(202, "ALLO command ignored."); 42371278Sjedgar } 4241592Srgrimes } 4251592Srgrimes | RETR check_login SP pathname CRLF 4261592Srgrimes { 42782796Ssheldonh if (noretr || (guest && noguestretr)) 428137852Syar reply(500, "RETR command disabled."); 42982460Snik else if ($2 && $4 != NULL) 430132931Syar retrieve(NULL, $4); 43182460Snik 4321592Srgrimes if ($4 != NULL) 4331592Srgrimes free($4); 4341592Srgrimes } 43570102Sphk | STOR check_login_ro SP pathname CRLF 4361592Srgrimes { 4371592Srgrimes if ($2 && $4 != NULL) 4381592Srgrimes store($4, "w", 0); 4391592Srgrimes if ($4 != NULL) 4401592Srgrimes free($4); 4411592Srgrimes } 44270102Sphk | APPE check_login_ro SP pathname CRLF 4431592Srgrimes { 4441592Srgrimes if ($2 && $4 != NULL) 4451592Srgrimes store($4, "a", 0); 4461592Srgrimes if ($4 != NULL) 4471592Srgrimes free($4); 4481592Srgrimes } 4491592Srgrimes | NLST check_login CRLF 4501592Srgrimes { 4511592Srgrimes if ($2) 4521592Srgrimes send_file_list("."); 4531592Srgrimes } 454101395Syar | NLST check_login SP pathstring CRLF 4551592Srgrimes { 456101395Syar if ($2) 4571592Srgrimes send_file_list($4); 458101395Syar free($4); 4591592Srgrimes } 4601592Srgrimes | LIST check_login CRLF 4611592Srgrimes { 4621592Srgrimes if ($2) 463109380Syar retrieve(_PATH_LS " -lgA", ""); 4641592Srgrimes } 46575567Speter | LIST check_login SP pathstring CRLF 4661592Srgrimes { 467101395Syar if ($2) 468109380Syar retrieve(_PATH_LS " -lgA %s", $4); 469101395Syar free($4); 4701592Srgrimes } 4711592Srgrimes | STAT check_login SP pathname CRLF 4721592Srgrimes { 4731592Srgrimes if ($2 && $4 != NULL) 4741592Srgrimes statfilecmd($4); 4751592Srgrimes if ($4 != NULL) 4761592Srgrimes free($4); 4771592Srgrimes } 47871278Sjedgar | STAT check_login CRLF 4791592Srgrimes { 48071278Sjedgar if ($2) { 48171278Sjedgar statcmd(); 48271278Sjedgar } 4831592Srgrimes } 48470102Sphk | DELE check_login_ro SP pathname CRLF 4851592Srgrimes { 4861592Srgrimes if ($2 && $4 != NULL) 4871592Srgrimes delete($4); 4881592Srgrimes if ($4 != NULL) 4891592Srgrimes free($4); 4901592Srgrimes } 49170102Sphk | RNTO check_login_ro SP pathname CRLF 4921592Srgrimes { 493101379Syar if ($2 && $4 != NULL) { 49417433Spst if (fromname) { 49517433Spst renamecmd(fromname, $4); 49617433Spst free(fromname); 497132931Syar fromname = NULL; 49817433Spst } else { 49917433Spst reply(503, "Bad sequence of commands."); 50017433Spst } 5011592Srgrimes } 502101379Syar if ($4 != NULL) 503101379Syar free($4); 5041592Srgrimes } 50571278Sjedgar | ABOR check_login CRLF 5061592Srgrimes { 50771278Sjedgar if ($2) 50871278Sjedgar reply(225, "ABOR command successful."); 5091592Srgrimes } 5101592Srgrimes | CWD check_login CRLF 5111592Srgrimes { 51269234Sdanny if ($2) { 513110036Syar cwd(homedir); 51469234Sdanny } 5151592Srgrimes } 5161592Srgrimes | CWD check_login SP pathname CRLF 5171592Srgrimes { 5181592Srgrimes if ($2 && $4 != NULL) 5191592Srgrimes cwd($4); 5201592Srgrimes if ($4 != NULL) 5211592Srgrimes free($4); 5221592Srgrimes } 5231592Srgrimes | HELP CRLF 5241592Srgrimes { 525132931Syar help(cmdtab, NULL); 5261592Srgrimes } 5271592Srgrimes | HELP SP STRING CRLF 5281592Srgrimes { 5291592Srgrimes char *cp = $3; 5301592Srgrimes 5311592Srgrimes if (strncasecmp(cp, "SITE", 4) == 0) { 5321592Srgrimes cp = $3 + 4; 5331592Srgrimes if (*cp == ' ') 5341592Srgrimes cp++; 5351592Srgrimes if (*cp) 5361592Srgrimes help(sitetab, cp); 5371592Srgrimes else 538132931Syar help(sitetab, NULL); 5391592Srgrimes } else 5401592Srgrimes help(cmdtab, $3); 54188935Sdwmalone free($3); 5421592Srgrimes } 5431592Srgrimes | NOOP CRLF 5441592Srgrimes { 5451592Srgrimes reply(200, "NOOP command successful."); 5461592Srgrimes } 54770102Sphk | MKD check_login_ro SP pathname CRLF 5481592Srgrimes { 5491592Srgrimes if ($2 && $4 != NULL) 5501592Srgrimes makedir($4); 5511592Srgrimes if ($4 != NULL) 5521592Srgrimes free($4); 5531592Srgrimes } 55470102Sphk | RMD check_login_ro SP pathname CRLF 5551592Srgrimes { 5561592Srgrimes if ($2 && $4 != NULL) 5571592Srgrimes removedir($4); 5581592Srgrimes if ($4 != NULL) 5591592Srgrimes free($4); 5601592Srgrimes } 5611592Srgrimes | PWD check_login CRLF 5621592Srgrimes { 5631592Srgrimes if ($2) 5641592Srgrimes pwd(); 5651592Srgrimes } 5661592Srgrimes | CDUP check_login CRLF 5671592Srgrimes { 5681592Srgrimes if ($2) 5691592Srgrimes cwd(".."); 5701592Srgrimes } 5711592Srgrimes | SITE SP HELP CRLF 5721592Srgrimes { 573132931Syar help(sitetab, NULL); 5741592Srgrimes } 5751592Srgrimes | SITE SP HELP SP STRING CRLF 5761592Srgrimes { 5771592Srgrimes help(sitetab, $5); 57888935Sdwmalone free($5); 5791592Srgrimes } 58075535Sphk | SITE SP MDFIVE check_login SP pathname CRLF 58175535Sphk { 58275535Sphk char p[64], *q; 58375535Sphk 584101379Syar if ($4 && $6) { 58575535Sphk q = MD5File($6, p); 58675535Sphk if (q != NULL) 58775535Sphk reply(200, "MD5(%s) = %s", $6, p); 58875535Sphk else 58975535Sphk perror_reply(550, $6); 59075535Sphk } 59188935Sdwmalone if ($6) 59288935Sdwmalone free($6); 59375535Sphk } 5941592Srgrimes | SITE SP UMASK check_login CRLF 5951592Srgrimes { 5961592Srgrimes int oldmask; 5971592Srgrimes 5981592Srgrimes if ($4) { 5991592Srgrimes oldmask = umask(0); 6001592Srgrimes (void) umask(oldmask); 601137852Syar reply(200, "Current UMASK is %03o.", oldmask); 6021592Srgrimes } 6031592Srgrimes } 6041592Srgrimes | SITE SP UMASK check_login SP octal_number CRLF 6051592Srgrimes { 6061592Srgrimes int oldmask; 6071592Srgrimes 6081592Srgrimes if ($4) { 6091592Srgrimes if (($6 == -1) || ($6 > 0777)) { 610137852Syar reply(501, "Bad UMASK value."); 6111592Srgrimes } else { 6121592Srgrimes oldmask = umask($6); 6131592Srgrimes reply(200, 614137852Syar "UMASK set to %03o (was %03o).", 6151592Srgrimes $6, oldmask); 6161592Srgrimes } 6171592Srgrimes } 6181592Srgrimes } 61970102Sphk | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF 6201592Srgrimes { 6211592Srgrimes if ($4 && ($8 != NULL)) { 622101378Syar if (($6 == -1 ) || ($6 > 0777)) 623137852Syar reply(501, "Bad mode value."); 6241592Srgrimes else if (chmod($8, $6) < 0) 6251592Srgrimes perror_reply(550, $8); 6261592Srgrimes else 6271592Srgrimes reply(200, "CHMOD command successful."); 6281592Srgrimes } 6291592Srgrimes if ($8 != NULL) 6301592Srgrimes free($8); 6311592Srgrimes } 63271278Sjedgar | SITE SP check_login IDLE CRLF 6331592Srgrimes { 63471278Sjedgar if ($3) 63571278Sjedgar reply(200, 636137852Syar "Current IDLE time limit is %d seconds; max %d.", 63771278Sjedgar timeout, maxtimeout); 6381592Srgrimes } 63971278Sjedgar | SITE SP check_login IDLE SP NUMBER CRLF 6401592Srgrimes { 64171278Sjedgar if ($3) { 64292272Smaxim if ($6.i < 30 || $6.i > maxtimeout) { 64371278Sjedgar reply(501, 644137852Syar "Maximum IDLE time must be between 30 and %d seconds.", 64571278Sjedgar maxtimeout); 64671278Sjedgar } else { 64792272Smaxim timeout = $6.i; 648137659Syar (void) alarm(timeout); 64971278Sjedgar reply(200, 650137852Syar "Maximum IDLE time set to %d seconds.", 65171278Sjedgar timeout); 65271278Sjedgar } 6531592Srgrimes } 6541592Srgrimes } 65570102Sphk | STOU check_login_ro SP pathname CRLF 6561592Srgrimes { 6571592Srgrimes if ($2 && $4 != NULL) 6581592Srgrimes store($4, "w", 1); 6591592Srgrimes if ($4 != NULL) 6601592Srgrimes free($4); 6611592Srgrimes } 662168849Syar | FEAT CRLF 663168849Syar { 664168849Syar lreply(211, "Extensions supported:"); 665168849Syar#if 0 666168849Syar /* XXX these two keywords are non-standard */ 667168849Syar printf(" EPRT\r\n"); 668168849Syar if (!noepsv) 669168849Syar printf(" EPSV\r\n"); 670168849Syar#endif 671168849Syar printf(" MDTM\r\n"); 672168849Syar printf(" REST STREAM\r\n"); 673168849Syar printf(" SIZE\r\n"); 674168849Syar if (assumeutf8) { 675168849Syar /* TVFS requires UTF8, see RFC 3659 */ 676168849Syar printf(" TVFS\r\n"); 677168849Syar printf(" UTF8\r\n"); 678168849Syar } 679168849Syar reply(211, "End."); 680168849Syar } 68171278Sjedgar | SYST check_login CRLF 6821592Srgrimes { 683116439Syar if ($2) { 684116439Syar if (hostinfo) 6851592Srgrimes#ifdef BSD 686116439Syar reply(215, "UNIX Type: L%d Version: BSD-%d", 687116439Syar CHAR_BIT, BSD); 6881592Srgrimes#else /* BSD */ 689116439Syar reply(215, "UNIX Type: L%d", CHAR_BIT); 6901592Srgrimes#endif /* BSD */ 691116439Syar else 692116439Syar reply(215, "UNKNOWN Type: L%d", CHAR_BIT); 693116439Syar } 6941592Srgrimes } 6951592Srgrimes 6961592Srgrimes /* 6971592Srgrimes * SIZE is not in RFC959, but Postel has blessed it and 6981592Srgrimes * it will be in the updated RFC. 6991592Srgrimes * 7001592Srgrimes * Return size of file in a format suitable for 7011592Srgrimes * using with RESTART (we just count bytes). 7021592Srgrimes */ 7031592Srgrimes | SIZE check_login SP pathname CRLF 7041592Srgrimes { 7051592Srgrimes if ($2 && $4 != NULL) 7061592Srgrimes sizecmd($4); 7071592Srgrimes if ($4 != NULL) 7081592Srgrimes free($4); 7091592Srgrimes } 7101592Srgrimes 7111592Srgrimes /* 7121592Srgrimes * MDTM is not in RFC959, but Postel has blessed it and 7131592Srgrimes * it will be in the updated RFC. 7141592Srgrimes * 7151592Srgrimes * Return modification time of file as an ISO 3307 7161592Srgrimes * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 7171592Srgrimes * where xxx is the fractional second (of any precision, 7181592Srgrimes * not necessarily 3 digits) 7191592Srgrimes */ 7201592Srgrimes | MDTM check_login SP pathname CRLF 7211592Srgrimes { 7221592Srgrimes if ($2 && $4 != NULL) { 7231592Srgrimes struct stat stbuf; 7241592Srgrimes if (stat($4, &stbuf) < 0) 725137850Syar perror_reply(550, $4); 7261592Srgrimes else if (!S_ISREG(stbuf.st_mode)) { 7271592Srgrimes reply(550, "%s: not a plain file.", $4); 7281592Srgrimes } else { 7291592Srgrimes struct tm *t; 7301592Srgrimes t = gmtime(&stbuf.st_mtime); 7311592Srgrimes reply(213, 73217435Spst "%04d%02d%02d%02d%02d%02d", 73317435Spst 1900 + t->tm_year, 73417435Spst t->tm_mon+1, t->tm_mday, 7351592Srgrimes t->tm_hour, t->tm_min, t->tm_sec); 7361592Srgrimes } 7371592Srgrimes } 7381592Srgrimes if ($4 != NULL) 7391592Srgrimes free($4); 7401592Srgrimes } 7411592Srgrimes | QUIT CRLF 7421592Srgrimes { 7431592Srgrimes reply(221, "Goodbye."); 7441592Srgrimes dologout(0); 7451592Srgrimes } 746102565Syar | NOTIMPL 747102565Syar { 748102565Syar nack($1); 749102565Syar } 75089935Syar | error 7511592Srgrimes { 75289935Syar yyclearin; /* discard lookahead data */ 75389935Syar yyerrok; /* clear error condition */ 754102565Syar state = CMD; /* reset lexer state */ 7551592Srgrimes } 7561592Srgrimes ; 7571592Srgrimesrcmd 75870102Sphk : RNFR check_login_ro SP pathname CRLF 7591592Srgrimes { 760132930Syar restart_point = 0; 7611592Srgrimes if ($2 && $4) { 76288935Sdwmalone if (fromname) 76388935Sdwmalone free(fromname); 764132931Syar fromname = NULL; 76588935Sdwmalone if (renamefrom($4)) 76688935Sdwmalone fromname = $4; 76788935Sdwmalone else 7681592Srgrimes free($4); 76988935Sdwmalone } else if ($4) { 77088935Sdwmalone free($4); 7711592Srgrimes } 7721592Srgrimes } 77392272Smaxim | REST check_login SP NUMBER CRLF 7741592Srgrimes { 77571278Sjedgar if ($2) { 77688935Sdwmalone if (fromname) 77788935Sdwmalone free(fromname); 778132931Syar fromname = NULL; 77992272Smaxim restart_point = $4.o; 780132929Syar reply(350, "Restarting at %jd. %s", 781132929Syar (intmax_t)restart_point, 78271278Sjedgar "Send STORE or RETRIEVE to initiate transfer."); 78371278Sjedgar } 7841592Srgrimes } 7851592Srgrimes ; 7861592Srgrimes 7871592Srgrimesusername 7881592Srgrimes : STRING 7891592Srgrimes ; 7901592Srgrimes 7911592Srgrimespassword 7921592Srgrimes : /* empty */ 7931592Srgrimes { 7941592Srgrimes $$ = (char *)calloc(1, sizeof(char)); 7951592Srgrimes } 7961592Srgrimes | STRING 7971592Srgrimes ; 7981592Srgrimes 7991592Srgrimesbyte_size 8001592Srgrimes : NUMBER 80192272Smaxim { 80292272Smaxim $$ = $1.i; 80392272Smaxim } 8041592Srgrimes ; 8051592Srgrimes 8061592Srgrimeshost_port 8071592Srgrimes : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 8081592Srgrimes NUMBER COMMA NUMBER 8091592Srgrimes { 8101592Srgrimes char *a, *p; 8111592Srgrimes 81256668Sshin data_dest.su_len = sizeof(struct sockaddr_in); 81356668Sshin data_dest.su_family = AF_INET; 81456668Sshin p = (char *)&data_dest.su_sin.sin_port; 81592272Smaxim p[0] = $9.i; p[1] = $11.i; 81656668Sshin a = (char *)&data_dest.su_sin.sin_addr; 81792272Smaxim a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; 8181592Srgrimes } 8191592Srgrimes ; 8201592Srgrimes 82156668Sshinhost_long_port 82256668Sshin : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 82356668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 82456668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 82556668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 82656668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 82756668Sshin NUMBER 82856668Sshin { 82956668Sshin char *a, *p; 83056668Sshin 83156668Sshin memset(&data_dest, 0, sizeof(data_dest)); 83256668Sshin data_dest.su_len = sizeof(struct sockaddr_in6); 83356668Sshin data_dest.su_family = AF_INET6; 83456668Sshin p = (char *)&data_dest.su_port; 83592272Smaxim p[0] = $39.i; p[1] = $41.i; 83656668Sshin a = (char *)&data_dest.su_sin6.sin6_addr; 83792272Smaxim a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 83892272Smaxim a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i; 83992272Smaxim a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i; 84092272Smaxim a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i; 84156668Sshin if (his_addr.su_family == AF_INET6) { 84256668Sshin /* XXX more sanity checks! */ 84356668Sshin data_dest.su_sin6.sin6_scope_id = 84456668Sshin his_addr.su_sin6.sin6_scope_id; 84556668Sshin } 84692272Smaxim if ($1.i != 6 || $3.i != 16 || $37.i != 2) 84756668Sshin memset(&data_dest, 0, sizeof(data_dest)); 84856668Sshin } 84956668Sshin | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 85056668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 85156668Sshin NUMBER 85256668Sshin { 85356668Sshin char *a, *p; 85456668Sshin 85556668Sshin memset(&data_dest, 0, sizeof(data_dest)); 85656668Sshin data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 85756668Sshin data_dest.su_family = AF_INET; 85856668Sshin p = (char *)&data_dest.su_port; 85992272Smaxim p[0] = $15.i; p[1] = $17.i; 86056668Sshin a = (char *)&data_dest.su_sin.sin_addr; 86192272Smaxim a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 86292272Smaxim if ($1.i != 4 || $3.i != 4 || $13.i != 2) 86356668Sshin memset(&data_dest, 0, sizeof(data_dest)); 86456668Sshin } 86556668Sshin ; 86656668Sshin 8671592Srgrimesform_code 8681592Srgrimes : N 8691592Srgrimes { 8701592Srgrimes $$ = FORM_N; 8711592Srgrimes } 8721592Srgrimes | T 8731592Srgrimes { 8741592Srgrimes $$ = FORM_T; 8751592Srgrimes } 8761592Srgrimes | C 8771592Srgrimes { 8781592Srgrimes $$ = FORM_C; 8791592Srgrimes } 8801592Srgrimes ; 8811592Srgrimes 8821592Srgrimestype_code 8831592Srgrimes : A 8841592Srgrimes { 8851592Srgrimes cmd_type = TYPE_A; 8861592Srgrimes cmd_form = FORM_N; 8871592Srgrimes } 8881592Srgrimes | A SP form_code 8891592Srgrimes { 8901592Srgrimes cmd_type = TYPE_A; 8911592Srgrimes cmd_form = $3; 8921592Srgrimes } 8931592Srgrimes | E 8941592Srgrimes { 8951592Srgrimes cmd_type = TYPE_E; 8961592Srgrimes cmd_form = FORM_N; 8971592Srgrimes } 8981592Srgrimes | E SP form_code 8991592Srgrimes { 9001592Srgrimes cmd_type = TYPE_E; 9011592Srgrimes cmd_form = $3; 9021592Srgrimes } 9031592Srgrimes | I 9041592Srgrimes { 9051592Srgrimes cmd_type = TYPE_I; 9061592Srgrimes } 9071592Srgrimes | L 9081592Srgrimes { 9091592Srgrimes cmd_type = TYPE_L; 910103949Smike cmd_bytesz = CHAR_BIT; 9111592Srgrimes } 9121592Srgrimes | L SP byte_size 9131592Srgrimes { 9141592Srgrimes cmd_type = TYPE_L; 9151592Srgrimes cmd_bytesz = $3; 9161592Srgrimes } 9171592Srgrimes /* this is for a bug in the BBN ftp */ 9181592Srgrimes | L byte_size 9191592Srgrimes { 9201592Srgrimes cmd_type = TYPE_L; 9211592Srgrimes cmd_bytesz = $2; 9221592Srgrimes } 9231592Srgrimes ; 9241592Srgrimes 9251592Srgrimesstruct_code 9261592Srgrimes : F 9271592Srgrimes { 9281592Srgrimes $$ = STRU_F; 9291592Srgrimes } 9301592Srgrimes | R 9311592Srgrimes { 9321592Srgrimes $$ = STRU_R; 9331592Srgrimes } 9341592Srgrimes | P 9351592Srgrimes { 9361592Srgrimes $$ = STRU_P; 9371592Srgrimes } 9381592Srgrimes ; 9391592Srgrimes 9401592Srgrimesmode_code 9411592Srgrimes : S 9421592Srgrimes { 9431592Srgrimes $$ = MODE_S; 9441592Srgrimes } 9451592Srgrimes | B 9461592Srgrimes { 9471592Srgrimes $$ = MODE_B; 9481592Srgrimes } 9491592Srgrimes | C 9501592Srgrimes { 9511592Srgrimes $$ = MODE_C; 9521592Srgrimes } 9531592Srgrimes ; 9541592Srgrimes 9551592Srgrimespathname 9561592Srgrimes : pathstring 9571592Srgrimes { 95875567Speter if (logged_in && $1) { 959110340Syar char *p; 9601592Srgrimes 961110340Syar /* 962110340Syar * Expand ~user manually since glob(3) 963110340Syar * will return the unexpanded pathname 964110340Syar * if the corresponding file/directory 965110340Syar * doesn't exist yet. Using sole glob(3) 966110340Syar * would break natural commands like 967110340Syar * MKD ~user/newdir 968110340Syar * or 969110340Syar * RNTO ~/newfile 970110340Syar */ 971110340Syar if ((p = exptilde($1)) != NULL) { 972110340Syar $$ = expglob(p); 973110340Syar free(p); 974110340Syar } else 9751592Srgrimes $$ = NULL; 9761592Srgrimes free($1); 9771592Srgrimes } else 9781592Srgrimes $$ = $1; 9791592Srgrimes } 9801592Srgrimes ; 9811592Srgrimes 9821592Srgrimespathstring 9831592Srgrimes : STRING 9841592Srgrimes ; 9851592Srgrimes 9861592Srgrimesoctal_number 9871592Srgrimes : NUMBER 9881592Srgrimes { 9891592Srgrimes int ret, dec, multby, digit; 9901592Srgrimes 9911592Srgrimes /* 9921592Srgrimes * Convert a number that was read as decimal number 9931592Srgrimes * to what it would be if it had been read as octal. 9941592Srgrimes */ 99592272Smaxim dec = $1.i; 9961592Srgrimes multby = 1; 9971592Srgrimes ret = 0; 9981592Srgrimes while (dec) { 9991592Srgrimes digit = dec%10; 10001592Srgrimes if (digit > 7) { 10011592Srgrimes ret = -1; 10021592Srgrimes break; 10031592Srgrimes } 10041592Srgrimes ret += digit * multby; 10051592Srgrimes multby *= 8; 10061592Srgrimes dec /= 10; 10071592Srgrimes } 10081592Srgrimes $$ = ret; 10091592Srgrimes } 10101592Srgrimes ; 10111592Srgrimes 10121592Srgrimes 10131592Srgrimescheck_login 10141592Srgrimes : /* empty */ 10151592Srgrimes { 101670102Sphk $$ = check_login1(); 10171592Srgrimes } 10181592Srgrimes ; 10191592Srgrimes 102070102Sphkcheck_login_epsv 102170102Sphk : /* empty */ 102270102Sphk { 102370102Sphk if (noepsv) { 1024137852Syar reply(500, "EPSV command disabled."); 102570102Sphk $$ = 0; 102670102Sphk } 102770102Sphk else 102870102Sphk $$ = check_login1(); 102970102Sphk } 103070102Sphk ; 103170102Sphk 103270102Sphkcheck_login_ro 103370102Sphk : /* empty */ 103470102Sphk { 103570102Sphk if (readonly) { 103672710Sdes reply(550, "Permission denied."); 103770102Sphk $$ = 0; 103870102Sphk } 103970102Sphk else 104070102Sphk $$ = check_login1(); 104170102Sphk } 104270102Sphk ; 104370102Sphk 10441592Srgrimes%% 10451592Srgrimes 10461592Srgrimes#define CMD 0 /* beginning of command */ 10471592Srgrimes#define ARGS 1 /* expect miscellaneous arguments */ 10481592Srgrimes#define STR1 2 /* expect SP followed by STRING */ 10491592Srgrimes#define STR2 3 /* expect STRING */ 10501592Srgrimes#define OSTR 4 /* optional SP then STRING */ 105175556Sgreen#define ZSTR1 5 /* optional SP then optional STRING */ 10521592Srgrimes#define ZSTR2 6 /* optional STRING after SP */ 10531592Srgrimes#define SITECMD 7 /* SITE command */ 10541592Srgrimes#define NSTR 8 /* Number followed by a string */ 10551592Srgrimes 105675560Sjedgar#define MAXGLOBARGS 1000 105775560Sjedgar 1058101034Syar#define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */ 1059101034Syar 10601592Srgrimesstruct tab { 10611592Srgrimes char *name; 10621592Srgrimes short token; 10631592Srgrimes short state; 10641592Srgrimes short implemented; /* 1 if command is implemented */ 10651592Srgrimes char *help; 10661592Srgrimes}; 10671592Srgrimes 10681592Srgrimesstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 10691592Srgrimes { "USER", USER, STR1, 1, "<sp> username" }, 107075556Sgreen { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" }, 10711592Srgrimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 10721592Srgrimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 10731592Srgrimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 10741592Srgrimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 1075101806Syar { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" }, 107656668Sshin { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 107756668Sshin { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 10781592Srgrimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 107956668Sshin { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 108056668Sshin { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 1081101806Syar { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" }, 10821592Srgrimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 10831592Srgrimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 10841592Srgrimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 10851592Srgrimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 10861592Srgrimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 10871592Srgrimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 10881592Srgrimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 10891592Srgrimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 10901592Srgrimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 10911592Srgrimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 10921592Srgrimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 10931592Srgrimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 10941592Srgrimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 10951592Srgrimes { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 10961592Srgrimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 10971592Srgrimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 10981592Srgrimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 10991592Srgrimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 11001592Srgrimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 11011592Srgrimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 11021592Srgrimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 11031592Srgrimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 11041592Srgrimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 11051592Srgrimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 1106168849Syar { "FEAT", FEAT, ARGS, 1, "(get extended features)" }, 11071592Srgrimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 11081592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11091592Srgrimes { "NOOP", NOOP, ARGS, 1, "" }, 11101592Srgrimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 11111592Srgrimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 11121592Srgrimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 11131592Srgrimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 11141592Srgrimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 11151592Srgrimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 11161592Srgrimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11171592Srgrimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11181592Srgrimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 11191592Srgrimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 11201592Srgrimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 11211592Srgrimes { NULL, 0, 0, 0, 0 } 11221592Srgrimes}; 11231592Srgrimes 11241592Srgrimesstruct tab sitetab[] = { 112575535Sphk { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" }, 11261592Srgrimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 11271592Srgrimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 11281592Srgrimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 11291592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11301592Srgrimes { NULL, 0, 0, 0, 0 } 11311592Srgrimes}; 11321592Srgrimes 113390148Simpstatic char *copy(char *); 1134110340Syarstatic char *expglob(char *); 1135110340Syarstatic char *exptilde(char *); 113690148Simpstatic void help(struct tab *, char *); 11371592Srgrimesstatic struct tab * 113890148Simp lookup(struct tab *, char *); 113990148Simpstatic int port_check(const char *); 1140159276Syar#ifdef INET6 114190148Simpstatic int port_check_v6(const char *); 1142159276Syar#endif 114390148Simpstatic void sizecmd(char *); 114490148Simpstatic void toolong(int); 1145159276Syar#ifdef INET6 114690148Simpstatic void v4map_data_dest(void); 1147159276Syar#endif 114890148Simpstatic int yylex(void); 11491592Srgrimes 11501592Srgrimesstatic struct tab * 115190148Simplookup(struct tab *p, char *cmd) 11521592Srgrimes{ 11531592Srgrimes 11541592Srgrimes for (; p->name != NULL; p++) 11551592Srgrimes if (strcmp(cmd, p->name) == 0) 11561592Srgrimes return (p); 11571592Srgrimes return (0); 11581592Srgrimes} 11591592Srgrimes 11601592Srgrimes#include <arpa/telnet.h> 11611592Srgrimes 11621592Srgrimes/* 1163299356Sbapt * get_line - a hacked up version of fgets to ignore TELNET escape codes. 11641592Srgrimes */ 1165186405Scpercivaint 1166299356Sbaptget_line(char *s, int n, FILE *iop) 11671592Srgrimes{ 11681592Srgrimes int c; 11691592Srgrimes register char *cs; 1170117352Syar sigset_t sset, osset; 11711592Srgrimes 11721592Srgrimes cs = s; 11731592Srgrimes/* tmpline may contain saved command from urgent mode interruption */ 11741592Srgrimes for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 11751592Srgrimes *cs++ = tmpline[c]; 11761592Srgrimes if (tmpline[c] == '\n') { 11771592Srgrimes *cs++ = '\0'; 117876096Smarkm if (ftpdebug) 11791592Srgrimes syslog(LOG_DEBUG, "command: %s", s); 11801592Srgrimes tmpline[0] = '\0'; 1181186405Scperciva return(0); 11821592Srgrimes } 11831592Srgrimes if (c == 0) 11841592Srgrimes tmpline[0] = '\0'; 11851592Srgrimes } 1186117352Syar /* SIGURG would interrupt stdio if not blocked during the read loop */ 1187117352Syar sigemptyset(&sset); 1188117352Syar sigaddset(&sset, SIGURG); 1189117352Syar sigprocmask(SIG_BLOCK, &sset, &osset); 11901592Srgrimes while ((c = getc(iop)) != EOF) { 11911592Srgrimes c &= 0377; 11921592Srgrimes if (c == IAC) { 1193117351Syar if ((c = getc(iop)) == EOF) 1194117351Syar goto got_eof; 11951592Srgrimes c &= 0377; 11961592Srgrimes switch (c) { 11971592Srgrimes case WILL: 11981592Srgrimes case WONT: 1199117351Syar if ((c = getc(iop)) == EOF) 1200117351Syar goto got_eof; 12011592Srgrimes printf("%c%c%c", IAC, DONT, 0377&c); 12021592Srgrimes (void) fflush(stdout); 12031592Srgrimes continue; 12041592Srgrimes case DO: 12051592Srgrimes case DONT: 1206117351Syar if ((c = getc(iop)) == EOF) 1207117351Syar goto got_eof; 12081592Srgrimes printf("%c%c%c", IAC, WONT, 0377&c); 12091592Srgrimes (void) fflush(stdout); 12101592Srgrimes continue; 12111592Srgrimes case IAC: 12121592Srgrimes break; 12131592Srgrimes default: 12141592Srgrimes continue; /* ignore command */ 12151592Srgrimes } 12161592Srgrimes } 12171592Srgrimes *cs++ = c; 1218186405Scperciva if (--n <= 0) { 1219186405Scperciva /* 1220186405Scperciva * If command doesn't fit into buffer, discard the 1221186405Scperciva * rest of the command and indicate truncation. 1222186405Scperciva * This prevents the command to be split up into 1223186405Scperciva * multiple commands. 1224186405Scperciva */ 1225186405Scperciva while (c != '\n' && (c = getc(iop)) != EOF) 1226186405Scperciva ; 1227186405Scperciva return (-2); 1228186405Scperciva } 1229186405Scperciva if (c == '\n') 12301592Srgrimes break; 12311592Srgrimes } 1232117351Syargot_eof: 1233117352Syar sigprocmask(SIG_SETMASK, &osset, NULL); 12341592Srgrimes if (c == EOF && cs == s) 1235186405Scperciva return (-1); 12361592Srgrimes *cs++ = '\0'; 123776096Smarkm if (ftpdebug) { 12381592Srgrimes if (!guest && strncasecmp("pass ", s, 5) == 0) { 12391592Srgrimes /* Don't syslog passwords */ 12401592Srgrimes syslog(LOG_DEBUG, "command: %.5s ???", s); 12411592Srgrimes } else { 12421592Srgrimes register char *cp; 12431592Srgrimes register int len; 12441592Srgrimes 12451592Srgrimes /* Don't syslog trailing CR-LF */ 12461592Srgrimes len = strlen(s); 12471592Srgrimes cp = s + len - 1; 12481592Srgrimes while (cp >= s && (*cp == '\n' || *cp == '\r')) { 12491592Srgrimes --cp; 12501592Srgrimes --len; 12511592Srgrimes } 12521592Srgrimes syslog(LOG_DEBUG, "command: %.*s", len, s); 12531592Srgrimes } 12541592Srgrimes } 1255186405Scperciva return (0); 12561592Srgrimes} 12571592Srgrimes 12581592Srgrimesstatic void 125990148Simptoolong(int signo) 12601592Srgrimes{ 12611592Srgrimes 12621592Srgrimes reply(421, 12631592Srgrimes "Timeout (%d seconds): closing control connection.", timeout); 12641592Srgrimes if (logging) 12651592Srgrimes syslog(LOG_INFO, "User %s timed out after %d seconds", 12661592Srgrimes (pw ? pw -> pw_name : "unknown"), timeout); 12671592Srgrimes dologout(1); 12681592Srgrimes} 12691592Srgrimes 12701592Srgrimesstatic int 127190148Simpyylex(void) 12721592Srgrimes{ 127389935Syar static int cpos; 12741592Srgrimes char *cp, *cp2; 12751592Srgrimes struct tab *p; 12761592Srgrimes int n; 12771592Srgrimes char c; 12781592Srgrimes 12791592Srgrimes for (;;) { 12801592Srgrimes switch (state) { 12811592Srgrimes 12821592Srgrimes case CMD: 12831592Srgrimes (void) signal(SIGALRM, toolong); 1284137659Syar (void) alarm(timeout); 1285299356Sbapt n = get_line(cbuf, sizeof(cbuf)-1, stdin); 1286186405Scperciva if (n == -1) { 12871592Srgrimes reply(221, "You could at least say goodbye."); 12881592Srgrimes dologout(0); 1289186405Scperciva } else if (n == -2) { 1290186405Scperciva reply(500, "Command too long."); 1291186405Scperciva (void) alarm(0); 1292186405Scperciva continue; 12931592Srgrimes } 12941592Srgrimes (void) alarm(0); 12951592Srgrimes#ifdef SETPROCTITLE 129629574Sphk if (strncasecmp(cbuf, "PASS", 4) != 0) 12971592Srgrimes setproctitle("%s: %s", proctitle, cbuf); 12981592Srgrimes#endif /* SETPROCTITLE */ 12991592Srgrimes if ((cp = strchr(cbuf, '\r'))) { 13001592Srgrimes *cp++ = '\n'; 13011592Srgrimes *cp = '\0'; 13021592Srgrimes } 13031592Srgrimes if ((cp = strpbrk(cbuf, " \n"))) 13041592Srgrimes cpos = cp - cbuf; 13051592Srgrimes if (cpos == 0) 13061592Srgrimes cpos = 4; 13071592Srgrimes c = cbuf[cpos]; 13081592Srgrimes cbuf[cpos] = '\0'; 13091592Srgrimes upper(cbuf); 13101592Srgrimes p = lookup(cmdtab, cbuf); 13111592Srgrimes cbuf[cpos] = c; 13123776Spst if (p != 0) { 1313102565Syar yylval.s = p->name; 1314102565Syar if (!p->implemented) 1315102565Syar return (NOTIMPL); /* state remains CMD */ 13161592Srgrimes state = p->state; 13171592Srgrimes return (p->token); 13181592Srgrimes } 13191592Srgrimes break; 13201592Srgrimes 13211592Srgrimes case SITECMD: 13221592Srgrimes if (cbuf[cpos] == ' ') { 13231592Srgrimes cpos++; 13241592Srgrimes return (SP); 13251592Srgrimes } 13261592Srgrimes cp = &cbuf[cpos]; 13271592Srgrimes if ((cp2 = strpbrk(cp, " \n"))) 13281592Srgrimes cpos = cp2 - cbuf; 13291592Srgrimes c = cbuf[cpos]; 13301592Srgrimes cbuf[cpos] = '\0'; 13311592Srgrimes upper(cp); 13321592Srgrimes p = lookup(sitetab, cp); 13331592Srgrimes cbuf[cpos] = c; 13343777Spst if (guest == 0 && p != 0) { 1335102565Syar yylval.s = p->name; 1336102565Syar if (!p->implemented) { 13371592Srgrimes state = CMD; 1338102565Syar return (NOTIMPL); 13391592Srgrimes } 13401592Srgrimes state = p->state; 13411592Srgrimes return (p->token); 13421592Srgrimes } 13431592Srgrimes state = CMD; 13441592Srgrimes break; 13451592Srgrimes 134675556Sgreen case ZSTR1: 13471592Srgrimes case OSTR: 13481592Srgrimes if (cbuf[cpos] == '\n') { 13491592Srgrimes state = CMD; 13501592Srgrimes return (CRLF); 13511592Srgrimes } 13521592Srgrimes /* FALLTHROUGH */ 13531592Srgrimes 13541592Srgrimes case STR1: 13551592Srgrimes dostr1: 13561592Srgrimes if (cbuf[cpos] == ' ') { 13571592Srgrimes cpos++; 135851979Salfred state = state == OSTR ? STR2 : state+1; 13591592Srgrimes return (SP); 13601592Srgrimes } 13611592Srgrimes break; 13621592Srgrimes 13631592Srgrimes case ZSTR2: 13641592Srgrimes if (cbuf[cpos] == '\n') { 13651592Srgrimes state = CMD; 13661592Srgrimes return (CRLF); 13671592Srgrimes } 13681592Srgrimes /* FALLTHROUGH */ 13691592Srgrimes 13701592Srgrimes case STR2: 13711592Srgrimes cp = &cbuf[cpos]; 13721592Srgrimes n = strlen(cp); 13731592Srgrimes cpos += n - 1; 13741592Srgrimes /* 13751592Srgrimes * Make sure the string is nonempty and \n terminated. 13761592Srgrimes */ 13771592Srgrimes if (n > 1 && cbuf[cpos] == '\n') { 13781592Srgrimes cbuf[cpos] = '\0'; 13791592Srgrimes yylval.s = copy(cp); 13801592Srgrimes cbuf[cpos] = '\n'; 13811592Srgrimes state = ARGS; 13821592Srgrimes return (STRING); 13831592Srgrimes } 13841592Srgrimes break; 13851592Srgrimes 13861592Srgrimes case NSTR: 13871592Srgrimes if (cbuf[cpos] == ' ') { 13881592Srgrimes cpos++; 13891592Srgrimes return (SP); 13901592Srgrimes } 13911592Srgrimes if (isdigit(cbuf[cpos])) { 13921592Srgrimes cp = &cbuf[cpos]; 13931592Srgrimes while (isdigit(cbuf[++cpos])) 13941592Srgrimes ; 13951592Srgrimes c = cbuf[cpos]; 13961592Srgrimes cbuf[cpos] = '\0'; 139792272Smaxim yylval.u.i = atoi(cp); 13981592Srgrimes cbuf[cpos] = c; 13991592Srgrimes state = STR1; 14001592Srgrimes return (NUMBER); 14011592Srgrimes } 14021592Srgrimes state = STR1; 14031592Srgrimes goto dostr1; 14041592Srgrimes 14051592Srgrimes case ARGS: 14061592Srgrimes if (isdigit(cbuf[cpos])) { 14071592Srgrimes cp = &cbuf[cpos]; 14081592Srgrimes while (isdigit(cbuf[++cpos])) 14091592Srgrimes ; 14101592Srgrimes c = cbuf[cpos]; 14111592Srgrimes cbuf[cpos] = '\0'; 141292272Smaxim yylval.u.i = atoi(cp); 1413137811Syar yylval.u.o = strtoull(cp, NULL, 10); 14141592Srgrimes cbuf[cpos] = c; 14151592Srgrimes return (NUMBER); 14161592Srgrimes } 141756668Sshin if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 141856668Sshin && !isalnum(cbuf[cpos + 3])) { 141956668Sshin cpos += 3; 142056668Sshin return ALL; 142156668Sshin } 14221592Srgrimes switch (cbuf[cpos++]) { 14231592Srgrimes 14241592Srgrimes case '\n': 14251592Srgrimes state = CMD; 14261592Srgrimes return (CRLF); 14271592Srgrimes 14281592Srgrimes case ' ': 14291592Srgrimes return (SP); 14301592Srgrimes 14311592Srgrimes case ',': 14321592Srgrimes return (COMMA); 14331592Srgrimes 14341592Srgrimes case 'A': 14351592Srgrimes case 'a': 14361592Srgrimes return (A); 14371592Srgrimes 14381592Srgrimes case 'B': 14391592Srgrimes case 'b': 14401592Srgrimes return (B); 14411592Srgrimes 14421592Srgrimes case 'C': 14431592Srgrimes case 'c': 14441592Srgrimes return (C); 14451592Srgrimes 14461592Srgrimes case 'E': 14471592Srgrimes case 'e': 14481592Srgrimes return (E); 14491592Srgrimes 14501592Srgrimes case 'F': 14511592Srgrimes case 'f': 14521592Srgrimes return (F); 14531592Srgrimes 14541592Srgrimes case 'I': 14551592Srgrimes case 'i': 14561592Srgrimes return (I); 14571592Srgrimes 14581592Srgrimes case 'L': 14591592Srgrimes case 'l': 14601592Srgrimes return (L); 14611592Srgrimes 14621592Srgrimes case 'N': 14631592Srgrimes case 'n': 14641592Srgrimes return (N); 14651592Srgrimes 14661592Srgrimes case 'P': 14671592Srgrimes case 'p': 14681592Srgrimes return (P); 14691592Srgrimes 14701592Srgrimes case 'R': 14711592Srgrimes case 'r': 14721592Srgrimes return (R); 14731592Srgrimes 14741592Srgrimes case 'S': 14751592Srgrimes case 's': 14761592Srgrimes return (S); 14771592Srgrimes 14781592Srgrimes case 'T': 14791592Srgrimes case 't': 14801592Srgrimes return (T); 14811592Srgrimes 14821592Srgrimes } 14831592Srgrimes break; 14841592Srgrimes 14851592Srgrimes default: 148676096Smarkm fatalerror("Unknown state in scanner."); 14871592Srgrimes } 14881592Srgrimes state = CMD; 148989935Syar return (LEXERR); 14901592Srgrimes } 14911592Srgrimes} 14921592Srgrimes 14931592Srgrimesvoid 149490148Simpupper(char *s) 14951592Srgrimes{ 14961592Srgrimes while (*s != '\0') { 14971592Srgrimes if (islower(*s)) 14981592Srgrimes *s = toupper(*s); 14991592Srgrimes s++; 15001592Srgrimes } 15011592Srgrimes} 15021592Srgrimes 15031592Srgrimesstatic char * 150490148Simpcopy(char *s) 15051592Srgrimes{ 15061592Srgrimes char *p; 15071592Srgrimes 1508137659Syar p = malloc(strlen(s) + 1); 15091592Srgrimes if (p == NULL) 151076096Smarkm fatalerror("Ran out of memory."); 15111592Srgrimes (void) strcpy(p, s); 15121592Srgrimes return (p); 15131592Srgrimes} 15141592Srgrimes 15151592Srgrimesstatic void 151690148Simphelp(struct tab *ctab, char *s) 15171592Srgrimes{ 15181592Srgrimes struct tab *c; 15191592Srgrimes int width, NCMDS; 15201592Srgrimes char *type; 15211592Srgrimes 15221592Srgrimes if (ctab == sitetab) 15231592Srgrimes type = "SITE "; 15241592Srgrimes else 15251592Srgrimes type = ""; 15261592Srgrimes width = 0, NCMDS = 0; 15271592Srgrimes for (c = ctab; c->name != NULL; c++) { 15281592Srgrimes int len = strlen(c->name); 15291592Srgrimes 15301592Srgrimes if (len > width) 15311592Srgrimes width = len; 15321592Srgrimes NCMDS++; 15331592Srgrimes } 15341592Srgrimes width = (width + 8) &~ 7; 15351592Srgrimes if (s == 0) { 15361592Srgrimes int i, j, w; 15371592Srgrimes int columns, lines; 15381592Srgrimes 15391592Srgrimes lreply(214, "The following %scommands are recognized %s.", 15401592Srgrimes type, "(* =>'s unimplemented)"); 15411592Srgrimes columns = 76 / width; 15421592Srgrimes if (columns == 0) 15431592Srgrimes columns = 1; 15441592Srgrimes lines = (NCMDS + columns - 1) / columns; 15451592Srgrimes for (i = 0; i < lines; i++) { 15461592Srgrimes printf(" "); 15471592Srgrimes for (j = 0; j < columns; j++) { 15481592Srgrimes c = ctab + j * lines + i; 15491592Srgrimes printf("%s%c", c->name, 15501592Srgrimes c->implemented ? ' ' : '*'); 15511592Srgrimes if (c + lines >= &ctab[NCMDS]) 15521592Srgrimes break; 15531592Srgrimes w = strlen(c->name) + 1; 15541592Srgrimes while (w < width) { 15551592Srgrimes putchar(' '); 15561592Srgrimes w++; 15571592Srgrimes } 15581592Srgrimes } 15591592Srgrimes printf("\r\n"); 15601592Srgrimes } 15611592Srgrimes (void) fflush(stdout); 1562110037Syar if (hostinfo) 1563110037Syar reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1564110037Syar else 1565110037Syar reply(214, "End."); 15661592Srgrimes return; 15671592Srgrimes } 15681592Srgrimes upper(s); 15691592Srgrimes c = lookup(ctab, s); 1570132931Syar if (c == NULL) { 15711592Srgrimes reply(502, "Unknown command %s.", s); 15721592Srgrimes return; 15731592Srgrimes } 15741592Srgrimes if (c->implemented) 15751592Srgrimes reply(214, "Syntax: %s%s %s", type, c->name, c->help); 15761592Srgrimes else 15771592Srgrimes reply(214, "%s%-*s\t%s; unimplemented.", type, width, 15781592Srgrimes c->name, c->help); 15791592Srgrimes} 15801592Srgrimes 15811592Srgrimesstatic void 158290148Simpsizecmd(char *filename) 15831592Srgrimes{ 15841592Srgrimes switch (type) { 15851592Srgrimes case TYPE_L: 15861592Srgrimes case TYPE_I: { 15871592Srgrimes struct stat stbuf; 158863350Sdes if (stat(filename, &stbuf) < 0) 158963350Sdes perror_reply(550, filename); 159063350Sdes else if (!S_ISREG(stbuf.st_mode)) 15911592Srgrimes reply(550, "%s: not a plain file.", filename); 15921592Srgrimes else 1593132929Syar reply(213, "%jd", (intmax_t)stbuf.st_size); 15941592Srgrimes break; } 15951592Srgrimes case TYPE_A: { 15961592Srgrimes FILE *fin; 15971592Srgrimes int c; 15981592Srgrimes off_t count; 15991592Srgrimes struct stat stbuf; 16001592Srgrimes fin = fopen(filename, "r"); 16011592Srgrimes if (fin == NULL) { 16021592Srgrimes perror_reply(550, filename); 16031592Srgrimes return; 16041592Srgrimes } 160563350Sdes if (fstat(fileno(fin), &stbuf) < 0) { 160663350Sdes perror_reply(550, filename); 160763350Sdes (void) fclose(fin); 160863350Sdes return; 160963350Sdes } else if (!S_ISREG(stbuf.st_mode)) { 16101592Srgrimes reply(550, "%s: not a plain file.", filename); 16111592Srgrimes (void) fclose(fin); 16121592Srgrimes return; 1613101034Syar } else if (stbuf.st_size > MAXASIZE) { 1614101034Syar reply(550, "%s: too large for type A SIZE.", filename); 1615101034Syar (void) fclose(fin); 1616101034Syar return; 16171592Srgrimes } 16181592Srgrimes 16191592Srgrimes count = 0; 16201592Srgrimes while((c=getc(fin)) != EOF) { 16211592Srgrimes if (c == '\n') /* will get expanded to \r\n */ 16221592Srgrimes count++; 16231592Srgrimes count++; 16241592Srgrimes } 16251592Srgrimes (void) fclose(fin); 16261592Srgrimes 1627132929Syar reply(213, "%jd", (intmax_t)count); 16281592Srgrimes break; } 16291592Srgrimes default: 1630100684Syar reply(504, "SIZE not implemented for type %s.", 1631100684Syar typenames[type]); 16321592Srgrimes } 16331592Srgrimes} 163456668Sshin 163556668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 163656668Sshinstatic int 163790148Simpport_check(const char *pcmd) 163856668Sshin{ 163956668Sshin if (his_addr.su_family == AF_INET) { 164056668Sshin if (data_dest.su_family != AF_INET) { 164156668Sshin usedefault = 1; 164256668Sshin reply(500, "Invalid address rejected."); 164356668Sshin return 1; 164456668Sshin } 164556668Sshin if (paranoid && 164656668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 164756668Sshin memcmp(&data_dest.su_sin.sin_addr, 164856668Sshin &his_addr.su_sin.sin_addr, 164956668Sshin sizeof(data_dest.su_sin.sin_addr)))) { 165056668Sshin usedefault = 1; 165156668Sshin reply(500, "Illegal PORT range rejected."); 165256668Sshin } else { 165356668Sshin usedefault = 0; 165456668Sshin if (pdata >= 0) { 165556668Sshin (void) close(pdata); 165656668Sshin pdata = -1; 165756668Sshin } 165856668Sshin reply(200, "%s command successful.", pcmd); 165956668Sshin } 166056668Sshin return 1; 166156668Sshin } 166256668Sshin return 0; 166356668Sshin} 166456668Sshin 166570102Sphkstatic int 166690148Simpcheck_login1(void) 166770102Sphk{ 166870102Sphk if (logged_in) 166970102Sphk return 1; 167070102Sphk else { 167170102Sphk reply(530, "Please login with USER and PASS."); 167270102Sphk return 0; 167370102Sphk } 167470102Sphk} 167570102Sphk 1676110340Syar/* 1677110340Syar * Replace leading "~user" in a pathname by the user's login directory. 1678110340Syar * Returned string will be in a freshly malloced buffer unless it's NULL. 1679110340Syar */ 1680110340Syarstatic char * 1681110340Syarexptilde(char *s) 1682110340Syar{ 1683110340Syar char *p, *q; 1684110340Syar char *path, *user; 1685110340Syar struct passwd *ppw; 1686110340Syar 1687110340Syar if ((p = strdup(s)) == NULL) 1688110340Syar return (NULL); 1689110340Syar if (*p != '~') 1690110340Syar return (p); 1691110340Syar 1692110340Syar user = p + 1; /* skip tilde */ 1693110340Syar if ((path = strchr(p, '/')) != NULL) 1694110340Syar *(path++) = '\0'; /* separate ~user from the rest of path */ 1695110378Syar if (*user == '\0') /* no user specified, use the current user */ 1696110378Syar user = pw->pw_name; 1697110378Syar /* read passwd even for the current user since we may be chrooted */ 1698110378Syar if ((ppw = getpwnam(user)) != NULL) { 1699110340Syar /* user found, substitute login directory for ~user */ 1700110340Syar if (path) 1701110340Syar asprintf(&q, "%s/%s", ppw->pw_dir, path); 1702110340Syar else 1703110340Syar q = strdup(ppw->pw_dir); 1704110340Syar free(p); 1705110340Syar p = q; 1706110340Syar } else { 1707110340Syar /* user not found, undo the damage */ 1708110340Syar if (path) 1709110340Syar path[-1] = '/'; 1710110340Syar } 1711110340Syar return (p); 1712110340Syar} 1713110340Syar 1714110340Syar/* 1715110340Syar * Expand glob(3) patterns possibly present in a pathname. 1716110340Syar * Avoid expanding to a pathname including '\r' or '\n' in order to 1717110340Syar * not disrupt the FTP protocol. 1718110340Syar * The expansion found must be unique. 1719229780Suqs * Return the result as a malloced string, or NULL if an error occurred. 1720110340Syar * 1721110340Syar * Problem: this production is used for all pathname 1722110340Syar * processing, but only gives a 550 error reply. 1723110340Syar * This is a valid reply in some cases but not in others. 1724110340Syar */ 1725110340Syarstatic char * 1726110340Syarexpglob(char *s) 1727110340Syar{ 1728110340Syar char *p, **pp, *rval; 1729110340Syar int flags = GLOB_BRACE | GLOB_NOCHECK; 1730110340Syar int n; 1731110340Syar glob_t gl; 1732110340Syar 1733110340Syar memset(&gl, 0, sizeof(gl)); 1734110340Syar flags |= GLOB_LIMIT; 1735110340Syar gl.gl_matchc = MAXGLOBARGS; 1736110340Syar if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) { 1737110340Syar for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++) 1738110340Syar if (*(*pp + strcspn(*pp, "\r\n")) == '\0') { 1739110340Syar p = *pp; 1740110340Syar n++; 1741110340Syar } 1742110340Syar if (n == 0) 1743110340Syar rval = strdup(s); 1744110340Syar else if (n == 1) 1745110340Syar rval = strdup(p); 1746110340Syar else { 1747137852Syar reply(550, "Wildcard is ambiguous."); 1748110340Syar rval = NULL; 1749110340Syar } 1750110340Syar } else { 1751137852Syar reply(550, "Wildcard expansion error."); 1752110340Syar rval = NULL; 1753110340Syar } 1754110340Syar globfree(&gl); 1755110340Syar return (rval); 1756110340Syar} 1757110340Syar 175856668Sshin#ifdef INET6 175956668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 176056668Sshinstatic int 176190148Simpport_check_v6(const char *pcmd) 176256668Sshin{ 176356668Sshin if (his_addr.su_family == AF_INET6) { 176456668Sshin if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 176556668Sshin /* Convert data_dest into v4 mapped sockaddr.*/ 176656668Sshin v4map_data_dest(); 176756668Sshin if (data_dest.su_family != AF_INET6) { 176856668Sshin usedefault = 1; 176956668Sshin reply(500, "Invalid address rejected."); 177056668Sshin return 1; 177156668Sshin } 177256668Sshin if (paranoid && 177356668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 177456668Sshin memcmp(&data_dest.su_sin6.sin6_addr, 177556668Sshin &his_addr.su_sin6.sin6_addr, 177656668Sshin sizeof(data_dest.su_sin6.sin6_addr)))) { 177756668Sshin usedefault = 1; 177856668Sshin reply(500, "Illegal PORT range rejected."); 177956668Sshin } else { 178056668Sshin usedefault = 0; 178156668Sshin if (pdata >= 0) { 178256668Sshin (void) close(pdata); 178356668Sshin pdata = -1; 178456668Sshin } 178556668Sshin reply(200, "%s command successful.", pcmd); 178656668Sshin } 178756668Sshin return 1; 178856668Sshin } 178956668Sshin return 0; 179056668Sshin} 179156668Sshin 179256668Sshinstatic void 179390148Simpv4map_data_dest(void) 179456668Sshin{ 179556668Sshin struct in_addr savedaddr; 179656668Sshin int savedport; 179756668Sshin 179856668Sshin if (data_dest.su_family != AF_INET) { 179956668Sshin usedefault = 1; 180056668Sshin reply(500, "Invalid address rejected."); 180156668Sshin return; 180256668Sshin } 180356668Sshin 180456668Sshin savedaddr = data_dest.su_sin.sin_addr; 180556668Sshin savedport = data_dest.su_port; 180656668Sshin 180756668Sshin memset(&data_dest, 0, sizeof(data_dest)); 180856668Sshin data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 180956668Sshin data_dest.su_sin6.sin6_family = AF_INET6; 181056668Sshin data_dest.su_sin6.sin6_port = savedport; 181156668Sshin memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 181256668Sshin memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 181356668Sshin (caddr_t)&savedaddr, sizeof(savedaddr)); 181456668Sshin} 181556668Sshin#endif 1816