ftpcmd.y revision 168849
11592Srgrimes/* 21592Srgrimes * Copyright (c) 1985, 1988, 1993, 1994 31592Srgrimes * The Regents of the University of California. All rights reserved. 41592Srgrimes * 51592Srgrimes * Redistribution and use in source and binary forms, with or without 61592Srgrimes * modification, are permitted provided that the following conditions 71592Srgrimes * are met: 81592Srgrimes * 1. Redistributions of source code must retain the above copyright 91592Srgrimes * notice, this list of conditions and the following disclaimer. 101592Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111592Srgrimes * notice, this list of conditions and the following disclaimer in the 121592Srgrimes * documentation and/or other materials provided with the distribution. 131592Srgrimes * 3. All advertising materials mentioning features or use of this software 141592Srgrimes * must display the following acknowledgement: 151592Srgrimes * This product includes software developed by the University of 161592Srgrimes * California, Berkeley and its contributors. 171592Srgrimes * 4. Neither the name of the University nor the names of its contributors 181592Srgrimes * may be used to endorse or promote products derived from this software 191592Srgrimes * without specific prior written permission. 201592Srgrimes * 211592Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221592Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231592Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241592Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251592Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261592Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271592Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281592Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291592Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301592Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311592Srgrimes * SUCH DAMAGE. 321592Srgrimes * 331592Srgrimes * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 341592Srgrimes */ 351592Srgrimes 361592Srgrimes/* 371592Srgrimes * Grammar for FTP commands. 381592Srgrimes * See RFC 959. 391592Srgrimes */ 401592Srgrimes 411592Srgrimes%{ 421592Srgrimes 431592Srgrimes#ifndef lint 4431329Scharnier#if 0 451592Srgrimesstatic char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; 4631329Scharnier#endif 471592Srgrimes#endif /* not lint */ 481592Srgrimes 49137859Syar#include <sys/cdefs.h> 50137859Syar__FBSDID("$FreeBSD: head/libexec/ftpd/ftpcmd.y 168849 2007-04-18 22:43:39Z yar $"); 51137859Syar 521592Srgrimes#include <sys/param.h> 531592Srgrimes#include <sys/socket.h> 541592Srgrimes#include <sys/stat.h> 551592Srgrimes 561592Srgrimes#include <netinet/in.h> 571592Srgrimes#include <arpa/ftp.h> 581592Srgrimes 591592Srgrimes#include <ctype.h> 601592Srgrimes#include <errno.h> 611592Srgrimes#include <glob.h> 6292090Smaxim#include <libutil.h> 6392272Smaxim#include <limits.h> 6492090Smaxim#include <md5.h> 6556668Sshin#include <netdb.h> 661592Srgrimes#include <pwd.h> 671592Srgrimes#include <signal.h> 68132929Syar#include <stdint.h> 691592Srgrimes#include <stdio.h> 701592Srgrimes#include <stdlib.h> 711592Srgrimes#include <string.h> 721592Srgrimes#include <syslog.h> 731592Srgrimes#include <time.h> 741592Srgrimes#include <unistd.h> 751592Srgrimes 761592Srgrimes#include "extern.h" 77109380Syar#include "pathnames.h" 781592Srgrimes 7956668Sshinextern union sockunion data_dest, his_addr; 80110037Syarextern int hostinfo; 811592Srgrimesextern int logged_in; 821592Srgrimesextern struct passwd *pw; 831592Srgrimesextern int guest; 84110036Syarextern char *homedir; 8517435Spstextern int paranoid; 861592Srgrimesextern int logging; 871592Srgrimesextern int type; 881592Srgrimesextern int form; 8976096Smarkmextern int ftpdebug; 901592Srgrimesextern int timeout; 911592Srgrimesextern int maxtimeout; 921592Srgrimesextern int pdata; 9327650Sdavidnextern char *hostname; 941592Srgrimesextern char proctitle[]; 951592Srgrimesextern int usedefault; 961592Srgrimesextern char tmpline[]; 9770102Sphkextern int readonly; 98168849Syarextern int assumeutf8; 9970102Sphkextern int noepsv; 10082460Snikextern int noretr; 10182796Ssheldonhextern int noguestretr; 102100684Syarextern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */ 1031592Srgrimes 1041592Srgrimesoff_t restart_point; 1051592Srgrimes 1061592Srgrimesstatic int cmd_type; 1071592Srgrimesstatic int cmd_form; 1081592Srgrimesstatic int cmd_bytesz; 10989935Syarstatic int state; 1101592Srgrimeschar cbuf[512]; 111132931Syarchar *fromname = NULL; 1121592Srgrimes 11356668Sshinextern int epsvall; 11456668Sshin 1151592Srgrimes%} 1161592Srgrimes 1171592Srgrimes%union { 11892272Smaxim struct { 11992272Smaxim off_t o; 12092272Smaxim int i; 12192272Smaxim } u; 1221592Srgrimes char *s; 1231592Srgrimes} 1241592Srgrimes 1251592Srgrimes%token 1261592Srgrimes A B C E F I 1271592Srgrimes L N P R S T 12856668Sshin ALL 1291592Srgrimes 1301592Srgrimes SP CRLF COMMA 1311592Srgrimes 1321592Srgrimes USER PASS ACCT REIN QUIT PORT 1331592Srgrimes PASV TYPE STRU MODE RETR STOR 1341592Srgrimes APPE MLFL MAIL MSND MSOM MSAM 1351592Srgrimes MRSQ MRCP ALLO REST RNFR RNTO 1361592Srgrimes ABOR DELE CWD LIST NLST SITE 1371592Srgrimes STAT HELP NOOP MKD RMD PWD 1381592Srgrimes CDUP STOU SMNT SYST SIZE MDTM 139168849Syar LPRT LPSV EPRT EPSV FEAT 1401592Srgrimes 14175535Sphk UMASK IDLE CHMOD MDFIVE 1421592Srgrimes 143102565Syar LEXERR NOTIMPL 1441592Srgrimes 1451592Srgrimes%token <s> STRING 14692272Smaxim%token <u> NUMBER 1471592Srgrimes 14892272Smaxim%type <u.i> check_login octal_number byte_size 14992272Smaxim%type <u.i> check_login_ro check_login_epsv 15092272Smaxim%type <u.i> struct_code mode_code type_code form_code 15175567Speter%type <s> pathstring pathname password username 152102565Syar%type <s> ALL NOTIMPL 1531592Srgrimes 1541592Srgrimes%start cmd_list 1551592Srgrimes 1561592Srgrimes%% 1571592Srgrimes 1581592Srgrimescmd_list 1591592Srgrimes : /* empty */ 1601592Srgrimes | cmd_list cmd 1611592Srgrimes { 16288935Sdwmalone if (fromname) 16388935Sdwmalone free(fromname); 164132931Syar fromname = NULL; 165132930Syar restart_point = 0; 1661592Srgrimes } 1671592Srgrimes | cmd_list rcmd 1681592Srgrimes ; 1691592Srgrimes 1701592Srgrimescmd 1711592Srgrimes : USER SP username CRLF 1721592Srgrimes { 1731592Srgrimes user($3); 1741592Srgrimes free($3); 1751592Srgrimes } 1761592Srgrimes | PASS SP password CRLF 1771592Srgrimes { 1781592Srgrimes pass($3); 1791592Srgrimes free($3); 1801592Srgrimes } 18175556Sgreen | PASS CRLF 18275556Sgreen { 18375556Sgreen pass(""); 18475556Sgreen } 18517433Spst | PORT check_login SP host_port CRLF 1861592Srgrimes { 18756668Sshin if (epsvall) { 188137852Syar reply(501, "No PORT allowed after EPSV ALL."); 18956668Sshin goto port_done; 19056668Sshin } 19156668Sshin if (!$2) 19256668Sshin goto port_done; 19356668Sshin if (port_check("PORT") == 1) 19456668Sshin goto port_done; 19556668Sshin#ifdef INET6 19656668Sshin if ((his_addr.su_family != AF_INET6 || 19756668Sshin !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { 19856668Sshin /* shoud never happen */ 19956668Sshin usedefault = 1; 20056668Sshin reply(500, "Invalid address rejected."); 20156668Sshin goto port_done; 20256668Sshin } 20356668Sshin port_check_v6("pcmd"); 20456668Sshin#endif 20556668Sshin port_done: 206132925Syar ; 20756668Sshin } 20856668Sshin | LPRT check_login SP host_long_port CRLF 20956668Sshin { 21056668Sshin if (epsvall) { 211137852Syar reply(501, "No LPRT allowed after EPSV ALL."); 21256668Sshin goto lprt_done; 21356668Sshin } 21456668Sshin if (!$2) 21556668Sshin goto lprt_done; 21656668Sshin if (port_check("LPRT") == 1) 21756668Sshin goto lprt_done; 21856668Sshin#ifdef INET6 21956668Sshin if (his_addr.su_family != AF_INET6) { 22056668Sshin usedefault = 1; 22156668Sshin reply(500, "Invalid address rejected."); 22256668Sshin goto lprt_done; 22356668Sshin } 22456668Sshin if (port_check_v6("LPRT") == 1) 22556668Sshin goto lprt_done; 22656668Sshin#endif 22756668Sshin lprt_done: 228132925Syar ; 22956668Sshin } 23056668Sshin | EPRT check_login SP STRING CRLF 23156668Sshin { 23256668Sshin char delim; 23356668Sshin char *tmp = NULL; 23456668Sshin char *p, *q; 23556668Sshin char *result[3]; 23656668Sshin struct addrinfo hints; 23756668Sshin struct addrinfo *res; 23856668Sshin int i; 23956668Sshin 24056668Sshin if (epsvall) { 241137852Syar reply(501, "No EPRT allowed after EPSV ALL."); 24256668Sshin goto eprt_done; 24356668Sshin } 24456668Sshin if (!$2) 24556668Sshin goto eprt_done; 24656668Sshin 24756668Sshin memset(&data_dest, 0, sizeof(data_dest)); 24856668Sshin tmp = strdup($4); 24976096Smarkm if (ftpdebug) 25056668Sshin syslog(LOG_DEBUG, "%s", tmp); 25156668Sshin if (!tmp) { 25276096Smarkm fatalerror("not enough core"); 25356668Sshin /*NOTREACHED*/ 25456668Sshin } 25556668Sshin p = tmp; 25656668Sshin delim = p[0]; 25756668Sshin p++; 25856668Sshin memset(result, 0, sizeof(result)); 25956668Sshin for (i = 0; i < 3; i++) { 26056668Sshin q = strchr(p, delim); 26156668Sshin if (!q || *q != delim) { 26256668Sshin parsefail: 26356668Sshin reply(500, 26456668Sshin "Invalid argument, rejected."); 26556668Sshin if (tmp) 26656668Sshin free(tmp); 26717433Spst usedefault = 1; 26856668Sshin goto eprt_done; 26917433Spst } 27056668Sshin *q++ = '\0'; 27156668Sshin result[i] = p; 27276096Smarkm if (ftpdebug) 27356668Sshin syslog(LOG_DEBUG, "%d: %s", i, p); 27456668Sshin p = q; 2751592Srgrimes } 27656668Sshin 27756668Sshin /* some more sanity check */ 27856668Sshin p = result[0]; 27956668Sshin while (*p) { 28056668Sshin if (!isdigit(*p)) 28156668Sshin goto parsefail; 28256668Sshin p++; 28356668Sshin } 28456668Sshin p = result[2]; 28556668Sshin while (*p) { 28656668Sshin if (!isdigit(*p)) 28756668Sshin goto parsefail; 28856668Sshin p++; 28956668Sshin } 29056668Sshin 29156668Sshin /* grab address */ 29256668Sshin memset(&hints, 0, sizeof(hints)); 29356668Sshin if (atoi(result[0]) == 1) 29456668Sshin hints.ai_family = PF_INET; 29556668Sshin#ifdef INET6 29656668Sshin else if (atoi(result[0]) == 2) 29756668Sshin hints.ai_family = PF_INET6; 29856668Sshin#endif 29956668Sshin else 30056668Sshin hints.ai_family = PF_UNSPEC; /*XXX*/ 30156668Sshin hints.ai_socktype = SOCK_STREAM; 30256668Sshin i = getaddrinfo(result[1], result[2], &hints, &res); 30356668Sshin if (i) 30456668Sshin goto parsefail; 30556668Sshin memcpy(&data_dest, res->ai_addr, res->ai_addrlen); 30656668Sshin#ifdef INET6 30756668Sshin if (his_addr.su_family == AF_INET6 30856668Sshin && data_dest.su_family == AF_INET6) { 30956668Sshin /* XXX more sanity checks! */ 31056668Sshin data_dest.su_sin6.sin6_scope_id = 31156668Sshin his_addr.su_sin6.sin6_scope_id; 31256668Sshin } 31356668Sshin#endif 31456668Sshin free(tmp); 31556668Sshin tmp = NULL; 31656668Sshin 31756668Sshin if (port_check("EPRT") == 1) 31856668Sshin goto eprt_done; 31956668Sshin#ifdef INET6 32056668Sshin if (his_addr.su_family != AF_INET6) { 32156668Sshin usedefault = 1; 32256668Sshin reply(500, "Invalid address rejected."); 32356668Sshin goto eprt_done; 32456668Sshin } 32556668Sshin if (port_check_v6("EPRT") == 1) 32656668Sshin goto eprt_done; 32756668Sshin#endif 32888935Sdwmalone eprt_done: 32988935Sdwmalone free($4); 3301592Srgrimes } 33117433Spst | PASV check_login CRLF 3321592Srgrimes { 33356668Sshin if (epsvall) 334137852Syar reply(501, "No PASV allowed after EPSV ALL."); 33556668Sshin else if ($2) 33617433Spst passive(); 3371592Srgrimes } 33856668Sshin | LPSV check_login CRLF 33956668Sshin { 34056668Sshin if (epsvall) 341137852Syar reply(501, "No LPSV allowed after EPSV ALL."); 34256668Sshin else if ($2) 34356668Sshin long_passive("LPSV", PF_UNSPEC); 34456668Sshin } 34570102Sphk | EPSV check_login_epsv SP NUMBER CRLF 34656668Sshin { 34756668Sshin if ($2) { 34856668Sshin int pf; 34992272Smaxim switch ($4.i) { 35056668Sshin case 1: 35156668Sshin pf = PF_INET; 35256668Sshin break; 35356668Sshin#ifdef INET6 35456668Sshin case 2: 35556668Sshin pf = PF_INET6; 35656668Sshin break; 35756668Sshin#endif 35856668Sshin default: 35956668Sshin pf = -1; /*junk value*/ 36056668Sshin break; 36156668Sshin } 36256668Sshin long_passive("EPSV", pf); 36356668Sshin } 36456668Sshin } 36570102Sphk | EPSV check_login_epsv SP ALL CRLF 36656668Sshin { 36756668Sshin if ($2) { 368137852Syar reply(200, "EPSV ALL command successful."); 36956668Sshin epsvall++; 37056668Sshin } 37156668Sshin } 37270102Sphk | EPSV check_login_epsv CRLF 37356668Sshin { 37456668Sshin if ($2) 37556668Sshin long_passive("EPSV", PF_UNSPEC); 37656668Sshin } 37771278Sjedgar | TYPE check_login SP type_code CRLF 3781592Srgrimes { 37971278Sjedgar if ($2) { 38071278Sjedgar switch (cmd_type) { 3811592Srgrimes 38271278Sjedgar case TYPE_A: 38371278Sjedgar if (cmd_form == FORM_N) { 38471278Sjedgar reply(200, "Type set to A."); 38571278Sjedgar type = cmd_type; 38671278Sjedgar form = cmd_form; 38771278Sjedgar } else 38871278Sjedgar reply(504, "Form must be N."); 38971278Sjedgar break; 3901592Srgrimes 39171278Sjedgar case TYPE_E: 39271278Sjedgar reply(504, "Type E not implemented."); 39371278Sjedgar break; 3941592Srgrimes 39571278Sjedgar case TYPE_I: 39671278Sjedgar reply(200, "Type set to I."); 39771278Sjedgar type = cmd_type; 39871278Sjedgar break; 3991592Srgrimes 40071278Sjedgar case TYPE_L: 401103949Smike#if CHAR_BIT == 8 40271278Sjedgar if (cmd_bytesz == 8) { 40371278Sjedgar reply(200, 40471278Sjedgar "Type set to L (byte size 8)."); 40571278Sjedgar type = cmd_type; 40671278Sjedgar } else 40771278Sjedgar reply(504, "Byte size must be 8."); 408103949Smike#else /* CHAR_BIT == 8 */ 409103949Smike UNIMPLEMENTED for CHAR_BIT != 8 410103949Smike#endif /* CHAR_BIT == 8 */ 41171278Sjedgar } 4121592Srgrimes } 4131592Srgrimes } 41471278Sjedgar | STRU check_login SP struct_code CRLF 4151592Srgrimes { 41671278Sjedgar if ($2) { 41771278Sjedgar switch ($4) { 4181592Srgrimes 41971278Sjedgar case STRU_F: 420137852Syar reply(200, "STRU F accepted."); 42171278Sjedgar break; 4221592Srgrimes 42371278Sjedgar default: 42471278Sjedgar reply(504, "Unimplemented STRU type."); 42571278Sjedgar } 4261592Srgrimes } 4271592Srgrimes } 42871278Sjedgar | MODE check_login SP mode_code CRLF 4291592Srgrimes { 43071278Sjedgar if ($2) { 43171278Sjedgar switch ($4) { 4321592Srgrimes 43371278Sjedgar case MODE_S: 434137852Syar reply(200, "MODE S accepted."); 43571278Sjedgar break; 43671278Sjedgar 43771278Sjedgar default: 43871278Sjedgar reply(502, "Unimplemented MODE type."); 43971278Sjedgar } 4401592Srgrimes } 4411592Srgrimes } 44271278Sjedgar | ALLO check_login SP NUMBER CRLF 4431592Srgrimes { 44471278Sjedgar if ($2) { 44571278Sjedgar reply(202, "ALLO command ignored."); 44671278Sjedgar } 4471592Srgrimes } 44871278Sjedgar | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 4491592Srgrimes { 45071278Sjedgar if ($2) { 45171278Sjedgar reply(202, "ALLO command ignored."); 45271278Sjedgar } 4531592Srgrimes } 4541592Srgrimes | RETR check_login SP pathname CRLF 4551592Srgrimes { 45682796Ssheldonh if (noretr || (guest && noguestretr)) 457137852Syar reply(500, "RETR command disabled."); 45882460Snik else if ($2 && $4 != NULL) 459132931Syar retrieve(NULL, $4); 46082460Snik 4611592Srgrimes if ($4 != NULL) 4621592Srgrimes free($4); 4631592Srgrimes } 46470102Sphk | STOR check_login_ro SP pathname CRLF 4651592Srgrimes { 4661592Srgrimes if ($2 && $4 != NULL) 4671592Srgrimes store($4, "w", 0); 4681592Srgrimes if ($4 != NULL) 4691592Srgrimes free($4); 4701592Srgrimes } 47170102Sphk | APPE check_login_ro SP pathname CRLF 4721592Srgrimes { 4731592Srgrimes if ($2 && $4 != NULL) 4741592Srgrimes store($4, "a", 0); 4751592Srgrimes if ($4 != NULL) 4761592Srgrimes free($4); 4771592Srgrimes } 4781592Srgrimes | NLST check_login CRLF 4791592Srgrimes { 4801592Srgrimes if ($2) 4811592Srgrimes send_file_list("."); 4821592Srgrimes } 483101395Syar | NLST check_login SP pathstring CRLF 4841592Srgrimes { 485101395Syar if ($2) 4861592Srgrimes send_file_list($4); 487101395Syar free($4); 4881592Srgrimes } 4891592Srgrimes | LIST check_login CRLF 4901592Srgrimes { 4911592Srgrimes if ($2) 492109380Syar retrieve(_PATH_LS " -lgA", ""); 4931592Srgrimes } 49475567Speter | LIST check_login SP pathstring CRLF 4951592Srgrimes { 496101395Syar if ($2) 497109380Syar retrieve(_PATH_LS " -lgA %s", $4); 498101395Syar free($4); 4991592Srgrimes } 5001592Srgrimes | STAT check_login SP pathname CRLF 5011592Srgrimes { 5021592Srgrimes if ($2 && $4 != NULL) 5031592Srgrimes statfilecmd($4); 5041592Srgrimes if ($4 != NULL) 5051592Srgrimes free($4); 5061592Srgrimes } 50771278Sjedgar | STAT check_login CRLF 5081592Srgrimes { 50971278Sjedgar if ($2) { 51071278Sjedgar statcmd(); 51171278Sjedgar } 5121592Srgrimes } 51370102Sphk | DELE check_login_ro SP pathname CRLF 5141592Srgrimes { 5151592Srgrimes if ($2 && $4 != NULL) 5161592Srgrimes delete($4); 5171592Srgrimes if ($4 != NULL) 5181592Srgrimes free($4); 5191592Srgrimes } 52070102Sphk | RNTO check_login_ro SP pathname CRLF 5211592Srgrimes { 522101379Syar if ($2 && $4 != NULL) { 52317433Spst if (fromname) { 52417433Spst renamecmd(fromname, $4); 52517433Spst free(fromname); 526132931Syar fromname = NULL; 52717433Spst } else { 52817433Spst reply(503, "Bad sequence of commands."); 52917433Spst } 5301592Srgrimes } 531101379Syar if ($4 != NULL) 532101379Syar free($4); 5331592Srgrimes } 53471278Sjedgar | ABOR check_login CRLF 5351592Srgrimes { 53671278Sjedgar if ($2) 53771278Sjedgar reply(225, "ABOR command successful."); 5381592Srgrimes } 5391592Srgrimes | CWD check_login CRLF 5401592Srgrimes { 54169234Sdanny if ($2) { 542110036Syar cwd(homedir); 54369234Sdanny } 5441592Srgrimes } 5451592Srgrimes | CWD check_login SP pathname CRLF 5461592Srgrimes { 5471592Srgrimes if ($2 && $4 != NULL) 5481592Srgrimes cwd($4); 5491592Srgrimes if ($4 != NULL) 5501592Srgrimes free($4); 5511592Srgrimes } 5521592Srgrimes | HELP CRLF 5531592Srgrimes { 554132931Syar help(cmdtab, NULL); 5551592Srgrimes } 5561592Srgrimes | HELP SP STRING CRLF 5571592Srgrimes { 5581592Srgrimes char *cp = $3; 5591592Srgrimes 5601592Srgrimes if (strncasecmp(cp, "SITE", 4) == 0) { 5611592Srgrimes cp = $3 + 4; 5621592Srgrimes if (*cp == ' ') 5631592Srgrimes cp++; 5641592Srgrimes if (*cp) 5651592Srgrimes help(sitetab, cp); 5661592Srgrimes else 567132931Syar help(sitetab, NULL); 5681592Srgrimes } else 5691592Srgrimes help(cmdtab, $3); 57088935Sdwmalone free($3); 5711592Srgrimes } 5721592Srgrimes | NOOP CRLF 5731592Srgrimes { 5741592Srgrimes reply(200, "NOOP command successful."); 5751592Srgrimes } 57670102Sphk | MKD check_login_ro SP pathname CRLF 5771592Srgrimes { 5781592Srgrimes if ($2 && $4 != NULL) 5791592Srgrimes makedir($4); 5801592Srgrimes if ($4 != NULL) 5811592Srgrimes free($4); 5821592Srgrimes } 58370102Sphk | RMD check_login_ro SP pathname CRLF 5841592Srgrimes { 5851592Srgrimes if ($2 && $4 != NULL) 5861592Srgrimes removedir($4); 5871592Srgrimes if ($4 != NULL) 5881592Srgrimes free($4); 5891592Srgrimes } 5901592Srgrimes | PWD check_login CRLF 5911592Srgrimes { 5921592Srgrimes if ($2) 5931592Srgrimes pwd(); 5941592Srgrimes } 5951592Srgrimes | CDUP check_login CRLF 5961592Srgrimes { 5971592Srgrimes if ($2) 5981592Srgrimes cwd(".."); 5991592Srgrimes } 6001592Srgrimes | SITE SP HELP CRLF 6011592Srgrimes { 602132931Syar help(sitetab, NULL); 6031592Srgrimes } 6041592Srgrimes | SITE SP HELP SP STRING CRLF 6051592Srgrimes { 6061592Srgrimes help(sitetab, $5); 60788935Sdwmalone free($5); 6081592Srgrimes } 60975535Sphk | SITE SP MDFIVE check_login SP pathname CRLF 61075535Sphk { 61175535Sphk char p[64], *q; 61275535Sphk 613101379Syar if ($4 && $6) { 61475535Sphk q = MD5File($6, p); 61575535Sphk if (q != NULL) 61675535Sphk reply(200, "MD5(%s) = %s", $6, p); 61775535Sphk else 61875535Sphk perror_reply(550, $6); 61975535Sphk } 62088935Sdwmalone if ($6) 62188935Sdwmalone free($6); 62275535Sphk } 6231592Srgrimes | SITE SP UMASK check_login CRLF 6241592Srgrimes { 6251592Srgrimes int oldmask; 6261592Srgrimes 6271592Srgrimes if ($4) { 6281592Srgrimes oldmask = umask(0); 6291592Srgrimes (void) umask(oldmask); 630137852Syar reply(200, "Current UMASK is %03o.", oldmask); 6311592Srgrimes } 6321592Srgrimes } 6331592Srgrimes | SITE SP UMASK check_login SP octal_number CRLF 6341592Srgrimes { 6351592Srgrimes int oldmask; 6361592Srgrimes 6371592Srgrimes if ($4) { 6381592Srgrimes if (($6 == -1) || ($6 > 0777)) { 639137852Syar reply(501, "Bad UMASK value."); 6401592Srgrimes } else { 6411592Srgrimes oldmask = umask($6); 6421592Srgrimes reply(200, 643137852Syar "UMASK set to %03o (was %03o).", 6441592Srgrimes $6, oldmask); 6451592Srgrimes } 6461592Srgrimes } 6471592Srgrimes } 64870102Sphk | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF 6491592Srgrimes { 6501592Srgrimes if ($4 && ($8 != NULL)) { 651101378Syar if (($6 == -1 ) || ($6 > 0777)) 652137852Syar reply(501, "Bad mode value."); 6531592Srgrimes else if (chmod($8, $6) < 0) 6541592Srgrimes perror_reply(550, $8); 6551592Srgrimes else 6561592Srgrimes reply(200, "CHMOD command successful."); 6571592Srgrimes } 6581592Srgrimes if ($8 != NULL) 6591592Srgrimes free($8); 6601592Srgrimes } 66171278Sjedgar | SITE SP check_login IDLE CRLF 6621592Srgrimes { 66371278Sjedgar if ($3) 66471278Sjedgar reply(200, 665137852Syar "Current IDLE time limit is %d seconds; max %d.", 66671278Sjedgar timeout, maxtimeout); 6671592Srgrimes } 66871278Sjedgar | SITE SP check_login IDLE SP NUMBER CRLF 6691592Srgrimes { 67071278Sjedgar if ($3) { 67192272Smaxim if ($6.i < 30 || $6.i > maxtimeout) { 67271278Sjedgar reply(501, 673137852Syar "Maximum IDLE time must be between 30 and %d seconds.", 67471278Sjedgar maxtimeout); 67571278Sjedgar } else { 67692272Smaxim timeout = $6.i; 677137659Syar (void) alarm(timeout); 67871278Sjedgar reply(200, 679137852Syar "Maximum IDLE time set to %d seconds.", 68071278Sjedgar timeout); 68171278Sjedgar } 6821592Srgrimes } 6831592Srgrimes } 68470102Sphk | STOU check_login_ro SP pathname CRLF 6851592Srgrimes { 6861592Srgrimes if ($2 && $4 != NULL) 6871592Srgrimes store($4, "w", 1); 6881592Srgrimes if ($4 != NULL) 6891592Srgrimes free($4); 6901592Srgrimes } 691168849Syar | FEAT CRLF 692168849Syar { 693168849Syar lreply(211, "Extensions supported:"); 694168849Syar#if 0 695168849Syar /* XXX these two keywords are non-standard */ 696168849Syar printf(" EPRT\r\n"); 697168849Syar if (!noepsv) 698168849Syar printf(" EPSV\r\n"); 699168849Syar#endif 700168849Syar printf(" MDTM\r\n"); 701168849Syar printf(" REST STREAM\r\n"); 702168849Syar printf(" SIZE\r\n"); 703168849Syar if (assumeutf8) { 704168849Syar /* TVFS requires UTF8, see RFC 3659 */ 705168849Syar printf(" TVFS\r\n"); 706168849Syar printf(" UTF8\r\n"); 707168849Syar } 708168849Syar reply(211, "End."); 709168849Syar } 71071278Sjedgar | SYST check_login CRLF 7111592Srgrimes { 712116439Syar if ($2) { 713116439Syar if (hostinfo) 7141592Srgrimes#ifdef BSD 715116439Syar reply(215, "UNIX Type: L%d Version: BSD-%d", 716116439Syar CHAR_BIT, BSD); 7171592Srgrimes#else /* BSD */ 718116439Syar reply(215, "UNIX Type: L%d", CHAR_BIT); 7191592Srgrimes#endif /* BSD */ 720116439Syar else 721116439Syar reply(215, "UNKNOWN Type: L%d", CHAR_BIT); 722116439Syar } 7231592Srgrimes } 7241592Srgrimes 7251592Srgrimes /* 7261592Srgrimes * SIZE is not in RFC959, but Postel has blessed it and 7271592Srgrimes * it will be in the updated RFC. 7281592Srgrimes * 7291592Srgrimes * Return size of file in a format suitable for 7301592Srgrimes * using with RESTART (we just count bytes). 7311592Srgrimes */ 7321592Srgrimes | SIZE check_login SP pathname CRLF 7331592Srgrimes { 7341592Srgrimes if ($2 && $4 != NULL) 7351592Srgrimes sizecmd($4); 7361592Srgrimes if ($4 != NULL) 7371592Srgrimes free($4); 7381592Srgrimes } 7391592Srgrimes 7401592Srgrimes /* 7411592Srgrimes * MDTM is not in RFC959, but Postel has blessed it and 7421592Srgrimes * it will be in the updated RFC. 7431592Srgrimes * 7441592Srgrimes * Return modification time of file as an ISO 3307 7451592Srgrimes * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 7461592Srgrimes * where xxx is the fractional second (of any precision, 7471592Srgrimes * not necessarily 3 digits) 7481592Srgrimes */ 7491592Srgrimes | MDTM check_login SP pathname CRLF 7501592Srgrimes { 7511592Srgrimes if ($2 && $4 != NULL) { 7521592Srgrimes struct stat stbuf; 7531592Srgrimes if (stat($4, &stbuf) < 0) 754137850Syar perror_reply(550, $4); 7551592Srgrimes else if (!S_ISREG(stbuf.st_mode)) { 7561592Srgrimes reply(550, "%s: not a plain file.", $4); 7571592Srgrimes } else { 7581592Srgrimes struct tm *t; 7591592Srgrimes t = gmtime(&stbuf.st_mtime); 7601592Srgrimes reply(213, 76117435Spst "%04d%02d%02d%02d%02d%02d", 76217435Spst 1900 + t->tm_year, 76317435Spst t->tm_mon+1, t->tm_mday, 7641592Srgrimes t->tm_hour, t->tm_min, t->tm_sec); 7651592Srgrimes } 7661592Srgrimes } 7671592Srgrimes if ($4 != NULL) 7681592Srgrimes free($4); 7691592Srgrimes } 7701592Srgrimes | QUIT CRLF 7711592Srgrimes { 7721592Srgrimes reply(221, "Goodbye."); 7731592Srgrimes dologout(0); 7741592Srgrimes } 775102565Syar | NOTIMPL 776102565Syar { 777102565Syar nack($1); 778102565Syar } 77989935Syar | error 7801592Srgrimes { 78189935Syar yyclearin; /* discard lookahead data */ 78289935Syar yyerrok; /* clear error condition */ 783102565Syar state = CMD; /* reset lexer state */ 7841592Srgrimes } 7851592Srgrimes ; 7861592Srgrimesrcmd 78770102Sphk : RNFR check_login_ro SP pathname CRLF 7881592Srgrimes { 789132930Syar restart_point = 0; 7901592Srgrimes if ($2 && $4) { 79188935Sdwmalone if (fromname) 79288935Sdwmalone free(fromname); 793132931Syar fromname = NULL; 79488935Sdwmalone if (renamefrom($4)) 79588935Sdwmalone fromname = $4; 79688935Sdwmalone else 7971592Srgrimes free($4); 79888935Sdwmalone } else if ($4) { 79988935Sdwmalone free($4); 8001592Srgrimes } 8011592Srgrimes } 80292272Smaxim | REST check_login SP NUMBER CRLF 8031592Srgrimes { 80471278Sjedgar if ($2) { 80588935Sdwmalone if (fromname) 80688935Sdwmalone free(fromname); 807132931Syar fromname = NULL; 80892272Smaxim restart_point = $4.o; 809132929Syar reply(350, "Restarting at %jd. %s", 810132929Syar (intmax_t)restart_point, 81171278Sjedgar "Send STORE or RETRIEVE to initiate transfer."); 81271278Sjedgar } 8131592Srgrimes } 8141592Srgrimes ; 8151592Srgrimes 8161592Srgrimesusername 8171592Srgrimes : STRING 8181592Srgrimes ; 8191592Srgrimes 8201592Srgrimespassword 8211592Srgrimes : /* empty */ 8221592Srgrimes { 8231592Srgrimes $$ = (char *)calloc(1, sizeof(char)); 8241592Srgrimes } 8251592Srgrimes | STRING 8261592Srgrimes ; 8271592Srgrimes 8281592Srgrimesbyte_size 8291592Srgrimes : NUMBER 83092272Smaxim { 83192272Smaxim $$ = $1.i; 83292272Smaxim } 8331592Srgrimes ; 8341592Srgrimes 8351592Srgrimeshost_port 8361592Srgrimes : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 8371592Srgrimes NUMBER COMMA NUMBER 8381592Srgrimes { 8391592Srgrimes char *a, *p; 8401592Srgrimes 84156668Sshin data_dest.su_len = sizeof(struct sockaddr_in); 84256668Sshin data_dest.su_family = AF_INET; 84356668Sshin p = (char *)&data_dest.su_sin.sin_port; 84492272Smaxim p[0] = $9.i; p[1] = $11.i; 84556668Sshin a = (char *)&data_dest.su_sin.sin_addr; 84692272Smaxim a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; 8471592Srgrimes } 8481592Srgrimes ; 8491592Srgrimes 85056668Sshinhost_long_port 85156668Sshin : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 85256668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 85356668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 85456668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 85556668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 85656668Sshin NUMBER 85756668Sshin { 85856668Sshin char *a, *p; 85956668Sshin 86056668Sshin memset(&data_dest, 0, sizeof(data_dest)); 86156668Sshin data_dest.su_len = sizeof(struct sockaddr_in6); 86256668Sshin data_dest.su_family = AF_INET6; 86356668Sshin p = (char *)&data_dest.su_port; 86492272Smaxim p[0] = $39.i; p[1] = $41.i; 86556668Sshin a = (char *)&data_dest.su_sin6.sin6_addr; 86692272Smaxim a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 86792272Smaxim a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i; 86892272Smaxim a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i; 86992272Smaxim a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i; 87056668Sshin if (his_addr.su_family == AF_INET6) { 87156668Sshin /* XXX more sanity checks! */ 87256668Sshin data_dest.su_sin6.sin6_scope_id = 87356668Sshin his_addr.su_sin6.sin6_scope_id; 87456668Sshin } 87592272Smaxim if ($1.i != 6 || $3.i != 16 || $37.i != 2) 87656668Sshin memset(&data_dest, 0, sizeof(data_dest)); 87756668Sshin } 87856668Sshin | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 87956668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 88056668Sshin NUMBER 88156668Sshin { 88256668Sshin char *a, *p; 88356668Sshin 88456668Sshin memset(&data_dest, 0, sizeof(data_dest)); 88556668Sshin data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 88656668Sshin data_dest.su_family = AF_INET; 88756668Sshin p = (char *)&data_dest.su_port; 88892272Smaxim p[0] = $15.i; p[1] = $17.i; 88956668Sshin a = (char *)&data_dest.su_sin.sin_addr; 89092272Smaxim a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 89192272Smaxim if ($1.i != 4 || $3.i != 4 || $13.i != 2) 89256668Sshin memset(&data_dest, 0, sizeof(data_dest)); 89356668Sshin } 89456668Sshin ; 89556668Sshin 8961592Srgrimesform_code 8971592Srgrimes : N 8981592Srgrimes { 8991592Srgrimes $$ = FORM_N; 9001592Srgrimes } 9011592Srgrimes | T 9021592Srgrimes { 9031592Srgrimes $$ = FORM_T; 9041592Srgrimes } 9051592Srgrimes | C 9061592Srgrimes { 9071592Srgrimes $$ = FORM_C; 9081592Srgrimes } 9091592Srgrimes ; 9101592Srgrimes 9111592Srgrimestype_code 9121592Srgrimes : A 9131592Srgrimes { 9141592Srgrimes cmd_type = TYPE_A; 9151592Srgrimes cmd_form = FORM_N; 9161592Srgrimes } 9171592Srgrimes | A SP form_code 9181592Srgrimes { 9191592Srgrimes cmd_type = TYPE_A; 9201592Srgrimes cmd_form = $3; 9211592Srgrimes } 9221592Srgrimes | E 9231592Srgrimes { 9241592Srgrimes cmd_type = TYPE_E; 9251592Srgrimes cmd_form = FORM_N; 9261592Srgrimes } 9271592Srgrimes | E SP form_code 9281592Srgrimes { 9291592Srgrimes cmd_type = TYPE_E; 9301592Srgrimes cmd_form = $3; 9311592Srgrimes } 9321592Srgrimes | I 9331592Srgrimes { 9341592Srgrimes cmd_type = TYPE_I; 9351592Srgrimes } 9361592Srgrimes | L 9371592Srgrimes { 9381592Srgrimes cmd_type = TYPE_L; 939103949Smike cmd_bytesz = CHAR_BIT; 9401592Srgrimes } 9411592Srgrimes | L SP byte_size 9421592Srgrimes { 9431592Srgrimes cmd_type = TYPE_L; 9441592Srgrimes cmd_bytesz = $3; 9451592Srgrimes } 9461592Srgrimes /* this is for a bug in the BBN ftp */ 9471592Srgrimes | L byte_size 9481592Srgrimes { 9491592Srgrimes cmd_type = TYPE_L; 9501592Srgrimes cmd_bytesz = $2; 9511592Srgrimes } 9521592Srgrimes ; 9531592Srgrimes 9541592Srgrimesstruct_code 9551592Srgrimes : F 9561592Srgrimes { 9571592Srgrimes $$ = STRU_F; 9581592Srgrimes } 9591592Srgrimes | R 9601592Srgrimes { 9611592Srgrimes $$ = STRU_R; 9621592Srgrimes } 9631592Srgrimes | P 9641592Srgrimes { 9651592Srgrimes $$ = STRU_P; 9661592Srgrimes } 9671592Srgrimes ; 9681592Srgrimes 9691592Srgrimesmode_code 9701592Srgrimes : S 9711592Srgrimes { 9721592Srgrimes $$ = MODE_S; 9731592Srgrimes } 9741592Srgrimes | B 9751592Srgrimes { 9761592Srgrimes $$ = MODE_B; 9771592Srgrimes } 9781592Srgrimes | C 9791592Srgrimes { 9801592Srgrimes $$ = MODE_C; 9811592Srgrimes } 9821592Srgrimes ; 9831592Srgrimes 9841592Srgrimespathname 9851592Srgrimes : pathstring 9861592Srgrimes { 98775567Speter if (logged_in && $1) { 988110340Syar char *p; 9891592Srgrimes 990110340Syar /* 991110340Syar * Expand ~user manually since glob(3) 992110340Syar * will return the unexpanded pathname 993110340Syar * if the corresponding file/directory 994110340Syar * doesn't exist yet. Using sole glob(3) 995110340Syar * would break natural commands like 996110340Syar * MKD ~user/newdir 997110340Syar * or 998110340Syar * RNTO ~/newfile 999110340Syar */ 1000110340Syar if ((p = exptilde($1)) != NULL) { 1001110340Syar $$ = expglob(p); 1002110340Syar free(p); 1003110340Syar } else 10041592Srgrimes $$ = NULL; 10051592Srgrimes free($1); 10061592Srgrimes } else 10071592Srgrimes $$ = $1; 10081592Srgrimes } 10091592Srgrimes ; 10101592Srgrimes 10111592Srgrimespathstring 10121592Srgrimes : STRING 10131592Srgrimes ; 10141592Srgrimes 10151592Srgrimesoctal_number 10161592Srgrimes : NUMBER 10171592Srgrimes { 10181592Srgrimes int ret, dec, multby, digit; 10191592Srgrimes 10201592Srgrimes /* 10211592Srgrimes * Convert a number that was read as decimal number 10221592Srgrimes * to what it would be if it had been read as octal. 10231592Srgrimes */ 102492272Smaxim dec = $1.i; 10251592Srgrimes multby = 1; 10261592Srgrimes ret = 0; 10271592Srgrimes while (dec) { 10281592Srgrimes digit = dec%10; 10291592Srgrimes if (digit > 7) { 10301592Srgrimes ret = -1; 10311592Srgrimes break; 10321592Srgrimes } 10331592Srgrimes ret += digit * multby; 10341592Srgrimes multby *= 8; 10351592Srgrimes dec /= 10; 10361592Srgrimes } 10371592Srgrimes $$ = ret; 10381592Srgrimes } 10391592Srgrimes ; 10401592Srgrimes 10411592Srgrimes 10421592Srgrimescheck_login 10431592Srgrimes : /* empty */ 10441592Srgrimes { 104570102Sphk $$ = check_login1(); 10461592Srgrimes } 10471592Srgrimes ; 10481592Srgrimes 104970102Sphkcheck_login_epsv 105070102Sphk : /* empty */ 105170102Sphk { 105270102Sphk if (noepsv) { 1053137852Syar reply(500, "EPSV command disabled."); 105470102Sphk $$ = 0; 105570102Sphk } 105670102Sphk else 105770102Sphk $$ = check_login1(); 105870102Sphk } 105970102Sphk ; 106070102Sphk 106170102Sphkcheck_login_ro 106270102Sphk : /* empty */ 106370102Sphk { 106470102Sphk if (readonly) { 106572710Sdes reply(550, "Permission denied."); 106670102Sphk $$ = 0; 106770102Sphk } 106870102Sphk else 106970102Sphk $$ = check_login1(); 107070102Sphk } 107170102Sphk ; 107270102Sphk 10731592Srgrimes%% 10741592Srgrimes 10751592Srgrimes#define CMD 0 /* beginning of command */ 10761592Srgrimes#define ARGS 1 /* expect miscellaneous arguments */ 10771592Srgrimes#define STR1 2 /* expect SP followed by STRING */ 10781592Srgrimes#define STR2 3 /* expect STRING */ 10791592Srgrimes#define OSTR 4 /* optional SP then STRING */ 108075556Sgreen#define ZSTR1 5 /* optional SP then optional STRING */ 10811592Srgrimes#define ZSTR2 6 /* optional STRING after SP */ 10821592Srgrimes#define SITECMD 7 /* SITE command */ 10831592Srgrimes#define NSTR 8 /* Number followed by a string */ 10841592Srgrimes 108575560Sjedgar#define MAXGLOBARGS 1000 108675560Sjedgar 1087101034Syar#define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */ 1088101034Syar 10891592Srgrimesstruct tab { 10901592Srgrimes char *name; 10911592Srgrimes short token; 10921592Srgrimes short state; 10931592Srgrimes short implemented; /* 1 if command is implemented */ 10941592Srgrimes char *help; 10951592Srgrimes}; 10961592Srgrimes 10971592Srgrimesstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 10981592Srgrimes { "USER", USER, STR1, 1, "<sp> username" }, 109975556Sgreen { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" }, 11001592Srgrimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 11011592Srgrimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 11021592Srgrimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 11031592Srgrimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 1104101806Syar { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" }, 110556668Sshin { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 110656668Sshin { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 11071592Srgrimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 110856668Sshin { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 110956668Sshin { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 1110101806Syar { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" }, 11111592Srgrimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 11121592Srgrimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 11131592Srgrimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 11141592Srgrimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 11151592Srgrimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 11161592Srgrimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 11171592Srgrimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 11181592Srgrimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 11191592Srgrimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 11201592Srgrimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 11211592Srgrimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 11221592Srgrimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 11231592Srgrimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 11241592Srgrimes { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 11251592Srgrimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 11261592Srgrimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 11271592Srgrimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 11281592Srgrimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 11291592Srgrimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 11301592Srgrimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 11311592Srgrimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 11321592Srgrimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 11331592Srgrimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 11341592Srgrimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 1135168849Syar { "FEAT", FEAT, ARGS, 1, "(get extended features)" }, 11361592Srgrimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 11371592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11381592Srgrimes { "NOOP", NOOP, ARGS, 1, "" }, 11391592Srgrimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 11401592Srgrimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 11411592Srgrimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 11421592Srgrimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 11431592Srgrimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 11441592Srgrimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 11451592Srgrimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11461592Srgrimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11471592Srgrimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 11481592Srgrimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 11491592Srgrimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 11501592Srgrimes { NULL, 0, 0, 0, 0 } 11511592Srgrimes}; 11521592Srgrimes 11531592Srgrimesstruct tab sitetab[] = { 115475535Sphk { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" }, 11551592Srgrimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 11561592Srgrimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 11571592Srgrimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 11581592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11591592Srgrimes { NULL, 0, 0, 0, 0 } 11601592Srgrimes}; 11611592Srgrimes 116290148Simpstatic char *copy(char *); 1163110340Syarstatic char *expglob(char *); 1164110340Syarstatic char *exptilde(char *); 116590148Simpstatic void help(struct tab *, char *); 11661592Srgrimesstatic struct tab * 116790148Simp lookup(struct tab *, char *); 116890148Simpstatic int port_check(const char *); 1169159276Syar#ifdef INET6 117090148Simpstatic int port_check_v6(const char *); 1171159276Syar#endif 117290148Simpstatic void sizecmd(char *); 117390148Simpstatic void toolong(int); 1174159276Syar#ifdef INET6 117590148Simpstatic void v4map_data_dest(void); 1176159276Syar#endif 117790148Simpstatic int yylex(void); 11781592Srgrimes 11791592Srgrimesstatic struct tab * 118090148Simplookup(struct tab *p, char *cmd) 11811592Srgrimes{ 11821592Srgrimes 11831592Srgrimes for (; p->name != NULL; p++) 11841592Srgrimes if (strcmp(cmd, p->name) == 0) 11851592Srgrimes return (p); 11861592Srgrimes return (0); 11871592Srgrimes} 11881592Srgrimes 11891592Srgrimes#include <arpa/telnet.h> 11901592Srgrimes 11911592Srgrimes/* 11921592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes. 11931592Srgrimes */ 11941592Srgrimeschar * 119590148Simpgetline(char *s, int n, FILE *iop) 11961592Srgrimes{ 11971592Srgrimes int c; 11981592Srgrimes register char *cs; 1199117352Syar sigset_t sset, osset; 12001592Srgrimes 12011592Srgrimes cs = s; 12021592Srgrimes/* tmpline may contain saved command from urgent mode interruption */ 12031592Srgrimes for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 12041592Srgrimes *cs++ = tmpline[c]; 12051592Srgrimes if (tmpline[c] == '\n') { 12061592Srgrimes *cs++ = '\0'; 120776096Smarkm if (ftpdebug) 12081592Srgrimes syslog(LOG_DEBUG, "command: %s", s); 12091592Srgrimes tmpline[0] = '\0'; 12101592Srgrimes return(s); 12111592Srgrimes } 12121592Srgrimes if (c == 0) 12131592Srgrimes tmpline[0] = '\0'; 12141592Srgrimes } 1215117352Syar /* SIGURG would interrupt stdio if not blocked during the read loop */ 1216117352Syar sigemptyset(&sset); 1217117352Syar sigaddset(&sset, SIGURG); 1218117352Syar sigprocmask(SIG_BLOCK, &sset, &osset); 12191592Srgrimes while ((c = getc(iop)) != EOF) { 12201592Srgrimes c &= 0377; 12211592Srgrimes if (c == IAC) { 1222117351Syar if ((c = getc(iop)) == EOF) 1223117351Syar goto got_eof; 12241592Srgrimes c &= 0377; 12251592Srgrimes switch (c) { 12261592Srgrimes case WILL: 12271592Srgrimes case WONT: 1228117351Syar if ((c = getc(iop)) == EOF) 1229117351Syar goto got_eof; 12301592Srgrimes printf("%c%c%c", IAC, DONT, 0377&c); 12311592Srgrimes (void) fflush(stdout); 12321592Srgrimes continue; 12331592Srgrimes case DO: 12341592Srgrimes case DONT: 1235117351Syar if ((c = getc(iop)) == EOF) 1236117351Syar goto got_eof; 12371592Srgrimes printf("%c%c%c", IAC, WONT, 0377&c); 12381592Srgrimes (void) fflush(stdout); 12391592Srgrimes continue; 12401592Srgrimes case IAC: 12411592Srgrimes break; 12421592Srgrimes default: 12431592Srgrimes continue; /* ignore command */ 12441592Srgrimes } 12451592Srgrimes } 12461592Srgrimes *cs++ = c; 12471592Srgrimes if (--n <= 0 || c == '\n') 12481592Srgrimes break; 12491592Srgrimes } 1250117351Syargot_eof: 1251117352Syar sigprocmask(SIG_SETMASK, &osset, NULL); 12521592Srgrimes if (c == EOF && cs == s) 12531592Srgrimes return (NULL); 12541592Srgrimes *cs++ = '\0'; 125576096Smarkm if (ftpdebug) { 12561592Srgrimes if (!guest && strncasecmp("pass ", s, 5) == 0) { 12571592Srgrimes /* Don't syslog passwords */ 12581592Srgrimes syslog(LOG_DEBUG, "command: %.5s ???", s); 12591592Srgrimes } else { 12601592Srgrimes register char *cp; 12611592Srgrimes register int len; 12621592Srgrimes 12631592Srgrimes /* Don't syslog trailing CR-LF */ 12641592Srgrimes len = strlen(s); 12651592Srgrimes cp = s + len - 1; 12661592Srgrimes while (cp >= s && (*cp == '\n' || *cp == '\r')) { 12671592Srgrimes --cp; 12681592Srgrimes --len; 12691592Srgrimes } 12701592Srgrimes syslog(LOG_DEBUG, "command: %.*s", len, s); 12711592Srgrimes } 12721592Srgrimes } 12731592Srgrimes return (s); 12741592Srgrimes} 12751592Srgrimes 12761592Srgrimesstatic void 127790148Simptoolong(int signo) 12781592Srgrimes{ 12791592Srgrimes 12801592Srgrimes reply(421, 12811592Srgrimes "Timeout (%d seconds): closing control connection.", timeout); 12821592Srgrimes if (logging) 12831592Srgrimes syslog(LOG_INFO, "User %s timed out after %d seconds", 12841592Srgrimes (pw ? pw -> pw_name : "unknown"), timeout); 12851592Srgrimes dologout(1); 12861592Srgrimes} 12871592Srgrimes 12881592Srgrimesstatic int 128990148Simpyylex(void) 12901592Srgrimes{ 129189935Syar static int cpos; 12921592Srgrimes char *cp, *cp2; 12931592Srgrimes struct tab *p; 12941592Srgrimes int n; 12951592Srgrimes char c; 12961592Srgrimes 12971592Srgrimes for (;;) { 12981592Srgrimes switch (state) { 12991592Srgrimes 13001592Srgrimes case CMD: 13011592Srgrimes (void) signal(SIGALRM, toolong); 1302137659Syar (void) alarm(timeout); 13031592Srgrimes if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 13041592Srgrimes reply(221, "You could at least say goodbye."); 13051592Srgrimes dologout(0); 13061592Srgrimes } 13071592Srgrimes (void) alarm(0); 13081592Srgrimes#ifdef SETPROCTITLE 130929574Sphk if (strncasecmp(cbuf, "PASS", 4) != 0) 13101592Srgrimes setproctitle("%s: %s", proctitle, cbuf); 13111592Srgrimes#endif /* SETPROCTITLE */ 13121592Srgrimes if ((cp = strchr(cbuf, '\r'))) { 13131592Srgrimes *cp++ = '\n'; 13141592Srgrimes *cp = '\0'; 13151592Srgrimes } 13161592Srgrimes if ((cp = strpbrk(cbuf, " \n"))) 13171592Srgrimes cpos = cp - cbuf; 13181592Srgrimes if (cpos == 0) 13191592Srgrimes cpos = 4; 13201592Srgrimes c = cbuf[cpos]; 13211592Srgrimes cbuf[cpos] = '\0'; 13221592Srgrimes upper(cbuf); 13231592Srgrimes p = lookup(cmdtab, cbuf); 13241592Srgrimes cbuf[cpos] = c; 13253776Spst if (p != 0) { 1326102565Syar yylval.s = p->name; 1327102565Syar if (!p->implemented) 1328102565Syar return (NOTIMPL); /* state remains CMD */ 13291592Srgrimes state = p->state; 13301592Srgrimes return (p->token); 13311592Srgrimes } 13321592Srgrimes break; 13331592Srgrimes 13341592Srgrimes case SITECMD: 13351592Srgrimes if (cbuf[cpos] == ' ') { 13361592Srgrimes cpos++; 13371592Srgrimes return (SP); 13381592Srgrimes } 13391592Srgrimes cp = &cbuf[cpos]; 13401592Srgrimes if ((cp2 = strpbrk(cp, " \n"))) 13411592Srgrimes cpos = cp2 - cbuf; 13421592Srgrimes c = cbuf[cpos]; 13431592Srgrimes cbuf[cpos] = '\0'; 13441592Srgrimes upper(cp); 13451592Srgrimes p = lookup(sitetab, cp); 13461592Srgrimes cbuf[cpos] = c; 13473777Spst if (guest == 0 && p != 0) { 1348102565Syar yylval.s = p->name; 1349102565Syar if (!p->implemented) { 13501592Srgrimes state = CMD; 1351102565Syar return (NOTIMPL); 13521592Srgrimes } 13531592Srgrimes state = p->state; 13541592Srgrimes return (p->token); 13551592Srgrimes } 13561592Srgrimes state = CMD; 13571592Srgrimes break; 13581592Srgrimes 135975556Sgreen case ZSTR1: 13601592Srgrimes case OSTR: 13611592Srgrimes if (cbuf[cpos] == '\n') { 13621592Srgrimes state = CMD; 13631592Srgrimes return (CRLF); 13641592Srgrimes } 13651592Srgrimes /* FALLTHROUGH */ 13661592Srgrimes 13671592Srgrimes case STR1: 13681592Srgrimes dostr1: 13691592Srgrimes if (cbuf[cpos] == ' ') { 13701592Srgrimes cpos++; 137151979Salfred state = state == OSTR ? STR2 : state+1; 13721592Srgrimes return (SP); 13731592Srgrimes } 13741592Srgrimes break; 13751592Srgrimes 13761592Srgrimes case ZSTR2: 13771592Srgrimes if (cbuf[cpos] == '\n') { 13781592Srgrimes state = CMD; 13791592Srgrimes return (CRLF); 13801592Srgrimes } 13811592Srgrimes /* FALLTHROUGH */ 13821592Srgrimes 13831592Srgrimes case STR2: 13841592Srgrimes cp = &cbuf[cpos]; 13851592Srgrimes n = strlen(cp); 13861592Srgrimes cpos += n - 1; 13871592Srgrimes /* 13881592Srgrimes * Make sure the string is nonempty and \n terminated. 13891592Srgrimes */ 13901592Srgrimes if (n > 1 && cbuf[cpos] == '\n') { 13911592Srgrimes cbuf[cpos] = '\0'; 13921592Srgrimes yylval.s = copy(cp); 13931592Srgrimes cbuf[cpos] = '\n'; 13941592Srgrimes state = ARGS; 13951592Srgrimes return (STRING); 13961592Srgrimes } 13971592Srgrimes break; 13981592Srgrimes 13991592Srgrimes case NSTR: 14001592Srgrimes if (cbuf[cpos] == ' ') { 14011592Srgrimes cpos++; 14021592Srgrimes return (SP); 14031592Srgrimes } 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); 14111592Srgrimes cbuf[cpos] = c; 14121592Srgrimes state = STR1; 14131592Srgrimes return (NUMBER); 14141592Srgrimes } 14151592Srgrimes state = STR1; 14161592Srgrimes goto dostr1; 14171592Srgrimes 14181592Srgrimes case ARGS: 14191592Srgrimes if (isdigit(cbuf[cpos])) { 14201592Srgrimes cp = &cbuf[cpos]; 14211592Srgrimes while (isdigit(cbuf[++cpos])) 14221592Srgrimes ; 14231592Srgrimes c = cbuf[cpos]; 14241592Srgrimes cbuf[cpos] = '\0'; 142592272Smaxim yylval.u.i = atoi(cp); 1426137811Syar yylval.u.o = strtoull(cp, NULL, 10); 14271592Srgrimes cbuf[cpos] = c; 14281592Srgrimes return (NUMBER); 14291592Srgrimes } 143056668Sshin if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 143156668Sshin && !isalnum(cbuf[cpos + 3])) { 143256668Sshin cpos += 3; 143356668Sshin return ALL; 143456668Sshin } 14351592Srgrimes switch (cbuf[cpos++]) { 14361592Srgrimes 14371592Srgrimes case '\n': 14381592Srgrimes state = CMD; 14391592Srgrimes return (CRLF); 14401592Srgrimes 14411592Srgrimes case ' ': 14421592Srgrimes return (SP); 14431592Srgrimes 14441592Srgrimes case ',': 14451592Srgrimes return (COMMA); 14461592Srgrimes 14471592Srgrimes case 'A': 14481592Srgrimes case 'a': 14491592Srgrimes return (A); 14501592Srgrimes 14511592Srgrimes case 'B': 14521592Srgrimes case 'b': 14531592Srgrimes return (B); 14541592Srgrimes 14551592Srgrimes case 'C': 14561592Srgrimes case 'c': 14571592Srgrimes return (C); 14581592Srgrimes 14591592Srgrimes case 'E': 14601592Srgrimes case 'e': 14611592Srgrimes return (E); 14621592Srgrimes 14631592Srgrimes case 'F': 14641592Srgrimes case 'f': 14651592Srgrimes return (F); 14661592Srgrimes 14671592Srgrimes case 'I': 14681592Srgrimes case 'i': 14691592Srgrimes return (I); 14701592Srgrimes 14711592Srgrimes case 'L': 14721592Srgrimes case 'l': 14731592Srgrimes return (L); 14741592Srgrimes 14751592Srgrimes case 'N': 14761592Srgrimes case 'n': 14771592Srgrimes return (N); 14781592Srgrimes 14791592Srgrimes case 'P': 14801592Srgrimes case 'p': 14811592Srgrimes return (P); 14821592Srgrimes 14831592Srgrimes case 'R': 14841592Srgrimes case 'r': 14851592Srgrimes return (R); 14861592Srgrimes 14871592Srgrimes case 'S': 14881592Srgrimes case 's': 14891592Srgrimes return (S); 14901592Srgrimes 14911592Srgrimes case 'T': 14921592Srgrimes case 't': 14931592Srgrimes return (T); 14941592Srgrimes 14951592Srgrimes } 14961592Srgrimes break; 14971592Srgrimes 14981592Srgrimes default: 149976096Smarkm fatalerror("Unknown state in scanner."); 15001592Srgrimes } 15011592Srgrimes state = CMD; 150289935Syar return (LEXERR); 15031592Srgrimes } 15041592Srgrimes} 15051592Srgrimes 15061592Srgrimesvoid 150790148Simpupper(char *s) 15081592Srgrimes{ 15091592Srgrimes while (*s != '\0') { 15101592Srgrimes if (islower(*s)) 15111592Srgrimes *s = toupper(*s); 15121592Srgrimes s++; 15131592Srgrimes } 15141592Srgrimes} 15151592Srgrimes 15161592Srgrimesstatic char * 151790148Simpcopy(char *s) 15181592Srgrimes{ 15191592Srgrimes char *p; 15201592Srgrimes 1521137659Syar p = malloc(strlen(s) + 1); 15221592Srgrimes if (p == NULL) 152376096Smarkm fatalerror("Ran out of memory."); 15241592Srgrimes (void) strcpy(p, s); 15251592Srgrimes return (p); 15261592Srgrimes} 15271592Srgrimes 15281592Srgrimesstatic void 152990148Simphelp(struct tab *ctab, char *s) 15301592Srgrimes{ 15311592Srgrimes struct tab *c; 15321592Srgrimes int width, NCMDS; 15331592Srgrimes char *type; 15341592Srgrimes 15351592Srgrimes if (ctab == sitetab) 15361592Srgrimes type = "SITE "; 15371592Srgrimes else 15381592Srgrimes type = ""; 15391592Srgrimes width = 0, NCMDS = 0; 15401592Srgrimes for (c = ctab; c->name != NULL; c++) { 15411592Srgrimes int len = strlen(c->name); 15421592Srgrimes 15431592Srgrimes if (len > width) 15441592Srgrimes width = len; 15451592Srgrimes NCMDS++; 15461592Srgrimes } 15471592Srgrimes width = (width + 8) &~ 7; 15481592Srgrimes if (s == 0) { 15491592Srgrimes int i, j, w; 15501592Srgrimes int columns, lines; 15511592Srgrimes 15521592Srgrimes lreply(214, "The following %scommands are recognized %s.", 15531592Srgrimes type, "(* =>'s unimplemented)"); 15541592Srgrimes columns = 76 / width; 15551592Srgrimes if (columns == 0) 15561592Srgrimes columns = 1; 15571592Srgrimes lines = (NCMDS + columns - 1) / columns; 15581592Srgrimes for (i = 0; i < lines; i++) { 15591592Srgrimes printf(" "); 15601592Srgrimes for (j = 0; j < columns; j++) { 15611592Srgrimes c = ctab + j * lines + i; 15621592Srgrimes printf("%s%c", c->name, 15631592Srgrimes c->implemented ? ' ' : '*'); 15641592Srgrimes if (c + lines >= &ctab[NCMDS]) 15651592Srgrimes break; 15661592Srgrimes w = strlen(c->name) + 1; 15671592Srgrimes while (w < width) { 15681592Srgrimes putchar(' '); 15691592Srgrimes w++; 15701592Srgrimes } 15711592Srgrimes } 15721592Srgrimes printf("\r\n"); 15731592Srgrimes } 15741592Srgrimes (void) fflush(stdout); 1575110037Syar if (hostinfo) 1576110037Syar reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1577110037Syar else 1578110037Syar reply(214, "End."); 15791592Srgrimes return; 15801592Srgrimes } 15811592Srgrimes upper(s); 15821592Srgrimes c = lookup(ctab, s); 1583132931Syar if (c == NULL) { 15841592Srgrimes reply(502, "Unknown command %s.", s); 15851592Srgrimes return; 15861592Srgrimes } 15871592Srgrimes if (c->implemented) 15881592Srgrimes reply(214, "Syntax: %s%s %s", type, c->name, c->help); 15891592Srgrimes else 15901592Srgrimes reply(214, "%s%-*s\t%s; unimplemented.", type, width, 15911592Srgrimes c->name, c->help); 15921592Srgrimes} 15931592Srgrimes 15941592Srgrimesstatic void 159590148Simpsizecmd(char *filename) 15961592Srgrimes{ 15971592Srgrimes switch (type) { 15981592Srgrimes case TYPE_L: 15991592Srgrimes case TYPE_I: { 16001592Srgrimes struct stat stbuf; 160163350Sdes if (stat(filename, &stbuf) < 0) 160263350Sdes perror_reply(550, filename); 160363350Sdes else if (!S_ISREG(stbuf.st_mode)) 16041592Srgrimes reply(550, "%s: not a plain file.", filename); 16051592Srgrimes else 1606132929Syar reply(213, "%jd", (intmax_t)stbuf.st_size); 16071592Srgrimes break; } 16081592Srgrimes case TYPE_A: { 16091592Srgrimes FILE *fin; 16101592Srgrimes int c; 16111592Srgrimes off_t count; 16121592Srgrimes struct stat stbuf; 16131592Srgrimes fin = fopen(filename, "r"); 16141592Srgrimes if (fin == NULL) { 16151592Srgrimes perror_reply(550, filename); 16161592Srgrimes return; 16171592Srgrimes } 161863350Sdes if (fstat(fileno(fin), &stbuf) < 0) { 161963350Sdes perror_reply(550, filename); 162063350Sdes (void) fclose(fin); 162163350Sdes return; 162263350Sdes } else if (!S_ISREG(stbuf.st_mode)) { 16231592Srgrimes reply(550, "%s: not a plain file.", filename); 16241592Srgrimes (void) fclose(fin); 16251592Srgrimes return; 1626101034Syar } else if (stbuf.st_size > MAXASIZE) { 1627101034Syar reply(550, "%s: too large for type A SIZE.", filename); 1628101034Syar (void) fclose(fin); 1629101034Syar return; 16301592Srgrimes } 16311592Srgrimes 16321592Srgrimes count = 0; 16331592Srgrimes while((c=getc(fin)) != EOF) { 16341592Srgrimes if (c == '\n') /* will get expanded to \r\n */ 16351592Srgrimes count++; 16361592Srgrimes count++; 16371592Srgrimes } 16381592Srgrimes (void) fclose(fin); 16391592Srgrimes 1640132929Syar reply(213, "%jd", (intmax_t)count); 16411592Srgrimes break; } 16421592Srgrimes default: 1643100684Syar reply(504, "SIZE not implemented for type %s.", 1644100684Syar typenames[type]); 16451592Srgrimes } 16461592Srgrimes} 164756668Sshin 164856668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 164956668Sshinstatic int 165090148Simpport_check(const char *pcmd) 165156668Sshin{ 165256668Sshin if (his_addr.su_family == AF_INET) { 165356668Sshin if (data_dest.su_family != AF_INET) { 165456668Sshin usedefault = 1; 165556668Sshin reply(500, "Invalid address rejected."); 165656668Sshin return 1; 165756668Sshin } 165856668Sshin if (paranoid && 165956668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 166056668Sshin memcmp(&data_dest.su_sin.sin_addr, 166156668Sshin &his_addr.su_sin.sin_addr, 166256668Sshin sizeof(data_dest.su_sin.sin_addr)))) { 166356668Sshin usedefault = 1; 166456668Sshin reply(500, "Illegal PORT range rejected."); 166556668Sshin } else { 166656668Sshin usedefault = 0; 166756668Sshin if (pdata >= 0) { 166856668Sshin (void) close(pdata); 166956668Sshin pdata = -1; 167056668Sshin } 167156668Sshin reply(200, "%s command successful.", pcmd); 167256668Sshin } 167356668Sshin return 1; 167456668Sshin } 167556668Sshin return 0; 167656668Sshin} 167756668Sshin 167870102Sphkstatic int 167990148Simpcheck_login1(void) 168070102Sphk{ 168170102Sphk if (logged_in) 168270102Sphk return 1; 168370102Sphk else { 168470102Sphk reply(530, "Please login with USER and PASS."); 168570102Sphk return 0; 168670102Sphk } 168770102Sphk} 168870102Sphk 1689110340Syar/* 1690110340Syar * Replace leading "~user" in a pathname by the user's login directory. 1691110340Syar * Returned string will be in a freshly malloced buffer unless it's NULL. 1692110340Syar */ 1693110340Syarstatic char * 1694110340Syarexptilde(char *s) 1695110340Syar{ 1696110340Syar char *p, *q; 1697110340Syar char *path, *user; 1698110340Syar struct passwd *ppw; 1699110340Syar 1700110340Syar if ((p = strdup(s)) == NULL) 1701110340Syar return (NULL); 1702110340Syar if (*p != '~') 1703110340Syar return (p); 1704110340Syar 1705110340Syar user = p + 1; /* skip tilde */ 1706110340Syar if ((path = strchr(p, '/')) != NULL) 1707110340Syar *(path++) = '\0'; /* separate ~user from the rest of path */ 1708110378Syar if (*user == '\0') /* no user specified, use the current user */ 1709110378Syar user = pw->pw_name; 1710110378Syar /* read passwd even for the current user since we may be chrooted */ 1711110378Syar if ((ppw = getpwnam(user)) != NULL) { 1712110340Syar /* user found, substitute login directory for ~user */ 1713110340Syar if (path) 1714110340Syar asprintf(&q, "%s/%s", ppw->pw_dir, path); 1715110340Syar else 1716110340Syar q = strdup(ppw->pw_dir); 1717110340Syar free(p); 1718110340Syar p = q; 1719110340Syar } else { 1720110340Syar /* user not found, undo the damage */ 1721110340Syar if (path) 1722110340Syar path[-1] = '/'; 1723110340Syar } 1724110340Syar return (p); 1725110340Syar} 1726110340Syar 1727110340Syar/* 1728110340Syar * Expand glob(3) patterns possibly present in a pathname. 1729110340Syar * Avoid expanding to a pathname including '\r' or '\n' in order to 1730110340Syar * not disrupt the FTP protocol. 1731110340Syar * The expansion found must be unique. 1732110340Syar * Return the result as a malloced string, or NULL if an error occured. 1733110340Syar * 1734110340Syar * Problem: this production is used for all pathname 1735110340Syar * processing, but only gives a 550 error reply. 1736110340Syar * This is a valid reply in some cases but not in others. 1737110340Syar */ 1738110340Syarstatic char * 1739110340Syarexpglob(char *s) 1740110340Syar{ 1741110340Syar char *p, **pp, *rval; 1742110340Syar int flags = GLOB_BRACE | GLOB_NOCHECK; 1743110340Syar int n; 1744110340Syar glob_t gl; 1745110340Syar 1746110340Syar memset(&gl, 0, sizeof(gl)); 1747110340Syar flags |= GLOB_LIMIT; 1748110340Syar gl.gl_matchc = MAXGLOBARGS; 1749110340Syar if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) { 1750110340Syar for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++) 1751110340Syar if (*(*pp + strcspn(*pp, "\r\n")) == '\0') { 1752110340Syar p = *pp; 1753110340Syar n++; 1754110340Syar } 1755110340Syar if (n == 0) 1756110340Syar rval = strdup(s); 1757110340Syar else if (n == 1) 1758110340Syar rval = strdup(p); 1759110340Syar else { 1760137852Syar reply(550, "Wildcard is ambiguous."); 1761110340Syar rval = NULL; 1762110340Syar } 1763110340Syar } else { 1764137852Syar reply(550, "Wildcard expansion error."); 1765110340Syar rval = NULL; 1766110340Syar } 1767110340Syar globfree(&gl); 1768110340Syar return (rval); 1769110340Syar} 1770110340Syar 177156668Sshin#ifdef INET6 177256668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 177356668Sshinstatic int 177490148Simpport_check_v6(const char *pcmd) 177556668Sshin{ 177656668Sshin if (his_addr.su_family == AF_INET6) { 177756668Sshin if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 177856668Sshin /* Convert data_dest into v4 mapped sockaddr.*/ 177956668Sshin v4map_data_dest(); 178056668Sshin if (data_dest.su_family != AF_INET6) { 178156668Sshin usedefault = 1; 178256668Sshin reply(500, "Invalid address rejected."); 178356668Sshin return 1; 178456668Sshin } 178556668Sshin if (paranoid && 178656668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 178756668Sshin memcmp(&data_dest.su_sin6.sin6_addr, 178856668Sshin &his_addr.su_sin6.sin6_addr, 178956668Sshin sizeof(data_dest.su_sin6.sin6_addr)))) { 179056668Sshin usedefault = 1; 179156668Sshin reply(500, "Illegal PORT range rejected."); 179256668Sshin } else { 179356668Sshin usedefault = 0; 179456668Sshin if (pdata >= 0) { 179556668Sshin (void) close(pdata); 179656668Sshin pdata = -1; 179756668Sshin } 179856668Sshin reply(200, "%s command successful.", pcmd); 179956668Sshin } 180056668Sshin return 1; 180156668Sshin } 180256668Sshin return 0; 180356668Sshin} 180456668Sshin 180556668Sshinstatic void 180690148Simpv4map_data_dest(void) 180756668Sshin{ 180856668Sshin struct in_addr savedaddr; 180956668Sshin int savedport; 181056668Sshin 181156668Sshin if (data_dest.su_family != AF_INET) { 181256668Sshin usedefault = 1; 181356668Sshin reply(500, "Invalid address rejected."); 181456668Sshin return; 181556668Sshin } 181656668Sshin 181756668Sshin savedaddr = data_dest.su_sin.sin_addr; 181856668Sshin savedport = data_dest.su_port; 181956668Sshin 182056668Sshin memset(&data_dest, 0, sizeof(data_dest)); 182156668Sshin data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 182256668Sshin data_dest.su_sin6.sin6_family = AF_INET6; 182356668Sshin data_dest.su_sin6.sin6_port = savedport; 182456668Sshin memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 182556668Sshin memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 182656668Sshin (caddr_t)&savedaddr, sizeof(savedaddr)); 182756668Sshin} 182856668Sshin#endif 1829