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. 13262435Sbrueffer * 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$"); 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 751592Srgrimesoff_t restart_point; 761592Srgrimes 771592Srgrimesstatic int cmd_type; 781592Srgrimesstatic int cmd_form; 791592Srgrimesstatic int cmd_bytesz; 8089935Syarstatic int state; 811592Srgrimeschar cbuf[512]; 82132931Syarchar *fromname = NULL; 831592Srgrimes 841592Srgrimes%} 851592Srgrimes 861592Srgrimes%union { 8792272Smaxim struct { 8892272Smaxim off_t o; 8992272Smaxim int i; 9092272Smaxim } u; 911592Srgrimes char *s; 921592Srgrimes} 931592Srgrimes 941592Srgrimes%token 951592Srgrimes A B C E F I 961592Srgrimes L N P R S T 9756668Sshin ALL 981592Srgrimes 991592Srgrimes SP CRLF COMMA 1001592Srgrimes 1011592Srgrimes USER PASS ACCT REIN QUIT PORT 1021592Srgrimes PASV TYPE STRU MODE RETR STOR 1031592Srgrimes APPE MLFL MAIL MSND MSOM MSAM 1041592Srgrimes MRSQ MRCP ALLO REST RNFR RNTO 1051592Srgrimes ABOR DELE CWD LIST NLST SITE 1061592Srgrimes STAT HELP NOOP MKD RMD PWD 1071592Srgrimes CDUP STOU SMNT SYST SIZE MDTM 108168849Syar LPRT LPSV EPRT EPSV FEAT 1091592Srgrimes 11075535Sphk UMASK IDLE CHMOD MDFIVE 1111592Srgrimes 112102565Syar LEXERR NOTIMPL 1131592Srgrimes 1141592Srgrimes%token <s> STRING 11592272Smaxim%token <u> NUMBER 1161592Srgrimes 11792272Smaxim%type <u.i> check_login octal_number byte_size 11892272Smaxim%type <u.i> check_login_ro check_login_epsv 11992272Smaxim%type <u.i> struct_code mode_code type_code form_code 12075567Speter%type <s> pathstring pathname password username 121102565Syar%type <s> ALL NOTIMPL 1221592Srgrimes 1231592Srgrimes%start cmd_list 1241592Srgrimes 1251592Srgrimes%% 1261592Srgrimes 1271592Srgrimescmd_list 1281592Srgrimes : /* empty */ 1291592Srgrimes | cmd_list cmd 1301592Srgrimes { 13188935Sdwmalone if (fromname) 13288935Sdwmalone free(fromname); 133132931Syar fromname = NULL; 134132930Syar restart_point = 0; 1351592Srgrimes } 1361592Srgrimes | cmd_list rcmd 1371592Srgrimes ; 1381592Srgrimes 1391592Srgrimescmd 1401592Srgrimes : USER SP username CRLF 1411592Srgrimes { 1421592Srgrimes user($3); 1431592Srgrimes free($3); 1441592Srgrimes } 1451592Srgrimes | PASS SP password CRLF 1461592Srgrimes { 1471592Srgrimes pass($3); 1481592Srgrimes free($3); 1491592Srgrimes } 15075556Sgreen | PASS CRLF 15175556Sgreen { 15275556Sgreen pass(""); 15375556Sgreen } 15417433Spst | PORT check_login SP host_port CRLF 1551592Srgrimes { 15656668Sshin if (epsvall) { 157137852Syar reply(501, "No PORT allowed after EPSV ALL."); 15856668Sshin goto port_done; 15956668Sshin } 16056668Sshin if (!$2) 16156668Sshin goto port_done; 16256668Sshin if (port_check("PORT") == 1) 16356668Sshin goto port_done; 16456668Sshin#ifdef INET6 16556668Sshin if ((his_addr.su_family != AF_INET6 || 16656668Sshin !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { 16756668Sshin /* shoud never happen */ 16856668Sshin usedefault = 1; 16956668Sshin reply(500, "Invalid address rejected."); 17056668Sshin goto port_done; 17156668Sshin } 17256668Sshin port_check_v6("pcmd"); 17356668Sshin#endif 17456668Sshin port_done: 175132925Syar ; 17656668Sshin } 17756668Sshin | LPRT check_login SP host_long_port CRLF 17856668Sshin { 17956668Sshin if (epsvall) { 180137852Syar reply(501, "No LPRT allowed after EPSV ALL."); 18156668Sshin goto lprt_done; 18256668Sshin } 18356668Sshin if (!$2) 18456668Sshin goto lprt_done; 18556668Sshin if (port_check("LPRT") == 1) 18656668Sshin goto lprt_done; 18756668Sshin#ifdef INET6 18856668Sshin if (his_addr.su_family != AF_INET6) { 18956668Sshin usedefault = 1; 19056668Sshin reply(500, "Invalid address rejected."); 19156668Sshin goto lprt_done; 19256668Sshin } 19356668Sshin if (port_check_v6("LPRT") == 1) 19456668Sshin goto lprt_done; 19556668Sshin#endif 19656668Sshin lprt_done: 197132925Syar ; 19856668Sshin } 19956668Sshin | EPRT check_login SP STRING CRLF 20056668Sshin { 20156668Sshin char delim; 20256668Sshin char *tmp = NULL; 20356668Sshin char *p, *q; 20456668Sshin char *result[3]; 20556668Sshin struct addrinfo hints; 20656668Sshin struct addrinfo *res; 20756668Sshin int i; 20856668Sshin 20956668Sshin if (epsvall) { 210137852Syar reply(501, "No EPRT allowed after EPSV ALL."); 21156668Sshin goto eprt_done; 21256668Sshin } 21356668Sshin if (!$2) 21456668Sshin goto eprt_done; 21556668Sshin 21656668Sshin memset(&data_dest, 0, sizeof(data_dest)); 21756668Sshin tmp = strdup($4); 21876096Smarkm if (ftpdebug) 21956668Sshin syslog(LOG_DEBUG, "%s", tmp); 22056668Sshin if (!tmp) { 22176096Smarkm fatalerror("not enough core"); 22256668Sshin /*NOTREACHED*/ 22356668Sshin } 22456668Sshin p = tmp; 22556668Sshin delim = p[0]; 22656668Sshin p++; 22756668Sshin memset(result, 0, sizeof(result)); 22856668Sshin for (i = 0; i < 3; i++) { 22956668Sshin q = strchr(p, delim); 23056668Sshin if (!q || *q != delim) { 23156668Sshin parsefail: 23256668Sshin reply(500, 23356668Sshin "Invalid argument, rejected."); 23456668Sshin if (tmp) 23556668Sshin free(tmp); 23617433Spst usedefault = 1; 23756668Sshin goto eprt_done; 23817433Spst } 23956668Sshin *q++ = '\0'; 24056668Sshin result[i] = p; 24176096Smarkm if (ftpdebug) 24256668Sshin syslog(LOG_DEBUG, "%d: %s", i, p); 24356668Sshin p = q; 2441592Srgrimes } 24556668Sshin 24656668Sshin /* some more sanity check */ 24756668Sshin p = result[0]; 24856668Sshin while (*p) { 24956668Sshin if (!isdigit(*p)) 25056668Sshin goto parsefail; 25156668Sshin p++; 25256668Sshin } 25356668Sshin p = result[2]; 25456668Sshin while (*p) { 25556668Sshin if (!isdigit(*p)) 25656668Sshin goto parsefail; 25756668Sshin p++; 25856668Sshin } 25956668Sshin 26056668Sshin /* grab address */ 26156668Sshin memset(&hints, 0, sizeof(hints)); 26256668Sshin if (atoi(result[0]) == 1) 26356668Sshin hints.ai_family = PF_INET; 26456668Sshin#ifdef INET6 26556668Sshin else if (atoi(result[0]) == 2) 26656668Sshin hints.ai_family = PF_INET6; 26756668Sshin#endif 26856668Sshin else 26956668Sshin hints.ai_family = PF_UNSPEC; /*XXX*/ 27056668Sshin hints.ai_socktype = SOCK_STREAM; 27156668Sshin i = getaddrinfo(result[1], result[2], &hints, &res); 27256668Sshin if (i) 27356668Sshin goto parsefail; 27456668Sshin memcpy(&data_dest, res->ai_addr, res->ai_addrlen); 27556668Sshin#ifdef INET6 27656668Sshin if (his_addr.su_family == AF_INET6 27756668Sshin && data_dest.su_family == AF_INET6) { 27856668Sshin /* XXX more sanity checks! */ 27956668Sshin data_dest.su_sin6.sin6_scope_id = 28056668Sshin his_addr.su_sin6.sin6_scope_id; 28156668Sshin } 28256668Sshin#endif 28356668Sshin free(tmp); 28456668Sshin tmp = NULL; 28556668Sshin 28656668Sshin if (port_check("EPRT") == 1) 28756668Sshin goto eprt_done; 28856668Sshin#ifdef INET6 28956668Sshin if (his_addr.su_family != AF_INET6) { 29056668Sshin usedefault = 1; 29156668Sshin reply(500, "Invalid address rejected."); 29256668Sshin goto eprt_done; 29356668Sshin } 29456668Sshin if (port_check_v6("EPRT") == 1) 29556668Sshin goto eprt_done; 29656668Sshin#endif 29788935Sdwmalone eprt_done: 29888935Sdwmalone free($4); 2991592Srgrimes } 30017433Spst | PASV check_login CRLF 3011592Srgrimes { 30256668Sshin if (epsvall) 303137852Syar reply(501, "No PASV allowed after EPSV ALL."); 30456668Sshin else if ($2) 30517433Spst passive(); 3061592Srgrimes } 30756668Sshin | LPSV check_login CRLF 30856668Sshin { 30956668Sshin if (epsvall) 310137852Syar reply(501, "No LPSV allowed after EPSV ALL."); 31156668Sshin else if ($2) 31256668Sshin long_passive("LPSV", PF_UNSPEC); 31356668Sshin } 31470102Sphk | EPSV check_login_epsv SP NUMBER CRLF 31556668Sshin { 31656668Sshin if ($2) { 31756668Sshin int pf; 31892272Smaxim switch ($4.i) { 31956668Sshin case 1: 32056668Sshin pf = PF_INET; 32156668Sshin break; 32256668Sshin#ifdef INET6 32356668Sshin case 2: 32456668Sshin pf = PF_INET6; 32556668Sshin break; 32656668Sshin#endif 32756668Sshin default: 32856668Sshin pf = -1; /*junk value*/ 32956668Sshin break; 33056668Sshin } 33156668Sshin long_passive("EPSV", pf); 33256668Sshin } 33356668Sshin } 33470102Sphk | EPSV check_login_epsv SP ALL CRLF 33556668Sshin { 33656668Sshin if ($2) { 337137852Syar reply(200, "EPSV ALL command successful."); 33856668Sshin epsvall++; 33956668Sshin } 34056668Sshin } 34170102Sphk | EPSV check_login_epsv CRLF 34256668Sshin { 34356668Sshin if ($2) 34456668Sshin long_passive("EPSV", PF_UNSPEC); 34556668Sshin } 34671278Sjedgar | TYPE check_login SP type_code CRLF 3471592Srgrimes { 34871278Sjedgar if ($2) { 34971278Sjedgar switch (cmd_type) { 3501592Srgrimes 35171278Sjedgar case TYPE_A: 35271278Sjedgar if (cmd_form == FORM_N) { 35371278Sjedgar reply(200, "Type set to A."); 35471278Sjedgar type = cmd_type; 35571278Sjedgar form = cmd_form; 35671278Sjedgar } else 35771278Sjedgar reply(504, "Form must be N."); 35871278Sjedgar break; 3591592Srgrimes 36071278Sjedgar case TYPE_E: 36171278Sjedgar reply(504, "Type E not implemented."); 36271278Sjedgar break; 3631592Srgrimes 36471278Sjedgar case TYPE_I: 36571278Sjedgar reply(200, "Type set to I."); 36671278Sjedgar type = cmd_type; 36771278Sjedgar break; 3681592Srgrimes 36971278Sjedgar case TYPE_L: 370103949Smike#if CHAR_BIT == 8 37171278Sjedgar if (cmd_bytesz == 8) { 37271278Sjedgar reply(200, 37371278Sjedgar "Type set to L (byte size 8)."); 37471278Sjedgar type = cmd_type; 37571278Sjedgar } else 37671278Sjedgar reply(504, "Byte size must be 8."); 377103949Smike#else /* CHAR_BIT == 8 */ 378103949Smike UNIMPLEMENTED for CHAR_BIT != 8 379103949Smike#endif /* CHAR_BIT == 8 */ 38071278Sjedgar } 3811592Srgrimes } 3821592Srgrimes } 38371278Sjedgar | STRU check_login SP struct_code CRLF 3841592Srgrimes { 38571278Sjedgar if ($2) { 38671278Sjedgar switch ($4) { 3871592Srgrimes 38871278Sjedgar case STRU_F: 389137852Syar reply(200, "STRU F accepted."); 39071278Sjedgar break; 3911592Srgrimes 39271278Sjedgar default: 39371278Sjedgar reply(504, "Unimplemented STRU type."); 39471278Sjedgar } 3951592Srgrimes } 3961592Srgrimes } 39771278Sjedgar | MODE check_login SP mode_code CRLF 3981592Srgrimes { 39971278Sjedgar if ($2) { 40071278Sjedgar switch ($4) { 4011592Srgrimes 40271278Sjedgar case MODE_S: 403137852Syar reply(200, "MODE S accepted."); 40471278Sjedgar break; 40571278Sjedgar 40671278Sjedgar default: 40771278Sjedgar reply(502, "Unimplemented MODE type."); 40871278Sjedgar } 4091592Srgrimes } 4101592Srgrimes } 41171278Sjedgar | ALLO check_login SP NUMBER CRLF 4121592Srgrimes { 41371278Sjedgar if ($2) { 41471278Sjedgar reply(202, "ALLO command ignored."); 41571278Sjedgar } 4161592Srgrimes } 41771278Sjedgar | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 4181592Srgrimes { 41971278Sjedgar if ($2) { 42071278Sjedgar reply(202, "ALLO command ignored."); 42171278Sjedgar } 4221592Srgrimes } 4231592Srgrimes | RETR check_login SP pathname CRLF 4241592Srgrimes { 42582796Ssheldonh if (noretr || (guest && noguestretr)) 426137852Syar reply(500, "RETR command disabled."); 42782460Snik else if ($2 && $4 != NULL) 428132931Syar retrieve(NULL, $4); 42982460Snik 4301592Srgrimes if ($4 != NULL) 4311592Srgrimes free($4); 4321592Srgrimes } 43370102Sphk | STOR check_login_ro SP pathname CRLF 4341592Srgrimes { 4351592Srgrimes if ($2 && $4 != NULL) 4361592Srgrimes store($4, "w", 0); 4371592Srgrimes if ($4 != NULL) 4381592Srgrimes free($4); 4391592Srgrimes } 44070102Sphk | APPE check_login_ro SP pathname CRLF 4411592Srgrimes { 4421592Srgrimes if ($2 && $4 != NULL) 4431592Srgrimes store($4, "a", 0); 4441592Srgrimes if ($4 != NULL) 4451592Srgrimes free($4); 4461592Srgrimes } 4471592Srgrimes | NLST check_login CRLF 4481592Srgrimes { 4491592Srgrimes if ($2) 4501592Srgrimes send_file_list("."); 4511592Srgrimes } 452101395Syar | NLST check_login SP pathstring CRLF 4531592Srgrimes { 454101395Syar if ($2) 4551592Srgrimes send_file_list($4); 456101395Syar free($4); 4571592Srgrimes } 4581592Srgrimes | LIST check_login CRLF 4591592Srgrimes { 4601592Srgrimes if ($2) 461109380Syar retrieve(_PATH_LS " -lgA", ""); 4621592Srgrimes } 46375567Speter | LIST check_login SP pathstring CRLF 4641592Srgrimes { 465101395Syar if ($2) 466109380Syar retrieve(_PATH_LS " -lgA %s", $4); 467101395Syar free($4); 4681592Srgrimes } 4691592Srgrimes | STAT check_login SP pathname CRLF 4701592Srgrimes { 4711592Srgrimes if ($2 && $4 != NULL) 4721592Srgrimes statfilecmd($4); 4731592Srgrimes if ($4 != NULL) 4741592Srgrimes free($4); 4751592Srgrimes } 47671278Sjedgar | STAT check_login CRLF 4771592Srgrimes { 47871278Sjedgar if ($2) { 47971278Sjedgar statcmd(); 48071278Sjedgar } 4811592Srgrimes } 48270102Sphk | DELE check_login_ro SP pathname CRLF 4831592Srgrimes { 4841592Srgrimes if ($2 && $4 != NULL) 4851592Srgrimes delete($4); 4861592Srgrimes if ($4 != NULL) 4871592Srgrimes free($4); 4881592Srgrimes } 48970102Sphk | RNTO check_login_ro SP pathname CRLF 4901592Srgrimes { 491101379Syar if ($2 && $4 != NULL) { 49217433Spst if (fromname) { 49317433Spst renamecmd(fromname, $4); 49417433Spst free(fromname); 495132931Syar fromname = NULL; 49617433Spst } else { 49717433Spst reply(503, "Bad sequence of commands."); 49817433Spst } 4991592Srgrimes } 500101379Syar if ($4 != NULL) 501101379Syar free($4); 5021592Srgrimes } 50371278Sjedgar | ABOR check_login CRLF 5041592Srgrimes { 50571278Sjedgar if ($2) 50671278Sjedgar reply(225, "ABOR command successful."); 5071592Srgrimes } 5081592Srgrimes | CWD check_login CRLF 5091592Srgrimes { 51069234Sdanny if ($2) { 511110036Syar cwd(homedir); 51269234Sdanny } 5131592Srgrimes } 5141592Srgrimes | CWD check_login SP pathname CRLF 5151592Srgrimes { 5161592Srgrimes if ($2 && $4 != NULL) 5171592Srgrimes cwd($4); 5181592Srgrimes if ($4 != NULL) 5191592Srgrimes free($4); 5201592Srgrimes } 5211592Srgrimes | HELP CRLF 5221592Srgrimes { 523132931Syar help(cmdtab, NULL); 5241592Srgrimes } 5251592Srgrimes | HELP SP STRING CRLF 5261592Srgrimes { 5271592Srgrimes char *cp = $3; 5281592Srgrimes 5291592Srgrimes if (strncasecmp(cp, "SITE", 4) == 0) { 5301592Srgrimes cp = $3 + 4; 5311592Srgrimes if (*cp == ' ') 5321592Srgrimes cp++; 5331592Srgrimes if (*cp) 5341592Srgrimes help(sitetab, cp); 5351592Srgrimes else 536132931Syar help(sitetab, NULL); 5371592Srgrimes } else 5381592Srgrimes help(cmdtab, $3); 53988935Sdwmalone free($3); 5401592Srgrimes } 5411592Srgrimes | NOOP CRLF 5421592Srgrimes { 5431592Srgrimes reply(200, "NOOP command successful."); 5441592Srgrimes } 54570102Sphk | MKD check_login_ro SP pathname CRLF 5461592Srgrimes { 5471592Srgrimes if ($2 && $4 != NULL) 5481592Srgrimes makedir($4); 5491592Srgrimes if ($4 != NULL) 5501592Srgrimes free($4); 5511592Srgrimes } 55270102Sphk | RMD check_login_ro SP pathname CRLF 5531592Srgrimes { 5541592Srgrimes if ($2 && $4 != NULL) 5551592Srgrimes removedir($4); 5561592Srgrimes if ($4 != NULL) 5571592Srgrimes free($4); 5581592Srgrimes } 5591592Srgrimes | PWD check_login CRLF 5601592Srgrimes { 5611592Srgrimes if ($2) 5621592Srgrimes pwd(); 5631592Srgrimes } 5641592Srgrimes | CDUP check_login CRLF 5651592Srgrimes { 5661592Srgrimes if ($2) 5671592Srgrimes cwd(".."); 5681592Srgrimes } 5691592Srgrimes | SITE SP HELP CRLF 5701592Srgrimes { 571132931Syar help(sitetab, NULL); 5721592Srgrimes } 5731592Srgrimes | SITE SP HELP SP STRING CRLF 5741592Srgrimes { 5751592Srgrimes help(sitetab, $5); 57688935Sdwmalone free($5); 5771592Srgrimes } 57875535Sphk | SITE SP MDFIVE check_login SP pathname CRLF 57975535Sphk { 58075535Sphk char p[64], *q; 58175535Sphk 582101379Syar if ($4 && $6) { 58375535Sphk q = MD5File($6, p); 58475535Sphk if (q != NULL) 58575535Sphk reply(200, "MD5(%s) = %s", $6, p); 58675535Sphk else 58775535Sphk perror_reply(550, $6); 58875535Sphk } 58988935Sdwmalone if ($6) 59088935Sdwmalone free($6); 59175535Sphk } 5921592Srgrimes | SITE SP UMASK check_login CRLF 5931592Srgrimes { 5941592Srgrimes int oldmask; 5951592Srgrimes 5961592Srgrimes if ($4) { 5971592Srgrimes oldmask = umask(0); 5981592Srgrimes (void) umask(oldmask); 599137852Syar reply(200, "Current UMASK is %03o.", oldmask); 6001592Srgrimes } 6011592Srgrimes } 6021592Srgrimes | SITE SP UMASK check_login SP octal_number CRLF 6031592Srgrimes { 6041592Srgrimes int oldmask; 6051592Srgrimes 6061592Srgrimes if ($4) { 6071592Srgrimes if (($6 == -1) || ($6 > 0777)) { 608137852Syar reply(501, "Bad UMASK value."); 6091592Srgrimes } else { 6101592Srgrimes oldmask = umask($6); 6111592Srgrimes reply(200, 612137852Syar "UMASK set to %03o (was %03o).", 6131592Srgrimes $6, oldmask); 6141592Srgrimes } 6151592Srgrimes } 6161592Srgrimes } 61770102Sphk | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF 6181592Srgrimes { 6191592Srgrimes if ($4 && ($8 != NULL)) { 620101378Syar if (($6 == -1 ) || ($6 > 0777)) 621137852Syar reply(501, "Bad mode value."); 6221592Srgrimes else if (chmod($8, $6) < 0) 6231592Srgrimes perror_reply(550, $8); 6241592Srgrimes else 6251592Srgrimes reply(200, "CHMOD command successful."); 6261592Srgrimes } 6271592Srgrimes if ($8 != NULL) 6281592Srgrimes free($8); 6291592Srgrimes } 63071278Sjedgar | SITE SP check_login IDLE CRLF 6311592Srgrimes { 63271278Sjedgar if ($3) 63371278Sjedgar reply(200, 634137852Syar "Current IDLE time limit is %d seconds; max %d.", 63571278Sjedgar timeout, maxtimeout); 6361592Srgrimes } 63771278Sjedgar | SITE SP check_login IDLE SP NUMBER CRLF 6381592Srgrimes { 63971278Sjedgar if ($3) { 64092272Smaxim if ($6.i < 30 || $6.i > maxtimeout) { 64171278Sjedgar reply(501, 642137852Syar "Maximum IDLE time must be between 30 and %d seconds.", 64371278Sjedgar maxtimeout); 64471278Sjedgar } else { 64592272Smaxim timeout = $6.i; 646137659Syar (void) alarm(timeout); 64771278Sjedgar reply(200, 648137852Syar "Maximum IDLE time set to %d seconds.", 64971278Sjedgar timeout); 65071278Sjedgar } 6511592Srgrimes } 6521592Srgrimes } 65370102Sphk | STOU check_login_ro SP pathname CRLF 6541592Srgrimes { 6551592Srgrimes if ($2 && $4 != NULL) 6561592Srgrimes store($4, "w", 1); 6571592Srgrimes if ($4 != NULL) 6581592Srgrimes free($4); 6591592Srgrimes } 660168849Syar | FEAT CRLF 661168849Syar { 662168849Syar lreply(211, "Extensions supported:"); 663168849Syar#if 0 664168849Syar /* XXX these two keywords are non-standard */ 665168849Syar printf(" EPRT\r\n"); 666168849Syar if (!noepsv) 667168849Syar printf(" EPSV\r\n"); 668168849Syar#endif 669168849Syar printf(" MDTM\r\n"); 670168849Syar printf(" REST STREAM\r\n"); 671168849Syar printf(" SIZE\r\n"); 672168849Syar if (assumeutf8) { 673168849Syar /* TVFS requires UTF8, see RFC 3659 */ 674168849Syar printf(" TVFS\r\n"); 675168849Syar printf(" UTF8\r\n"); 676168849Syar } 677168849Syar reply(211, "End."); 678168849Syar } 67971278Sjedgar | SYST check_login CRLF 6801592Srgrimes { 681116439Syar if ($2) { 682116439Syar if (hostinfo) 6831592Srgrimes#ifdef BSD 684116439Syar reply(215, "UNIX Type: L%d Version: BSD-%d", 685116439Syar CHAR_BIT, BSD); 6861592Srgrimes#else /* BSD */ 687116439Syar reply(215, "UNIX Type: L%d", CHAR_BIT); 6881592Srgrimes#endif /* BSD */ 689116439Syar else 690116439Syar reply(215, "UNKNOWN Type: L%d", CHAR_BIT); 691116439Syar } 6921592Srgrimes } 6931592Srgrimes 6941592Srgrimes /* 6951592Srgrimes * SIZE is not in RFC959, but Postel has blessed it and 6961592Srgrimes * it will be in the updated RFC. 6971592Srgrimes * 6981592Srgrimes * Return size of file in a format suitable for 6991592Srgrimes * using with RESTART (we just count bytes). 7001592Srgrimes */ 7011592Srgrimes | SIZE check_login SP pathname CRLF 7021592Srgrimes { 7031592Srgrimes if ($2 && $4 != NULL) 7041592Srgrimes sizecmd($4); 7051592Srgrimes if ($4 != NULL) 7061592Srgrimes free($4); 7071592Srgrimes } 7081592Srgrimes 7091592Srgrimes /* 7101592Srgrimes * MDTM is not in RFC959, but Postel has blessed it and 7111592Srgrimes * it will be in the updated RFC. 7121592Srgrimes * 7131592Srgrimes * Return modification time of file as an ISO 3307 7141592Srgrimes * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 7151592Srgrimes * where xxx is the fractional second (of any precision, 7161592Srgrimes * not necessarily 3 digits) 7171592Srgrimes */ 7181592Srgrimes | MDTM check_login SP pathname CRLF 7191592Srgrimes { 7201592Srgrimes if ($2 && $4 != NULL) { 7211592Srgrimes struct stat stbuf; 7221592Srgrimes if (stat($4, &stbuf) < 0) 723137850Syar perror_reply(550, $4); 7241592Srgrimes else if (!S_ISREG(stbuf.st_mode)) { 7251592Srgrimes reply(550, "%s: not a plain file.", $4); 7261592Srgrimes } else { 7271592Srgrimes struct tm *t; 7281592Srgrimes t = gmtime(&stbuf.st_mtime); 7291592Srgrimes reply(213, 73017435Spst "%04d%02d%02d%02d%02d%02d", 73117435Spst 1900 + t->tm_year, 73217435Spst t->tm_mon+1, t->tm_mday, 7331592Srgrimes t->tm_hour, t->tm_min, t->tm_sec); 7341592Srgrimes } 7351592Srgrimes } 7361592Srgrimes if ($4 != NULL) 7371592Srgrimes free($4); 7381592Srgrimes } 7391592Srgrimes | QUIT CRLF 7401592Srgrimes { 7411592Srgrimes reply(221, "Goodbye."); 7421592Srgrimes dologout(0); 7431592Srgrimes } 744102565Syar | NOTIMPL 745102565Syar { 746102565Syar nack($1); 747102565Syar } 74889935Syar | error 7491592Srgrimes { 75089935Syar yyclearin; /* discard lookahead data */ 75189935Syar yyerrok; /* clear error condition */ 752102565Syar state = CMD; /* reset lexer state */ 7531592Srgrimes } 7541592Srgrimes ; 7551592Srgrimesrcmd 75670102Sphk : RNFR check_login_ro SP pathname CRLF 7571592Srgrimes { 758132930Syar restart_point = 0; 7591592Srgrimes if ($2 && $4) { 76088935Sdwmalone if (fromname) 76188935Sdwmalone free(fromname); 762132931Syar fromname = NULL; 76388935Sdwmalone if (renamefrom($4)) 76488935Sdwmalone fromname = $4; 76588935Sdwmalone else 7661592Srgrimes free($4); 76788935Sdwmalone } else if ($4) { 76888935Sdwmalone free($4); 7691592Srgrimes } 7701592Srgrimes } 77192272Smaxim | REST check_login SP NUMBER CRLF 7721592Srgrimes { 77371278Sjedgar if ($2) { 77488935Sdwmalone if (fromname) 77588935Sdwmalone free(fromname); 776132931Syar fromname = NULL; 77792272Smaxim restart_point = $4.o; 778132929Syar reply(350, "Restarting at %jd. %s", 779132929Syar (intmax_t)restart_point, 78071278Sjedgar "Send STORE or RETRIEVE to initiate transfer."); 78171278Sjedgar } 7821592Srgrimes } 7831592Srgrimes ; 7841592Srgrimes 7851592Srgrimesusername 7861592Srgrimes : STRING 7871592Srgrimes ; 7881592Srgrimes 7891592Srgrimespassword 7901592Srgrimes : /* empty */ 7911592Srgrimes { 7921592Srgrimes $$ = (char *)calloc(1, sizeof(char)); 7931592Srgrimes } 7941592Srgrimes | STRING 7951592Srgrimes ; 7961592Srgrimes 7971592Srgrimesbyte_size 7981592Srgrimes : NUMBER 79992272Smaxim { 80092272Smaxim $$ = $1.i; 80192272Smaxim } 8021592Srgrimes ; 8031592Srgrimes 8041592Srgrimeshost_port 8051592Srgrimes : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 8061592Srgrimes NUMBER COMMA NUMBER 8071592Srgrimes { 8081592Srgrimes char *a, *p; 8091592Srgrimes 81056668Sshin data_dest.su_len = sizeof(struct sockaddr_in); 81156668Sshin data_dest.su_family = AF_INET; 81256668Sshin p = (char *)&data_dest.su_sin.sin_port; 81392272Smaxim p[0] = $9.i; p[1] = $11.i; 81456668Sshin a = (char *)&data_dest.su_sin.sin_addr; 81592272Smaxim a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; 8161592Srgrimes } 8171592Srgrimes ; 8181592Srgrimes 81956668Sshinhost_long_port 82056668Sshin : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 82156668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 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 82656668Sshin { 82756668Sshin char *a, *p; 82856668Sshin 82956668Sshin memset(&data_dest, 0, sizeof(data_dest)); 83056668Sshin data_dest.su_len = sizeof(struct sockaddr_in6); 83156668Sshin data_dest.su_family = AF_INET6; 83256668Sshin p = (char *)&data_dest.su_port; 83392272Smaxim p[0] = $39.i; p[1] = $41.i; 83456668Sshin a = (char *)&data_dest.su_sin6.sin6_addr; 83592272Smaxim a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 83692272Smaxim a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i; 83792272Smaxim a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i; 83892272Smaxim a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i; 83956668Sshin if (his_addr.su_family == AF_INET6) { 84056668Sshin /* XXX more sanity checks! */ 84156668Sshin data_dest.su_sin6.sin6_scope_id = 84256668Sshin his_addr.su_sin6.sin6_scope_id; 84356668Sshin } 84492272Smaxim if ($1.i != 6 || $3.i != 16 || $37.i != 2) 84556668Sshin memset(&data_dest, 0, sizeof(data_dest)); 84656668Sshin } 84756668Sshin | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 84856668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 84956668Sshin NUMBER 85056668Sshin { 85156668Sshin char *a, *p; 85256668Sshin 85356668Sshin memset(&data_dest, 0, sizeof(data_dest)); 85456668Sshin data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 85556668Sshin data_dest.su_family = AF_INET; 85656668Sshin p = (char *)&data_dest.su_port; 85792272Smaxim p[0] = $15.i; p[1] = $17.i; 85856668Sshin a = (char *)&data_dest.su_sin.sin_addr; 85992272Smaxim a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 86092272Smaxim if ($1.i != 4 || $3.i != 4 || $13.i != 2) 86156668Sshin memset(&data_dest, 0, sizeof(data_dest)); 86256668Sshin } 86356668Sshin ; 86456668Sshin 8651592Srgrimesform_code 8661592Srgrimes : N 8671592Srgrimes { 8681592Srgrimes $$ = FORM_N; 8691592Srgrimes } 8701592Srgrimes | T 8711592Srgrimes { 8721592Srgrimes $$ = FORM_T; 8731592Srgrimes } 8741592Srgrimes | C 8751592Srgrimes { 8761592Srgrimes $$ = FORM_C; 8771592Srgrimes } 8781592Srgrimes ; 8791592Srgrimes 8801592Srgrimestype_code 8811592Srgrimes : A 8821592Srgrimes { 8831592Srgrimes cmd_type = TYPE_A; 8841592Srgrimes cmd_form = FORM_N; 8851592Srgrimes } 8861592Srgrimes | A SP form_code 8871592Srgrimes { 8881592Srgrimes cmd_type = TYPE_A; 8891592Srgrimes cmd_form = $3; 8901592Srgrimes } 8911592Srgrimes | E 8921592Srgrimes { 8931592Srgrimes cmd_type = TYPE_E; 8941592Srgrimes cmd_form = FORM_N; 8951592Srgrimes } 8961592Srgrimes | E SP form_code 8971592Srgrimes { 8981592Srgrimes cmd_type = TYPE_E; 8991592Srgrimes cmd_form = $3; 9001592Srgrimes } 9011592Srgrimes | I 9021592Srgrimes { 9031592Srgrimes cmd_type = TYPE_I; 9041592Srgrimes } 9051592Srgrimes | L 9061592Srgrimes { 9071592Srgrimes cmd_type = TYPE_L; 908103949Smike cmd_bytesz = CHAR_BIT; 9091592Srgrimes } 9101592Srgrimes | L SP byte_size 9111592Srgrimes { 9121592Srgrimes cmd_type = TYPE_L; 9131592Srgrimes cmd_bytesz = $3; 9141592Srgrimes } 9151592Srgrimes /* this is for a bug in the BBN ftp */ 9161592Srgrimes | L byte_size 9171592Srgrimes { 9181592Srgrimes cmd_type = TYPE_L; 9191592Srgrimes cmd_bytesz = $2; 9201592Srgrimes } 9211592Srgrimes ; 9221592Srgrimes 9231592Srgrimesstruct_code 9241592Srgrimes : F 9251592Srgrimes { 9261592Srgrimes $$ = STRU_F; 9271592Srgrimes } 9281592Srgrimes | R 9291592Srgrimes { 9301592Srgrimes $$ = STRU_R; 9311592Srgrimes } 9321592Srgrimes | P 9331592Srgrimes { 9341592Srgrimes $$ = STRU_P; 9351592Srgrimes } 9361592Srgrimes ; 9371592Srgrimes 9381592Srgrimesmode_code 9391592Srgrimes : S 9401592Srgrimes { 9411592Srgrimes $$ = MODE_S; 9421592Srgrimes } 9431592Srgrimes | B 9441592Srgrimes { 9451592Srgrimes $$ = MODE_B; 9461592Srgrimes } 9471592Srgrimes | C 9481592Srgrimes { 9491592Srgrimes $$ = MODE_C; 9501592Srgrimes } 9511592Srgrimes ; 9521592Srgrimes 9531592Srgrimespathname 9541592Srgrimes : pathstring 9551592Srgrimes { 95675567Speter if (logged_in && $1) { 957110340Syar char *p; 9581592Srgrimes 959110340Syar /* 960110340Syar * Expand ~user manually since glob(3) 961110340Syar * will return the unexpanded pathname 962110340Syar * if the corresponding file/directory 963110340Syar * doesn't exist yet. Using sole glob(3) 964110340Syar * would break natural commands like 965110340Syar * MKD ~user/newdir 966110340Syar * or 967110340Syar * RNTO ~/newfile 968110340Syar */ 969110340Syar if ((p = exptilde($1)) != NULL) { 970110340Syar $$ = expglob(p); 971110340Syar free(p); 972110340Syar } else 9731592Srgrimes $$ = NULL; 9741592Srgrimes free($1); 9751592Srgrimes } else 9761592Srgrimes $$ = $1; 9771592Srgrimes } 9781592Srgrimes ; 9791592Srgrimes 9801592Srgrimespathstring 9811592Srgrimes : STRING 9821592Srgrimes ; 9831592Srgrimes 9841592Srgrimesoctal_number 9851592Srgrimes : NUMBER 9861592Srgrimes { 9871592Srgrimes int ret, dec, multby, digit; 9881592Srgrimes 9891592Srgrimes /* 9901592Srgrimes * Convert a number that was read as decimal number 9911592Srgrimes * to what it would be if it had been read as octal. 9921592Srgrimes */ 99392272Smaxim dec = $1.i; 9941592Srgrimes multby = 1; 9951592Srgrimes ret = 0; 9961592Srgrimes while (dec) { 9971592Srgrimes digit = dec%10; 9981592Srgrimes if (digit > 7) { 9991592Srgrimes ret = -1; 10001592Srgrimes break; 10011592Srgrimes } 10021592Srgrimes ret += digit * multby; 10031592Srgrimes multby *= 8; 10041592Srgrimes dec /= 10; 10051592Srgrimes } 10061592Srgrimes $$ = ret; 10071592Srgrimes } 10081592Srgrimes ; 10091592Srgrimes 10101592Srgrimes 10111592Srgrimescheck_login 10121592Srgrimes : /* empty */ 10131592Srgrimes { 101470102Sphk $$ = check_login1(); 10151592Srgrimes } 10161592Srgrimes ; 10171592Srgrimes 101870102Sphkcheck_login_epsv 101970102Sphk : /* empty */ 102070102Sphk { 102170102Sphk if (noepsv) { 1022137852Syar reply(500, "EPSV command disabled."); 102370102Sphk $$ = 0; 102470102Sphk } 102570102Sphk else 102670102Sphk $$ = check_login1(); 102770102Sphk } 102870102Sphk ; 102970102Sphk 103070102Sphkcheck_login_ro 103170102Sphk : /* empty */ 103270102Sphk { 103370102Sphk if (readonly) { 103472710Sdes reply(550, "Permission denied."); 103570102Sphk $$ = 0; 103670102Sphk } 103770102Sphk else 103870102Sphk $$ = check_login1(); 103970102Sphk } 104070102Sphk ; 104170102Sphk 10421592Srgrimes%% 10431592Srgrimes 10441592Srgrimes#define CMD 0 /* beginning of command */ 10451592Srgrimes#define ARGS 1 /* expect miscellaneous arguments */ 10461592Srgrimes#define STR1 2 /* expect SP followed by STRING */ 10471592Srgrimes#define STR2 3 /* expect STRING */ 10481592Srgrimes#define OSTR 4 /* optional SP then STRING */ 104975556Sgreen#define ZSTR1 5 /* optional SP then optional STRING */ 10501592Srgrimes#define ZSTR2 6 /* optional STRING after SP */ 10511592Srgrimes#define SITECMD 7 /* SITE command */ 10521592Srgrimes#define NSTR 8 /* Number followed by a string */ 10531592Srgrimes 105475560Sjedgar#define MAXGLOBARGS 1000 105575560Sjedgar 1056101034Syar#define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */ 1057101034Syar 10581592Srgrimesstruct tab { 10591592Srgrimes char *name; 10601592Srgrimes short token; 10611592Srgrimes short state; 10621592Srgrimes short implemented; /* 1 if command is implemented */ 10631592Srgrimes char *help; 10641592Srgrimes}; 10651592Srgrimes 10661592Srgrimesstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 10671592Srgrimes { "USER", USER, STR1, 1, "<sp> username" }, 106875556Sgreen { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" }, 10691592Srgrimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 10701592Srgrimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 10711592Srgrimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 10721592Srgrimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 1073101806Syar { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" }, 107456668Sshin { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 107556668Sshin { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 10761592Srgrimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 107756668Sshin { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 107856668Sshin { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 1079101806Syar { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" }, 10801592Srgrimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 10811592Srgrimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 10821592Srgrimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 10831592Srgrimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 10841592Srgrimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 10851592Srgrimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 10861592Srgrimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 10871592Srgrimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 10881592Srgrimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 10891592Srgrimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 10901592Srgrimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 10911592Srgrimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 10921592Srgrimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 10931592Srgrimes { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 10941592Srgrimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 10951592Srgrimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 10961592Srgrimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 10971592Srgrimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 10981592Srgrimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 10991592Srgrimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 11001592Srgrimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 11011592Srgrimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 11021592Srgrimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 11031592Srgrimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 1104168849Syar { "FEAT", FEAT, ARGS, 1, "(get extended features)" }, 11051592Srgrimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 11061592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11071592Srgrimes { "NOOP", NOOP, ARGS, 1, "" }, 11081592Srgrimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 11091592Srgrimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 11101592Srgrimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 11111592Srgrimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 11121592Srgrimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 11131592Srgrimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 11141592Srgrimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11151592Srgrimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11161592Srgrimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 11171592Srgrimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 11181592Srgrimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 11191592Srgrimes { NULL, 0, 0, 0, 0 } 11201592Srgrimes}; 11211592Srgrimes 11221592Srgrimesstruct tab sitetab[] = { 112375535Sphk { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" }, 11241592Srgrimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 11251592Srgrimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 11261592Srgrimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 11271592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11281592Srgrimes { NULL, 0, 0, 0, 0 } 11291592Srgrimes}; 11301592Srgrimes 113190148Simpstatic char *copy(char *); 1132110340Syarstatic char *expglob(char *); 1133110340Syarstatic char *exptilde(char *); 113490148Simpstatic void help(struct tab *, char *); 11351592Srgrimesstatic struct tab * 113690148Simp lookup(struct tab *, char *); 113790148Simpstatic int port_check(const char *); 1138159276Syar#ifdef INET6 113990148Simpstatic int port_check_v6(const char *); 1140159276Syar#endif 114190148Simpstatic void sizecmd(char *); 114290148Simpstatic void toolong(int); 1143159276Syar#ifdef INET6 114490148Simpstatic void v4map_data_dest(void); 1145159276Syar#endif 114690148Simpstatic int yylex(void); 11471592Srgrimes 11481592Srgrimesstatic struct tab * 114990148Simplookup(struct tab *p, char *cmd) 11501592Srgrimes{ 11511592Srgrimes 11521592Srgrimes for (; p->name != NULL; p++) 11531592Srgrimes if (strcmp(cmd, p->name) == 0) 11541592Srgrimes return (p); 11551592Srgrimes return (0); 11561592Srgrimes} 11571592Srgrimes 11581592Srgrimes#include <arpa/telnet.h> 11591592Srgrimes 11601592Srgrimes/* 11611592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes. 11621592Srgrimes */ 1163186405Scpercivaint 116490148Simpgetline(char *s, int n, FILE *iop) 11651592Srgrimes{ 11661592Srgrimes int c; 11671592Srgrimes register char *cs; 1168117352Syar sigset_t sset, osset; 11691592Srgrimes 11701592Srgrimes cs = s; 11711592Srgrimes/* tmpline may contain saved command from urgent mode interruption */ 11721592Srgrimes for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 11731592Srgrimes *cs++ = tmpline[c]; 11741592Srgrimes if (tmpline[c] == '\n') { 11751592Srgrimes *cs++ = '\0'; 117676096Smarkm if (ftpdebug) 11771592Srgrimes syslog(LOG_DEBUG, "command: %s", s); 11781592Srgrimes tmpline[0] = '\0'; 1179186405Scperciva return(0); 11801592Srgrimes } 11811592Srgrimes if (c == 0) 11821592Srgrimes tmpline[0] = '\0'; 11831592Srgrimes } 1184117352Syar /* SIGURG would interrupt stdio if not blocked during the read loop */ 1185117352Syar sigemptyset(&sset); 1186117352Syar sigaddset(&sset, SIGURG); 1187117352Syar sigprocmask(SIG_BLOCK, &sset, &osset); 11881592Srgrimes while ((c = getc(iop)) != EOF) { 11891592Srgrimes c &= 0377; 11901592Srgrimes if (c == IAC) { 1191117351Syar if ((c = getc(iop)) == EOF) 1192117351Syar goto got_eof; 11931592Srgrimes c &= 0377; 11941592Srgrimes switch (c) { 11951592Srgrimes case WILL: 11961592Srgrimes case WONT: 1197117351Syar if ((c = getc(iop)) == EOF) 1198117351Syar goto got_eof; 11991592Srgrimes printf("%c%c%c", IAC, DONT, 0377&c); 12001592Srgrimes (void) fflush(stdout); 12011592Srgrimes continue; 12021592Srgrimes case DO: 12031592Srgrimes case DONT: 1204117351Syar if ((c = getc(iop)) == EOF) 1205117351Syar goto got_eof; 12061592Srgrimes printf("%c%c%c", IAC, WONT, 0377&c); 12071592Srgrimes (void) fflush(stdout); 12081592Srgrimes continue; 12091592Srgrimes case IAC: 12101592Srgrimes break; 12111592Srgrimes default: 12121592Srgrimes continue; /* ignore command */ 12131592Srgrimes } 12141592Srgrimes } 12151592Srgrimes *cs++ = c; 1216186405Scperciva if (--n <= 0) { 1217186405Scperciva /* 1218186405Scperciva * If command doesn't fit into buffer, discard the 1219186405Scperciva * rest of the command and indicate truncation. 1220186405Scperciva * This prevents the command to be split up into 1221186405Scperciva * multiple commands. 1222186405Scperciva */ 1223186405Scperciva while (c != '\n' && (c = getc(iop)) != EOF) 1224186405Scperciva ; 1225186405Scperciva return (-2); 1226186405Scperciva } 1227186405Scperciva if (c == '\n') 12281592Srgrimes break; 12291592Srgrimes } 1230117351Syargot_eof: 1231117352Syar sigprocmask(SIG_SETMASK, &osset, NULL); 12321592Srgrimes if (c == EOF && cs == s) 1233186405Scperciva return (-1); 12341592Srgrimes *cs++ = '\0'; 123576096Smarkm if (ftpdebug) { 12361592Srgrimes if (!guest && strncasecmp("pass ", s, 5) == 0) { 12371592Srgrimes /* Don't syslog passwords */ 12381592Srgrimes syslog(LOG_DEBUG, "command: %.5s ???", s); 12391592Srgrimes } else { 12401592Srgrimes register char *cp; 12411592Srgrimes register int len; 12421592Srgrimes 12431592Srgrimes /* Don't syslog trailing CR-LF */ 12441592Srgrimes len = strlen(s); 12451592Srgrimes cp = s + len - 1; 12461592Srgrimes while (cp >= s && (*cp == '\n' || *cp == '\r')) { 12471592Srgrimes --cp; 12481592Srgrimes --len; 12491592Srgrimes } 12501592Srgrimes syslog(LOG_DEBUG, "command: %.*s", len, s); 12511592Srgrimes } 12521592Srgrimes } 1253186405Scperciva return (0); 12541592Srgrimes} 12551592Srgrimes 12561592Srgrimesstatic void 125790148Simptoolong(int signo) 12581592Srgrimes{ 12591592Srgrimes 12601592Srgrimes reply(421, 12611592Srgrimes "Timeout (%d seconds): closing control connection.", timeout); 12621592Srgrimes if (logging) 12631592Srgrimes syslog(LOG_INFO, "User %s timed out after %d seconds", 12641592Srgrimes (pw ? pw -> pw_name : "unknown"), timeout); 12651592Srgrimes dologout(1); 12661592Srgrimes} 12671592Srgrimes 12681592Srgrimesstatic int 126990148Simpyylex(void) 12701592Srgrimes{ 127189935Syar static int cpos; 12721592Srgrimes char *cp, *cp2; 12731592Srgrimes struct tab *p; 12741592Srgrimes int n; 12751592Srgrimes char c; 12761592Srgrimes 12771592Srgrimes for (;;) { 12781592Srgrimes switch (state) { 12791592Srgrimes 12801592Srgrimes case CMD: 12811592Srgrimes (void) signal(SIGALRM, toolong); 1282137659Syar (void) alarm(timeout); 1283186405Scperciva n = getline(cbuf, sizeof(cbuf)-1, stdin); 1284186405Scperciva if (n == -1) { 12851592Srgrimes reply(221, "You could at least say goodbye."); 12861592Srgrimes dologout(0); 1287186405Scperciva } else if (n == -2) { 1288186405Scperciva reply(500, "Command too long."); 1289186405Scperciva (void) alarm(0); 1290186405Scperciva continue; 12911592Srgrimes } 12921592Srgrimes (void) alarm(0); 12931592Srgrimes#ifdef SETPROCTITLE 129429574Sphk if (strncasecmp(cbuf, "PASS", 4) != 0) 12951592Srgrimes setproctitle("%s: %s", proctitle, cbuf); 12961592Srgrimes#endif /* SETPROCTITLE */ 12971592Srgrimes if ((cp = strchr(cbuf, '\r'))) { 12981592Srgrimes *cp++ = '\n'; 12991592Srgrimes *cp = '\0'; 13001592Srgrimes } 13011592Srgrimes if ((cp = strpbrk(cbuf, " \n"))) 13021592Srgrimes cpos = cp - cbuf; 13031592Srgrimes if (cpos == 0) 13041592Srgrimes cpos = 4; 13051592Srgrimes c = cbuf[cpos]; 13061592Srgrimes cbuf[cpos] = '\0'; 13071592Srgrimes upper(cbuf); 13081592Srgrimes p = lookup(cmdtab, cbuf); 13091592Srgrimes cbuf[cpos] = c; 13103776Spst if (p != 0) { 1311102565Syar yylval.s = p->name; 1312102565Syar if (!p->implemented) 1313102565Syar return (NOTIMPL); /* state remains CMD */ 13141592Srgrimes state = p->state; 13151592Srgrimes return (p->token); 13161592Srgrimes } 13171592Srgrimes break; 13181592Srgrimes 13191592Srgrimes case SITECMD: 13201592Srgrimes if (cbuf[cpos] == ' ') { 13211592Srgrimes cpos++; 13221592Srgrimes return (SP); 13231592Srgrimes } 13241592Srgrimes cp = &cbuf[cpos]; 13251592Srgrimes if ((cp2 = strpbrk(cp, " \n"))) 13261592Srgrimes cpos = cp2 - cbuf; 13271592Srgrimes c = cbuf[cpos]; 13281592Srgrimes cbuf[cpos] = '\0'; 13291592Srgrimes upper(cp); 13301592Srgrimes p = lookup(sitetab, cp); 13311592Srgrimes cbuf[cpos] = c; 13323777Spst if (guest == 0 && p != 0) { 1333102565Syar yylval.s = p->name; 1334102565Syar if (!p->implemented) { 13351592Srgrimes state = CMD; 1336102565Syar return (NOTIMPL); 13371592Srgrimes } 13381592Srgrimes state = p->state; 13391592Srgrimes return (p->token); 13401592Srgrimes } 13411592Srgrimes state = CMD; 13421592Srgrimes break; 13431592Srgrimes 134475556Sgreen case ZSTR1: 13451592Srgrimes case OSTR: 13461592Srgrimes if (cbuf[cpos] == '\n') { 13471592Srgrimes state = CMD; 13481592Srgrimes return (CRLF); 13491592Srgrimes } 13501592Srgrimes /* FALLTHROUGH */ 13511592Srgrimes 13521592Srgrimes case STR1: 13531592Srgrimes dostr1: 13541592Srgrimes if (cbuf[cpos] == ' ') { 13551592Srgrimes cpos++; 135651979Salfred state = state == OSTR ? STR2 : state+1; 13571592Srgrimes return (SP); 13581592Srgrimes } 13591592Srgrimes break; 13601592Srgrimes 13611592Srgrimes case ZSTR2: 13621592Srgrimes if (cbuf[cpos] == '\n') { 13631592Srgrimes state = CMD; 13641592Srgrimes return (CRLF); 13651592Srgrimes } 13661592Srgrimes /* FALLTHROUGH */ 13671592Srgrimes 13681592Srgrimes case STR2: 13691592Srgrimes cp = &cbuf[cpos]; 13701592Srgrimes n = strlen(cp); 13711592Srgrimes cpos += n - 1; 13721592Srgrimes /* 13731592Srgrimes * Make sure the string is nonempty and \n terminated. 13741592Srgrimes */ 13751592Srgrimes if (n > 1 && cbuf[cpos] == '\n') { 13761592Srgrimes cbuf[cpos] = '\0'; 13771592Srgrimes yylval.s = copy(cp); 13781592Srgrimes cbuf[cpos] = '\n'; 13791592Srgrimes state = ARGS; 13801592Srgrimes return (STRING); 13811592Srgrimes } 13821592Srgrimes break; 13831592Srgrimes 13841592Srgrimes case NSTR: 13851592Srgrimes if (cbuf[cpos] == ' ') { 13861592Srgrimes cpos++; 13871592Srgrimes return (SP); 13881592Srgrimes } 13891592Srgrimes if (isdigit(cbuf[cpos])) { 13901592Srgrimes cp = &cbuf[cpos]; 13911592Srgrimes while (isdigit(cbuf[++cpos])) 13921592Srgrimes ; 13931592Srgrimes c = cbuf[cpos]; 13941592Srgrimes cbuf[cpos] = '\0'; 139592272Smaxim yylval.u.i = atoi(cp); 13961592Srgrimes cbuf[cpos] = c; 13971592Srgrimes state = STR1; 13981592Srgrimes return (NUMBER); 13991592Srgrimes } 14001592Srgrimes state = STR1; 14011592Srgrimes goto dostr1; 14021592Srgrimes 14031592Srgrimes case ARGS: 14041592Srgrimes if (isdigit(cbuf[cpos])) { 14051592Srgrimes cp = &cbuf[cpos]; 14061592Srgrimes while (isdigit(cbuf[++cpos])) 14071592Srgrimes ; 14081592Srgrimes c = cbuf[cpos]; 14091592Srgrimes cbuf[cpos] = '\0'; 141092272Smaxim yylval.u.i = atoi(cp); 1411137811Syar yylval.u.o = strtoull(cp, NULL, 10); 14121592Srgrimes cbuf[cpos] = c; 14131592Srgrimes return (NUMBER); 14141592Srgrimes } 141556668Sshin if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 141656668Sshin && !isalnum(cbuf[cpos + 3])) { 141756668Sshin cpos += 3; 141856668Sshin return ALL; 141956668Sshin } 14201592Srgrimes switch (cbuf[cpos++]) { 14211592Srgrimes 14221592Srgrimes case '\n': 14231592Srgrimes state = CMD; 14241592Srgrimes return (CRLF); 14251592Srgrimes 14261592Srgrimes case ' ': 14271592Srgrimes return (SP); 14281592Srgrimes 14291592Srgrimes case ',': 14301592Srgrimes return (COMMA); 14311592Srgrimes 14321592Srgrimes case 'A': 14331592Srgrimes case 'a': 14341592Srgrimes return (A); 14351592Srgrimes 14361592Srgrimes case 'B': 14371592Srgrimes case 'b': 14381592Srgrimes return (B); 14391592Srgrimes 14401592Srgrimes case 'C': 14411592Srgrimes case 'c': 14421592Srgrimes return (C); 14431592Srgrimes 14441592Srgrimes case 'E': 14451592Srgrimes case 'e': 14461592Srgrimes return (E); 14471592Srgrimes 14481592Srgrimes case 'F': 14491592Srgrimes case 'f': 14501592Srgrimes return (F); 14511592Srgrimes 14521592Srgrimes case 'I': 14531592Srgrimes case 'i': 14541592Srgrimes return (I); 14551592Srgrimes 14561592Srgrimes case 'L': 14571592Srgrimes case 'l': 14581592Srgrimes return (L); 14591592Srgrimes 14601592Srgrimes case 'N': 14611592Srgrimes case 'n': 14621592Srgrimes return (N); 14631592Srgrimes 14641592Srgrimes case 'P': 14651592Srgrimes case 'p': 14661592Srgrimes return (P); 14671592Srgrimes 14681592Srgrimes case 'R': 14691592Srgrimes case 'r': 14701592Srgrimes return (R); 14711592Srgrimes 14721592Srgrimes case 'S': 14731592Srgrimes case 's': 14741592Srgrimes return (S); 14751592Srgrimes 14761592Srgrimes case 'T': 14771592Srgrimes case 't': 14781592Srgrimes return (T); 14791592Srgrimes 14801592Srgrimes } 14811592Srgrimes break; 14821592Srgrimes 14831592Srgrimes default: 148476096Smarkm fatalerror("Unknown state in scanner."); 14851592Srgrimes } 14861592Srgrimes state = CMD; 148789935Syar return (LEXERR); 14881592Srgrimes } 14891592Srgrimes} 14901592Srgrimes 14911592Srgrimesvoid 149290148Simpupper(char *s) 14931592Srgrimes{ 14941592Srgrimes while (*s != '\0') { 14951592Srgrimes if (islower(*s)) 14961592Srgrimes *s = toupper(*s); 14971592Srgrimes s++; 14981592Srgrimes } 14991592Srgrimes} 15001592Srgrimes 15011592Srgrimesstatic char * 150290148Simpcopy(char *s) 15031592Srgrimes{ 15041592Srgrimes char *p; 15051592Srgrimes 1506137659Syar p = malloc(strlen(s) + 1); 15071592Srgrimes if (p == NULL) 150876096Smarkm fatalerror("Ran out of memory."); 15091592Srgrimes (void) strcpy(p, s); 15101592Srgrimes return (p); 15111592Srgrimes} 15121592Srgrimes 15131592Srgrimesstatic void 151490148Simphelp(struct tab *ctab, char *s) 15151592Srgrimes{ 15161592Srgrimes struct tab *c; 15171592Srgrimes int width, NCMDS; 15181592Srgrimes char *type; 15191592Srgrimes 15201592Srgrimes if (ctab == sitetab) 15211592Srgrimes type = "SITE "; 15221592Srgrimes else 15231592Srgrimes type = ""; 15241592Srgrimes width = 0, NCMDS = 0; 15251592Srgrimes for (c = ctab; c->name != NULL; c++) { 15261592Srgrimes int len = strlen(c->name); 15271592Srgrimes 15281592Srgrimes if (len > width) 15291592Srgrimes width = len; 15301592Srgrimes NCMDS++; 15311592Srgrimes } 15321592Srgrimes width = (width + 8) &~ 7; 15331592Srgrimes if (s == 0) { 15341592Srgrimes int i, j, w; 15351592Srgrimes int columns, lines; 15361592Srgrimes 15371592Srgrimes lreply(214, "The following %scommands are recognized %s.", 15381592Srgrimes type, "(* =>'s unimplemented)"); 15391592Srgrimes columns = 76 / width; 15401592Srgrimes if (columns == 0) 15411592Srgrimes columns = 1; 15421592Srgrimes lines = (NCMDS + columns - 1) / columns; 15431592Srgrimes for (i = 0; i < lines; i++) { 15441592Srgrimes printf(" "); 15451592Srgrimes for (j = 0; j < columns; j++) { 15461592Srgrimes c = ctab + j * lines + i; 15471592Srgrimes printf("%s%c", c->name, 15481592Srgrimes c->implemented ? ' ' : '*'); 15491592Srgrimes if (c + lines >= &ctab[NCMDS]) 15501592Srgrimes break; 15511592Srgrimes w = strlen(c->name) + 1; 15521592Srgrimes while (w < width) { 15531592Srgrimes putchar(' '); 15541592Srgrimes w++; 15551592Srgrimes } 15561592Srgrimes } 15571592Srgrimes printf("\r\n"); 15581592Srgrimes } 15591592Srgrimes (void) fflush(stdout); 1560110037Syar if (hostinfo) 1561110037Syar reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1562110037Syar else 1563110037Syar reply(214, "End."); 15641592Srgrimes return; 15651592Srgrimes } 15661592Srgrimes upper(s); 15671592Srgrimes c = lookup(ctab, s); 1568132931Syar if (c == NULL) { 15691592Srgrimes reply(502, "Unknown command %s.", s); 15701592Srgrimes return; 15711592Srgrimes } 15721592Srgrimes if (c->implemented) 15731592Srgrimes reply(214, "Syntax: %s%s %s", type, c->name, c->help); 15741592Srgrimes else 15751592Srgrimes reply(214, "%s%-*s\t%s; unimplemented.", type, width, 15761592Srgrimes c->name, c->help); 15771592Srgrimes} 15781592Srgrimes 15791592Srgrimesstatic void 158090148Simpsizecmd(char *filename) 15811592Srgrimes{ 15821592Srgrimes switch (type) { 15831592Srgrimes case TYPE_L: 15841592Srgrimes case TYPE_I: { 15851592Srgrimes struct stat stbuf; 158663350Sdes if (stat(filename, &stbuf) < 0) 158763350Sdes perror_reply(550, filename); 158863350Sdes else if (!S_ISREG(stbuf.st_mode)) 15891592Srgrimes reply(550, "%s: not a plain file.", filename); 15901592Srgrimes else 1591132929Syar reply(213, "%jd", (intmax_t)stbuf.st_size); 15921592Srgrimes break; } 15931592Srgrimes case TYPE_A: { 15941592Srgrimes FILE *fin; 15951592Srgrimes int c; 15961592Srgrimes off_t count; 15971592Srgrimes struct stat stbuf; 15981592Srgrimes fin = fopen(filename, "r"); 15991592Srgrimes if (fin == NULL) { 16001592Srgrimes perror_reply(550, filename); 16011592Srgrimes return; 16021592Srgrimes } 160363350Sdes if (fstat(fileno(fin), &stbuf) < 0) { 160463350Sdes perror_reply(550, filename); 160563350Sdes (void) fclose(fin); 160663350Sdes return; 160763350Sdes } else if (!S_ISREG(stbuf.st_mode)) { 16081592Srgrimes reply(550, "%s: not a plain file.", filename); 16091592Srgrimes (void) fclose(fin); 16101592Srgrimes return; 1611101034Syar } else if (stbuf.st_size > MAXASIZE) { 1612101034Syar reply(550, "%s: too large for type A SIZE.", filename); 1613101034Syar (void) fclose(fin); 1614101034Syar return; 16151592Srgrimes } 16161592Srgrimes 16171592Srgrimes count = 0; 16181592Srgrimes while((c=getc(fin)) != EOF) { 16191592Srgrimes if (c == '\n') /* will get expanded to \r\n */ 16201592Srgrimes count++; 16211592Srgrimes count++; 16221592Srgrimes } 16231592Srgrimes (void) fclose(fin); 16241592Srgrimes 1625132929Syar reply(213, "%jd", (intmax_t)count); 16261592Srgrimes break; } 16271592Srgrimes default: 1628100684Syar reply(504, "SIZE not implemented for type %s.", 1629100684Syar typenames[type]); 16301592Srgrimes } 16311592Srgrimes} 163256668Sshin 163356668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 163456668Sshinstatic int 163590148Simpport_check(const char *pcmd) 163656668Sshin{ 163756668Sshin if (his_addr.su_family == AF_INET) { 163856668Sshin if (data_dest.su_family != AF_INET) { 163956668Sshin usedefault = 1; 164056668Sshin reply(500, "Invalid address rejected."); 164156668Sshin return 1; 164256668Sshin } 164356668Sshin if (paranoid && 164456668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 164556668Sshin memcmp(&data_dest.su_sin.sin_addr, 164656668Sshin &his_addr.su_sin.sin_addr, 164756668Sshin sizeof(data_dest.su_sin.sin_addr)))) { 164856668Sshin usedefault = 1; 164956668Sshin reply(500, "Illegal PORT range rejected."); 165056668Sshin } else { 165156668Sshin usedefault = 0; 165256668Sshin if (pdata >= 0) { 165356668Sshin (void) close(pdata); 165456668Sshin pdata = -1; 165556668Sshin } 165656668Sshin reply(200, "%s command successful.", pcmd); 165756668Sshin } 165856668Sshin return 1; 165956668Sshin } 166056668Sshin return 0; 166156668Sshin} 166256668Sshin 166370102Sphkstatic int 166490148Simpcheck_login1(void) 166570102Sphk{ 166670102Sphk if (logged_in) 166770102Sphk return 1; 166870102Sphk else { 166970102Sphk reply(530, "Please login with USER and PASS."); 167070102Sphk return 0; 167170102Sphk } 167270102Sphk} 167370102Sphk 1674110340Syar/* 1675110340Syar * Replace leading "~user" in a pathname by the user's login directory. 1676110340Syar * Returned string will be in a freshly malloced buffer unless it's NULL. 1677110340Syar */ 1678110340Syarstatic char * 1679110340Syarexptilde(char *s) 1680110340Syar{ 1681110340Syar char *p, *q; 1682110340Syar char *path, *user; 1683110340Syar struct passwd *ppw; 1684110340Syar 1685110340Syar if ((p = strdup(s)) == NULL) 1686110340Syar return (NULL); 1687110340Syar if (*p != '~') 1688110340Syar return (p); 1689110340Syar 1690110340Syar user = p + 1; /* skip tilde */ 1691110340Syar if ((path = strchr(p, '/')) != NULL) 1692110340Syar *(path++) = '\0'; /* separate ~user from the rest of path */ 1693110378Syar if (*user == '\0') /* no user specified, use the current user */ 1694110378Syar user = pw->pw_name; 1695110378Syar /* read passwd even for the current user since we may be chrooted */ 1696110378Syar if ((ppw = getpwnam(user)) != NULL) { 1697110340Syar /* user found, substitute login directory for ~user */ 1698110340Syar if (path) 1699110340Syar asprintf(&q, "%s/%s", ppw->pw_dir, path); 1700110340Syar else 1701110340Syar q = strdup(ppw->pw_dir); 1702110340Syar free(p); 1703110340Syar p = q; 1704110340Syar } else { 1705110340Syar /* user not found, undo the damage */ 1706110340Syar if (path) 1707110340Syar path[-1] = '/'; 1708110340Syar } 1709110340Syar return (p); 1710110340Syar} 1711110340Syar 1712110340Syar/* 1713110340Syar * Expand glob(3) patterns possibly present in a pathname. 1714110340Syar * Avoid expanding to a pathname including '\r' or '\n' in order to 1715110340Syar * not disrupt the FTP protocol. 1716110340Syar * The expansion found must be unique. 1717229780Suqs * Return the result as a malloced string, or NULL if an error occurred. 1718110340Syar * 1719110340Syar * Problem: this production is used for all pathname 1720110340Syar * processing, but only gives a 550 error reply. 1721110340Syar * This is a valid reply in some cases but not in others. 1722110340Syar */ 1723110340Syarstatic char * 1724110340Syarexpglob(char *s) 1725110340Syar{ 1726110340Syar char *p, **pp, *rval; 1727110340Syar int flags = GLOB_BRACE | GLOB_NOCHECK; 1728110340Syar int n; 1729110340Syar glob_t gl; 1730110340Syar 1731110340Syar memset(&gl, 0, sizeof(gl)); 1732110340Syar flags |= GLOB_LIMIT; 1733110340Syar gl.gl_matchc = MAXGLOBARGS; 1734110340Syar if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) { 1735110340Syar for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++) 1736110340Syar if (*(*pp + strcspn(*pp, "\r\n")) == '\0') { 1737110340Syar p = *pp; 1738110340Syar n++; 1739110340Syar } 1740110340Syar if (n == 0) 1741110340Syar rval = strdup(s); 1742110340Syar else if (n == 1) 1743110340Syar rval = strdup(p); 1744110340Syar else { 1745137852Syar reply(550, "Wildcard is ambiguous."); 1746110340Syar rval = NULL; 1747110340Syar } 1748110340Syar } else { 1749137852Syar reply(550, "Wildcard expansion error."); 1750110340Syar rval = NULL; 1751110340Syar } 1752110340Syar globfree(&gl); 1753110340Syar return (rval); 1754110340Syar} 1755110340Syar 175656668Sshin#ifdef INET6 175756668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 175856668Sshinstatic int 175990148Simpport_check_v6(const char *pcmd) 176056668Sshin{ 176156668Sshin if (his_addr.su_family == AF_INET6) { 176256668Sshin if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 176356668Sshin /* Convert data_dest into v4 mapped sockaddr.*/ 176456668Sshin v4map_data_dest(); 176556668Sshin if (data_dest.su_family != AF_INET6) { 176656668Sshin usedefault = 1; 176756668Sshin reply(500, "Invalid address rejected."); 176856668Sshin return 1; 176956668Sshin } 177056668Sshin if (paranoid && 177156668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 177256668Sshin memcmp(&data_dest.su_sin6.sin6_addr, 177356668Sshin &his_addr.su_sin6.sin6_addr, 177456668Sshin sizeof(data_dest.su_sin6.sin6_addr)))) { 177556668Sshin usedefault = 1; 177656668Sshin reply(500, "Illegal PORT range rejected."); 177756668Sshin } else { 177856668Sshin usedefault = 0; 177956668Sshin if (pdata >= 0) { 178056668Sshin (void) close(pdata); 178156668Sshin pdata = -1; 178256668Sshin } 178356668Sshin reply(200, "%s command successful.", pcmd); 178456668Sshin } 178556668Sshin return 1; 178656668Sshin } 178756668Sshin return 0; 178856668Sshin} 178956668Sshin 179056668Sshinstatic void 179190148Simpv4map_data_dest(void) 179256668Sshin{ 179356668Sshin struct in_addr savedaddr; 179456668Sshin int savedport; 179556668Sshin 179656668Sshin if (data_dest.su_family != AF_INET) { 179756668Sshin usedefault = 1; 179856668Sshin reply(500, "Invalid address rejected."); 179956668Sshin return; 180056668Sshin } 180156668Sshin 180256668Sshin savedaddr = data_dest.su_sin.sin_addr; 180356668Sshin savedport = data_dest.su_port; 180456668Sshin 180556668Sshin memset(&data_dest, 0, sizeof(data_dest)); 180656668Sshin data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 180756668Sshin data_dest.su_sin6.sin6_family = AF_INET6; 180856668Sshin data_dest.su_sin6.sin6_port = savedport; 180956668Sshin memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 181056668Sshin memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 181156668Sshin (caddr_t)&savedaddr, sizeof(savedaddr)); 181256668Sshin} 181356668Sshin#endif 1814