ftpcmd.y revision 92282
192282Sobrien/* $NetBSD: ftpcmd.y,v 1.66 2001/12/01 10:25:30 lukem Exp $ */ 279968Sobrien 379968Sobrien/*- 479968Sobrien * Copyright (c) 1997-2001 The NetBSD Foundation, Inc. 579968Sobrien * All rights reserved. 679968Sobrien * 779968Sobrien * This code is derived from software contributed to The NetBSD Foundation 879968Sobrien * by Luke Mewburn. 979968Sobrien * 1079968Sobrien * Redistribution and use in source and binary forms, with or without 1179968Sobrien * modification, are permitted provided that the following conditions 1279968Sobrien * are met: 1379968Sobrien * 1. Redistributions of source code must retain the above copyright 1479968Sobrien * notice, this list of conditions and the following disclaimer. 1579968Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1679968Sobrien * notice, this list of conditions and the following disclaimer in the 1779968Sobrien * documentation and/or other materials provided with the distribution. 1879968Sobrien * 3. All advertising materials mentioning features or use of this software 1979968Sobrien * must display the following acknowledgement: 2079968Sobrien * This product includes software developed by the NetBSD 2179968Sobrien * Foundation, Inc. and its contributors. 2279968Sobrien * 4. Neither the name of The NetBSD Foundation nor the names of its 2379968Sobrien * contributors may be used to endorse or promote products derived 2479968Sobrien * from this software without specific prior written permission. 2579968Sobrien * 2679968Sobrien * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2779968Sobrien * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2879968Sobrien * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2979968Sobrien * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 3079968Sobrien * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 3179968Sobrien * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 3279968Sobrien * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 3379968Sobrien * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 3479968Sobrien * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3579968Sobrien * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3679968Sobrien * POSSIBILITY OF SUCH DAMAGE. 3779968Sobrien */ 3879968Sobrien 3979968Sobrien/* 4079968Sobrien * Copyright (c) 1985, 1988, 1993, 1994 4179968Sobrien * The Regents of the University of California. All rights reserved. 4279968Sobrien * 4379968Sobrien * Redistribution and use in source and binary forms, with or without 4479968Sobrien * modification, are permitted provided that the following conditions 4579968Sobrien * are met: 4679968Sobrien * 1. Redistributions of source code must retain the above copyright 4779968Sobrien * notice, this list of conditions and the following disclaimer. 4879968Sobrien * 2. Redistributions in binary form must reproduce the above copyright 4979968Sobrien * notice, this list of conditions and the following disclaimer in the 5079968Sobrien * documentation and/or other materials provided with the distribution. 5179968Sobrien * 3. All advertising materials mentioning features or use of this software 5279968Sobrien * must display the following acknowledgement: 5379968Sobrien * This product includes software developed by the University of 5479968Sobrien * California, Berkeley and its contributors. 5579968Sobrien * 4. Neither the name of the University nor the names of its contributors 5679968Sobrien * may be used to endorse or promote products derived from this software 5779968Sobrien * without specific prior written permission. 5879968Sobrien * 5979968Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 6079968Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 6179968Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 6279968Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 6379968Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 6479968Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 6579968Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 6679968Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 6779968Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 6879968Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 6979968Sobrien * SUCH DAMAGE. 7079968Sobrien * 7179968Sobrien * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 7279968Sobrien */ 7379968Sobrien 7479968Sobrien/* 7579968Sobrien * Grammar for FTP commands. 7679968Sobrien * See RFC 959. 7779968Sobrien */ 7879968Sobrien 7979968Sobrien%{ 8079968Sobrien#include "lukemftpd.h" 8179968Sobrien 8279968Sobrien#include "extern.h" 8379968Sobrien#include "version.h" 8479968Sobrien 8579968Sobrienstatic int cmd_type; 8679968Sobrienstatic int cmd_form; 8779968Sobrienstatic int cmd_bytesz; 8879968Sobrien 8979968Sobrienchar cbuf[FTP_BUFLEN]; 9079968Sobrienchar *cmdp; 9179968Sobrienchar *fromname; 9279968Sobrien 9379968Sobrien%} 9479968Sobrien 9579968Sobrien%union { 9679968Sobrien int i; 9779968Sobrien char *s; 9879968Sobrien} 9979968Sobrien 10079968Sobrien%token 10179968Sobrien A B C E F I 10279968Sobrien L N P R S T 10379968Sobrien 10479968Sobrien SP CRLF COMMA 10579968Sobrien 10679968Sobrien USER PASS ACCT CWD CDUP SMNT 10779968Sobrien QUIT REIN PORT PASV TYPE STRU 10879968Sobrien MODE RETR STOR STOU APPE ALLO 10979968Sobrien REST RNFR RNTO ABOR DELE RMD 11079968Sobrien MKD PWD LIST NLST SITE SYST 11179968Sobrien STAT HELP NOOP 11279968Sobrien 11379968Sobrien AUTH ADAT PROT PBSZ CCC MIC 11479968Sobrien CONF ENC 11579968Sobrien 11679968Sobrien FEAT OPTS 11779968Sobrien 11879968Sobrien SIZE MDTM MLST MLSD 11979968Sobrien 12079968Sobrien LPRT LPSV EPRT EPSV 12179968Sobrien 12279968Sobrien MAIL MLFL MRCP MRSQ MSAM MSND 12379968Sobrien MSOM 12479968Sobrien 12579968Sobrien CHMOD IDLE RATEGET RATEPUT UMASK 12679968Sobrien 12779968Sobrien LEXERR 12879968Sobrien 12979968Sobrien%token <s> STRING 13079968Sobrien%token <s> ALL 13179968Sobrien%token <i> NUMBER 13279968Sobrien 13379968Sobrien%type <i> check_login octal_number byte_size 13479968Sobrien%type <i> struct_code mode_code type_code form_code decimal_integer 13579968Sobrien%type <s> pathstring pathname password username 13679968Sobrien%type <s> mechanism_name base64data prot_code 13779968Sobrien 13879968Sobrien%start cmd_sel 13979968Sobrien 14079968Sobrien%% 14179968Sobrien 14279968Sobriencmd_sel 14379968Sobrien : cmd 14479968Sobrien { 14579968Sobrien fromname = NULL; 14679968Sobrien restart_point = (off_t) 0; 14779968Sobrien } 14879968Sobrien 14979968Sobrien | rcmd 15079968Sobrien 15179968Sobrien ; 15279968Sobrien 15379968Sobriencmd 15479968Sobrien /* RFC 959 */ 15579968Sobrien : USER SP username CRLF 15679968Sobrien { 15779968Sobrien user($3); 15879968Sobrien free($3); 15979968Sobrien } 16079968Sobrien 16179968Sobrien | PASS SP password CRLF 16279968Sobrien { 16379968Sobrien pass($3); 16479968Sobrien memset($3, 0, strlen($3)); 16579968Sobrien free($3); 16679968Sobrien } 16779968Sobrien 16879968Sobrien | CWD check_login CRLF 16979968Sobrien { 17079968Sobrien if ($2) 17179968Sobrien cwd(homedir); 17279968Sobrien } 17379968Sobrien 17479968Sobrien | CWD check_login SP pathname CRLF 17579968Sobrien { 17679968Sobrien if ($2 && $4 != NULL) 17779968Sobrien cwd($4); 17879968Sobrien if ($4 != NULL) 17979968Sobrien free($4); 18079968Sobrien } 18179968Sobrien 18279968Sobrien | CDUP check_login CRLF 18379968Sobrien { 18479968Sobrien if ($2) 18579968Sobrien cwd(".."); 18679968Sobrien } 18779968Sobrien 18879968Sobrien | QUIT CRLF 18979968Sobrien { 19079968Sobrien if (logged_in) { 19179968Sobrien reply(-221, "%s", ""); 19279968Sobrien reply(0, 19379968Sobrien "Data traffic for this session was " LLF " byte%s in " LLF " file%s.", 19479968Sobrien (LLT)total_data, PLURAL(total_data), 19579968Sobrien (LLT)total_files, PLURAL(total_files)); 19679968Sobrien reply(0, 19779968Sobrien "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.", 19879968Sobrien (LLT)total_bytes, PLURAL(total_bytes), 19979968Sobrien (LLT)total_xfers, PLURAL(total_xfers)); 20079968Sobrien } 20179968Sobrien reply(221, 20279968Sobrien "Thank you for using the FTP service on %s.", 20379968Sobrien hostname); 20479968Sobrien if (logged_in && logging) { 20579968Sobrien syslog(LOG_INFO, 20679968Sobrien "Data traffic: " LLF " byte%s in " LLF " file%s", 20779968Sobrien (LLT)total_data, PLURAL(total_data), 20879968Sobrien (LLT)total_files, PLURAL(total_files)); 20979968Sobrien syslog(LOG_INFO, 21079968Sobrien "Total traffic: " LLF " byte%s in " LLF " transfer%s", 21179968Sobrien (LLT)total_bytes, PLURAL(total_bytes), 21279968Sobrien (LLT)total_xfers, PLURAL(total_xfers)); 21379968Sobrien } 21479968Sobrien 21579968Sobrien dologout(0); 21679968Sobrien } 21779968Sobrien 21879968Sobrien | PORT check_login SP host_port CRLF 21979968Sobrien { 22079968Sobrien if ($2) 22179968Sobrien port_check("PORT", AF_INET); 22279968Sobrien } 22379968Sobrien 22479968Sobrien | LPRT check_login SP host_long_port4 CRLF 22579968Sobrien { 22679968Sobrien if ($2) 22779968Sobrien port_check("LPRT", AF_INET); 22879968Sobrien } 22979968Sobrien 23079968Sobrien | LPRT check_login SP host_long_port6 CRLF 23179968Sobrien { 23279968Sobrien#ifdef INET6 23379968Sobrien if ($2) 23479968Sobrien port_check("LPRT", AF_INET6); 23579968Sobrien#else 23679968Sobrien reply(500, "IPv6 support not available."); 23779968Sobrien#endif 23879968Sobrien } 23979968Sobrien 24079968Sobrien | EPRT check_login SP STRING CRLF 24179968Sobrien { 24279968Sobrien if ($2) { 24379968Sobrien if (extended_port($4) == 0) 24479968Sobrien port_check("EPRT", -1); 24579968Sobrien } 24679968Sobrien free($4); 24779968Sobrien } 24879968Sobrien 24979968Sobrien | PASV check_login CRLF 25079968Sobrien { 25179968Sobrien if ($2) { 25279968Sobrien if (CURCLASS_FLAGS_ISSET(passive)) 25379968Sobrien passive(); 25479968Sobrien else 25579968Sobrien reply(500, "PASV mode not available."); 25679968Sobrien } 25779968Sobrien } 25879968Sobrien 25979968Sobrien | LPSV check_login CRLF 26079968Sobrien { 26179968Sobrien if ($2) { 26279968Sobrien if (epsvall) 26379968Sobrien reply(501, 26479968Sobrien "LPSV disallowed after EPSV ALL"); 26579968Sobrien else 26679968Sobrien long_passive("LPSV", PF_UNSPEC); 26779968Sobrien } 26879968Sobrien } 26979968Sobrien 27079968Sobrien | EPSV check_login SP NUMBER CRLF 27179968Sobrien { 27279968Sobrien if ($2) 27379968Sobrien long_passive("EPSV", epsvproto2af($4)); 27479968Sobrien } 27579968Sobrien 27679968Sobrien | EPSV check_login SP ALL CRLF 27779968Sobrien { 27879968Sobrien if ($2) { 27979968Sobrien reply(200, "EPSV ALL command successful."); 28079968Sobrien epsvall++; 28179968Sobrien } 28279968Sobrien } 28379968Sobrien 28479968Sobrien | EPSV check_login CRLF 28579968Sobrien { 28679968Sobrien if ($2) 28779968Sobrien long_passive("EPSV", PF_UNSPEC); 28879968Sobrien } 28979968Sobrien 29079968Sobrien | TYPE check_login SP type_code CRLF 29179968Sobrien { 29279968Sobrien if ($2) { 29379968Sobrien 29479968Sobrien switch (cmd_type) { 29579968Sobrien 29679968Sobrien case TYPE_A: 29779968Sobrien if (cmd_form == FORM_N) { 29879968Sobrien reply(200, "Type set to A."); 29979968Sobrien type = cmd_type; 30079968Sobrien form = cmd_form; 30179968Sobrien } else 30279968Sobrien reply(504, "Form must be N."); 30379968Sobrien break; 30479968Sobrien 30579968Sobrien case TYPE_E: 30679968Sobrien reply(504, "Type E not implemented."); 30779968Sobrien break; 30879968Sobrien 30979968Sobrien case TYPE_I: 31079968Sobrien reply(200, "Type set to I."); 31179968Sobrien type = cmd_type; 31279968Sobrien break; 31379968Sobrien 31479968Sobrien case TYPE_L: 31579968Sobrien#if NBBY == 8 31679968Sobrien if (cmd_bytesz == 8) { 31779968Sobrien reply(200, 31879968Sobrien "Type set to L (byte size 8)."); 31979968Sobrien type = cmd_type; 32079968Sobrien } else 32179968Sobrien reply(504, "Byte size must be 8."); 32279968Sobrien#else /* NBBY == 8 */ 32379968Sobrien UNIMPLEMENTED for NBBY != 8 32479968Sobrien#endif /* NBBY == 8 */ 32579968Sobrien } 32679968Sobrien 32779968Sobrien } 32879968Sobrien } 32979968Sobrien 33079968Sobrien | STRU check_login SP struct_code CRLF 33179968Sobrien { 33279968Sobrien if ($2) { 33379968Sobrien switch ($4) { 33479968Sobrien 33579968Sobrien case STRU_F: 33679968Sobrien reply(200, "STRU F ok."); 33779968Sobrien break; 33879968Sobrien 33979968Sobrien default: 34079968Sobrien reply(504, "Unimplemented STRU type."); 34179968Sobrien } 34279968Sobrien } 34379968Sobrien } 34479968Sobrien 34579968Sobrien | MODE check_login SP mode_code CRLF 34679968Sobrien { 34779968Sobrien if ($2) { 34879968Sobrien switch ($4) { 34979968Sobrien 35079968Sobrien case MODE_S: 35179968Sobrien reply(200, "MODE S ok."); 35279968Sobrien break; 35379968Sobrien 35479968Sobrien default: 35579968Sobrien reply(502, "Unimplemented MODE type."); 35679968Sobrien } 35779968Sobrien } 35879968Sobrien } 35979968Sobrien 36079968Sobrien | RETR check_login SP pathname CRLF 36179968Sobrien { 36279968Sobrien if ($2 && $4 != NULL) 36379968Sobrien retrieve(NULL, $4); 36479968Sobrien if ($4 != NULL) 36579968Sobrien free($4); 36679968Sobrien } 36779968Sobrien 36879968Sobrien | STOR SP pathname CRLF 36979968Sobrien { 37079968Sobrien if (check_write($3, 1)) 37179968Sobrien store($3, "w", 0); 37279968Sobrien if ($3 != NULL) 37379968Sobrien free($3); 37479968Sobrien } 37579968Sobrien 37679968Sobrien | STOU SP pathname CRLF 37779968Sobrien { 37879968Sobrien if (check_write($3, 1)) 37979968Sobrien store($3, "w", 1); 38079968Sobrien if ($3 != NULL) 38179968Sobrien free($3); 38279968Sobrien } 38379968Sobrien 38479968Sobrien | APPE SP pathname CRLF 38579968Sobrien { 38679968Sobrien if (check_write($3, 1)) 38779968Sobrien store($3, "a", 0); 38879968Sobrien if ($3 != NULL) 38979968Sobrien free($3); 39079968Sobrien } 39179968Sobrien 39279968Sobrien | ALLO check_login SP NUMBER CRLF 39379968Sobrien { 39479968Sobrien if ($2) 39579968Sobrien reply(202, "ALLO command ignored."); 39679968Sobrien } 39779968Sobrien 39879968Sobrien | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 39979968Sobrien { 40079968Sobrien if ($2) 40179968Sobrien reply(202, "ALLO command ignored."); 40279968Sobrien } 40379968Sobrien 40479968Sobrien | RNTO SP pathname CRLF 40579968Sobrien { 40679968Sobrien if (check_write($3, 0)) { 40779968Sobrien if (fromname) { 40879968Sobrien renamecmd(fromname, $3); 40979968Sobrien free(fromname); 41079968Sobrien fromname = NULL; 41179968Sobrien } else { 41279968Sobrien reply(503, "Bad sequence of commands."); 41379968Sobrien } 41479968Sobrien } 41579968Sobrien if ($3 != NULL) 41679968Sobrien free($3); 41779968Sobrien } 41879968Sobrien 41979968Sobrien | ABOR check_login CRLF 42079968Sobrien { 42179968Sobrien if (is_oob) 42279968Sobrien abor(); 42379968Sobrien else if ($2) 42479968Sobrien reply(225, "ABOR command successful."); 42579968Sobrien } 42679968Sobrien 42779968Sobrien | DELE SP pathname CRLF 42879968Sobrien { 42979968Sobrien if (check_write($3, 0)) 43079968Sobrien delete($3); 43179968Sobrien if ($3 != NULL) 43279968Sobrien free($3); 43379968Sobrien } 43479968Sobrien 43579968Sobrien | RMD SP pathname CRLF 43679968Sobrien { 43779968Sobrien if (check_write($3, 0)) 43879968Sobrien removedir($3); 43979968Sobrien if ($3 != NULL) 44079968Sobrien free($3); 44179968Sobrien } 44279968Sobrien 44379968Sobrien | MKD SP pathname CRLF 44479968Sobrien { 44579968Sobrien if (check_write($3, 0)) 44679968Sobrien makedir($3); 44779968Sobrien if ($3 != NULL) 44879968Sobrien free($3); 44979968Sobrien } 45079968Sobrien 45179968Sobrien | PWD check_login CRLF 45279968Sobrien { 45379968Sobrien if ($2) 45479968Sobrien pwd(); 45579968Sobrien } 45679968Sobrien 45779968Sobrien | LIST check_login CRLF 45879968Sobrien { 45979968Sobrien char *argv[] = { INTERNAL_LS, "-lgA", NULL }; 46079968Sobrien 46179968Sobrien if ($2) 46279968Sobrien retrieve(argv, ""); 46379968Sobrien } 46479968Sobrien 46579968Sobrien | LIST check_login SP pathname CRLF 46679968Sobrien { 46779968Sobrien char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL }; 46879968Sobrien 46979968Sobrien if ($2 && $4 != NULL) { 47079968Sobrien argv[2] = $4; 47179968Sobrien retrieve(argv, $4); 47279968Sobrien } 47379968Sobrien if ($4 != NULL) 47479968Sobrien free($4); 47579968Sobrien } 47679968Sobrien 47779968Sobrien | NLST check_login CRLF 47879968Sobrien { 47979968Sobrien if ($2) 48079968Sobrien send_file_list("."); 48179968Sobrien } 48279968Sobrien 48379968Sobrien | NLST check_login SP pathname CRLF 48479968Sobrien { 48579968Sobrien if ($2) 48679968Sobrien send_file_list($4); 48779968Sobrien free($4); 48879968Sobrien } 48979968Sobrien 49079968Sobrien | SITE SP HELP CRLF 49179968Sobrien { 49279968Sobrien help(sitetab, NULL); 49379968Sobrien } 49479968Sobrien 49579968Sobrien | SITE SP CHMOD SP octal_number SP pathname CRLF 49679968Sobrien { 49779968Sobrien if (check_write($7, 0)) { 49879968Sobrien if ($5 > 0777) 49979968Sobrien reply(501, 50079968Sobrien "CHMOD: Mode value must be between 0 and 0777"); 50179968Sobrien else if (chmod($7, $5) < 0) 50279968Sobrien perror_reply(550, $7); 50379968Sobrien else 50479968Sobrien reply(200, "CHMOD command successful."); 50579968Sobrien } 50679968Sobrien if ($7 != NULL) 50779968Sobrien free($7); 50879968Sobrien } 50979968Sobrien 51079968Sobrien | SITE SP HELP SP STRING CRLF 51179968Sobrien { 51279968Sobrien help(sitetab, $5); 51379968Sobrien free($5); 51479968Sobrien } 51579968Sobrien 51679968Sobrien | SITE SP IDLE check_login CRLF 51779968Sobrien { 51879968Sobrien if ($4) { 51979968Sobrien reply(200, 52079968Sobrien "Current IDLE time limit is %d seconds; max %d", 52179968Sobrien curclass.timeout, curclass.maxtimeout); 52279968Sobrien } 52379968Sobrien } 52479968Sobrien 52579968Sobrien | SITE SP IDLE check_login SP NUMBER CRLF 52679968Sobrien { 52779968Sobrien if ($4) { 52879968Sobrien if ($6 < 30 || $6 > curclass.maxtimeout) { 52979968Sobrien reply(501, 53079968Sobrien "IDLE time limit must be between 30 and %d seconds", 53179968Sobrien curclass.maxtimeout); 53279968Sobrien } else { 53379968Sobrien curclass.timeout = $6; 53479968Sobrien (void) alarm(curclass.timeout); 53579968Sobrien reply(200, 53679968Sobrien "IDLE time limit set to %d seconds", 53779968Sobrien curclass.timeout); 53879968Sobrien } 53979968Sobrien } 54079968Sobrien } 54179968Sobrien 54279968Sobrien | SITE SP RATEGET check_login CRLF 54379968Sobrien { 54479968Sobrien if ($4) { 54579968Sobrien reply(200, 54679968Sobrien "Current RATEGET is " LLF " bytes/sec", 54779968Sobrien (LLT)curclass.rateget); 54879968Sobrien } 54979968Sobrien } 55079968Sobrien 55179968Sobrien | SITE SP RATEGET check_login SP STRING CRLF 55279968Sobrien { 55379968Sobrien char *p = $6; 55479968Sobrien LLT rate; 55579968Sobrien 55679968Sobrien if ($4) { 55779968Sobrien rate = strsuftoll(p); 55879968Sobrien if (rate == -1) 55979968Sobrien reply(501, "Invalid RATEGET %s", p); 56079968Sobrien else if (curclass.maxrateget && 56179968Sobrien rate > curclass.maxrateget) 56279968Sobrien reply(501, 56379968Sobrien "RATEGET " LLF " is larger than maximum RATEGET " LLF, 56479968Sobrien (LLT)rate, 56579968Sobrien (LLT)curclass.maxrateget); 56679968Sobrien else { 56779968Sobrien curclass.rateget = rate; 56879968Sobrien reply(200, 56979968Sobrien "RATEGET set to " LLF " bytes/sec", 57079968Sobrien (LLT)curclass.rateget); 57179968Sobrien } 57279968Sobrien } 57379968Sobrien free($6); 57479968Sobrien } 57579968Sobrien 57679968Sobrien | SITE SP RATEPUT check_login CRLF 57779968Sobrien { 57879968Sobrien if ($4) { 57979968Sobrien reply(200, 58079968Sobrien "Current RATEPUT is " LLF " bytes/sec", 58179968Sobrien (LLT)curclass.rateput); 58279968Sobrien } 58379968Sobrien } 58479968Sobrien 58579968Sobrien | SITE SP RATEPUT check_login SP STRING CRLF 58679968Sobrien { 58779968Sobrien char *p = $6; 58879968Sobrien LLT rate; 58979968Sobrien 59079968Sobrien if ($4) { 59179968Sobrien rate = strsuftoll(p); 59279968Sobrien if (rate == -1) 59379968Sobrien reply(501, "Invalid RATEPUT %s", p); 59479968Sobrien else if (curclass.maxrateput && 59579968Sobrien rate > curclass.maxrateput) 59679968Sobrien reply(501, 59779968Sobrien "RATEPUT " LLF " is larger than maximum RATEPUT " LLF, 59879968Sobrien (LLT)rate, 59979968Sobrien (LLT)curclass.maxrateput); 60079968Sobrien else { 60179968Sobrien curclass.rateput = rate; 60279968Sobrien reply(200, 60379968Sobrien "RATEPUT set to " LLF " bytes/sec", 60479968Sobrien (LLT)curclass.rateput); 60579968Sobrien } 60679968Sobrien } 60779968Sobrien free($6); 60879968Sobrien } 60979968Sobrien 61079968Sobrien | SITE SP UMASK check_login CRLF 61179968Sobrien { 61279968Sobrien int oldmask; 61379968Sobrien 61479968Sobrien if ($4) { 61579968Sobrien oldmask = umask(0); 61679968Sobrien (void) umask(oldmask); 61779968Sobrien reply(200, "Current UMASK is %03o", oldmask); 61879968Sobrien } 61979968Sobrien } 62079968Sobrien 62179968Sobrien | SITE SP UMASK check_login SP octal_number CRLF 62279968Sobrien { 62379968Sobrien int oldmask; 62479968Sobrien 62579968Sobrien if ($4 && CURCLASS_FLAGS_ISSET(modify)) { 62679968Sobrien if (($6 == -1) || ($6 > 0777)) { 62779968Sobrien reply(501, "Bad UMASK value"); 62879968Sobrien } else { 62979968Sobrien oldmask = umask($6); 63079968Sobrien reply(200, 63179968Sobrien "UMASK set to %03o (was %03o)", 63279968Sobrien $6, oldmask); 63379968Sobrien } 63479968Sobrien } 63579968Sobrien } 63679968Sobrien 63779968Sobrien | SYST CRLF 63879968Sobrien { 63979968Sobrien if (EMPTYSTR(version)) 64079968Sobrien reply(215, "UNIX Type: L%d", NBBY); 64179968Sobrien else 64279968Sobrien reply(215, "UNIX Type: L%d Version: %s", NBBY, 64379968Sobrien version); 64479968Sobrien } 64579968Sobrien 64679968Sobrien | STAT check_login SP pathname CRLF 64779968Sobrien { 64879968Sobrien if ($2 && $4 != NULL) 64979968Sobrien statfilecmd($4); 65079968Sobrien if ($4 != NULL) 65179968Sobrien free($4); 65279968Sobrien } 65379968Sobrien 65479968Sobrien | STAT CRLF 65579968Sobrien { 65679968Sobrien if (is_oob) 65779968Sobrien statxfer(); 65879968Sobrien else 65979968Sobrien statcmd(); 66079968Sobrien } 66179968Sobrien 66279968Sobrien | HELP CRLF 66379968Sobrien { 66479968Sobrien help(cmdtab, NULL); 66579968Sobrien } 66679968Sobrien 66779968Sobrien | HELP SP STRING CRLF 66879968Sobrien { 66979968Sobrien char *cp = $3; 67079968Sobrien 67179968Sobrien if (strncasecmp(cp, "SITE", 4) == 0) { 67279968Sobrien cp = $3 + 4; 67379968Sobrien if (*cp == ' ') 67479968Sobrien cp++; 67579968Sobrien if (*cp) 67679968Sobrien help(sitetab, cp); 67779968Sobrien else 67879968Sobrien help(sitetab, NULL); 67979968Sobrien } else 68079968Sobrien help(cmdtab, $3); 68179968Sobrien free($3); 68279968Sobrien } 68379968Sobrien 68479968Sobrien | NOOP CRLF 68579968Sobrien { 68679968Sobrien reply(200, "NOOP command successful."); 68779968Sobrien } 68879968Sobrien 68979968Sobrien /* RFC 2228 */ 69079968Sobrien | AUTH SP mechanism_name CRLF 69179968Sobrien { 69279968Sobrien reply(502, "RFC 2228 authentication not implemented."); 69379968Sobrien free($3); 69479968Sobrien } 69579968Sobrien 69679968Sobrien | ADAT SP base64data CRLF 69779968Sobrien { 69879968Sobrien reply(503, 69979968Sobrien "Please set authentication state with AUTH."); 70079968Sobrien free($3); 70179968Sobrien } 70279968Sobrien 70379968Sobrien | PROT SP prot_code CRLF 70479968Sobrien { 70579968Sobrien reply(503, 70679968Sobrien "Please set protection buffer size with PBSZ."); 70779968Sobrien free($3); 70879968Sobrien } 70979968Sobrien 71079968Sobrien | PBSZ SP decimal_integer CRLF 71179968Sobrien { 71279968Sobrien reply(503, 71379968Sobrien "Please set authentication state with AUTH."); 71479968Sobrien } 71579968Sobrien 71679968Sobrien | CCC CRLF 71779968Sobrien { 71879968Sobrien reply(533, "No protection enabled."); 71979968Sobrien } 72079968Sobrien 72179968Sobrien | MIC SP base64data CRLF 72279968Sobrien { 72379968Sobrien reply(502, "RFC 2228 authentication not implemented."); 72479968Sobrien free($3); 72579968Sobrien } 72679968Sobrien 72779968Sobrien | CONF SP base64data CRLF 72879968Sobrien { 72979968Sobrien reply(502, "RFC 2228 authentication not implemented."); 73079968Sobrien free($3); 73179968Sobrien } 73279968Sobrien 73379968Sobrien | ENC SP base64data CRLF 73479968Sobrien { 73579968Sobrien reply(502, "RFC 2228 authentication not implemented."); 73679968Sobrien free($3); 73779968Sobrien } 73879968Sobrien 73979968Sobrien /* RFC 2389 */ 74079968Sobrien | FEAT CRLF 74179968Sobrien { 74279968Sobrien 74379968Sobrien feat(); 74479968Sobrien } 74579968Sobrien 74679968Sobrien | OPTS SP STRING CRLF 74779968Sobrien { 74879968Sobrien 74979968Sobrien opts($3); 75079968Sobrien free($3); 75179968Sobrien } 75279968Sobrien 75379968Sobrien 75479968Sobrien /* extensions from draft-ietf-ftpext-mlst-11 */ 75579968Sobrien 75679968Sobrien /* 75779968Sobrien * Return size of file in a format suitable for 75879968Sobrien * using with RESTART (we just count bytes). 75979968Sobrien */ 76079968Sobrien | SIZE check_login SP pathname CRLF 76179968Sobrien { 76279968Sobrien if ($2 && $4 != NULL) 76379968Sobrien sizecmd($4); 76479968Sobrien if ($4 != NULL) 76579968Sobrien free($4); 76679968Sobrien } 76779968Sobrien 76879968Sobrien /* 76979968Sobrien * Return modification time of file as an ISO 3307 77079968Sobrien * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 77179968Sobrien * where xxx is the fractional second (of any precision, 77279968Sobrien * not necessarily 3 digits) 77379968Sobrien */ 77479968Sobrien | MDTM check_login SP pathname CRLF 77579968Sobrien { 77679968Sobrien if ($2 && $4 != NULL) { 77779968Sobrien struct stat stbuf; 77879968Sobrien if (stat($4, &stbuf) < 0) 77979968Sobrien perror_reply(550, $4); 78079968Sobrien else if (!S_ISREG(stbuf.st_mode)) { 78179968Sobrien reply(550, "%s: not a plain file.", $4); 78279968Sobrien } else { 78379968Sobrien struct tm *t; 78479968Sobrien 78579968Sobrien t = gmtime(&stbuf.st_mtime); 78679968Sobrien reply(213, 78779968Sobrien "%04d%02d%02d%02d%02d%02d", 78879968Sobrien TM_YEAR_BASE + t->tm_year, 78979968Sobrien t->tm_mon+1, t->tm_mday, 79079968Sobrien t->tm_hour, t->tm_min, t->tm_sec); 79179968Sobrien } 79279968Sobrien } 79379968Sobrien if ($4 != NULL) 79479968Sobrien free($4); 79579968Sobrien } 79679968Sobrien 79779968Sobrien | MLST check_login SP pathname CRLF 79879968Sobrien { 79979968Sobrien if ($2 && $4 != NULL) 80079968Sobrien mlst($4); 80179968Sobrien if ($4 != NULL) 80279968Sobrien free($4); 80379968Sobrien } 80479968Sobrien 80579968Sobrien | MLST check_login CRLF 80679968Sobrien { 80779968Sobrien mlst(NULL); 80879968Sobrien } 80979968Sobrien 81079968Sobrien | MLSD check_login SP pathname CRLF 81179968Sobrien { 81279968Sobrien if ($2 && $4 != NULL) 81379968Sobrien mlsd($4); 81479968Sobrien if ($4 != NULL) 81579968Sobrien free($4); 81679968Sobrien } 81779968Sobrien 81879968Sobrien | MLSD check_login CRLF 81979968Sobrien { 82079968Sobrien mlsd(NULL); 82179968Sobrien } 82279968Sobrien 82379968Sobrien | error CRLF 82479968Sobrien { 82579968Sobrien yyerrok; 82679968Sobrien } 82779968Sobrien ; 82879968Sobrien 82979968Sobrienrcmd 83079968Sobrien : REST check_login SP byte_size CRLF 83179968Sobrien { 83279968Sobrien if ($2) { 83379968Sobrien fromname = NULL; 83479968Sobrien restart_point = $4; /* XXX: $4 is only "int" */ 83579968Sobrien reply(350, 83679968Sobrien "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.", 83779968Sobrien (LLT)restart_point); 83879968Sobrien } 83979968Sobrien } 84079968Sobrien 84179968Sobrien | RNFR SP pathname CRLF 84279968Sobrien { 84379968Sobrien restart_point = (off_t) 0; 84479968Sobrien if (check_write($3, 0)) 84579968Sobrien fromname = renamefrom($3); 84679968Sobrien if ($3 != NULL) 84779968Sobrien free($3); 84879968Sobrien } 84979968Sobrien ; 85079968Sobrien 85179968Sobrienusername 85279968Sobrien : STRING 85379968Sobrien ; 85479968Sobrien 85579968Sobrienpassword 85679968Sobrien : /* empty */ 85779968Sobrien { 85879968Sobrien $$ = (char *)calloc(1, sizeof(char)); 85979968Sobrien } 86079968Sobrien 86179968Sobrien | STRING 86279968Sobrien ; 86379968Sobrien 86479968Sobrienbyte_size 86579968Sobrien : NUMBER 86679968Sobrien ; 86779968Sobrien 86879968Sobrienhost_port 86979968Sobrien : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 87079968Sobrien NUMBER COMMA NUMBER 87179968Sobrien { 87279968Sobrien char *a, *p; 87379968Sobrien 87479968Sobrien memset(&data_dest, 0, sizeof(data_dest)); 87579968Sobrien data_dest.su_len = sizeof(struct sockaddr_in); 87679968Sobrien data_dest.su_family = AF_INET; 87779968Sobrien p = (char *)&data_dest.su_port; 87879968Sobrien p[0] = $9; p[1] = $11; 87979968Sobrien a = (char *)&data_dest.su_addr; 88079968Sobrien a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 88179968Sobrien } 88279968Sobrien ; 88379968Sobrien 88479968Sobrienhost_long_port4 88579968Sobrien : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 88679968Sobrien NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 88779968Sobrien NUMBER 88879968Sobrien { 88979968Sobrien char *a, *p; 89079968Sobrien 89179968Sobrien memset(&data_dest, 0, sizeof(data_dest)); 89279968Sobrien data_dest.su_len = sizeof(struct sockaddr_in); 89379968Sobrien data_dest.su_family = AF_INET; 89479968Sobrien p = (char *)&data_dest.su_port; 89579968Sobrien p[0] = $15; p[1] = $17; 89679968Sobrien a = (char *)&data_dest.su_addr; 89779968Sobrien a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 89879968Sobrien 89979968Sobrien /* reject invalid LPRT command */ 90079968Sobrien if ($1 != 4 || $3 != 4 || $13 != 2) 90179968Sobrien memset(&data_dest, 0, sizeof(data_dest)); 90279968Sobrien } 90379968Sobrien ; 90479968Sobrien 90579968Sobrienhost_long_port6 90679968Sobrien : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 90779968Sobrien NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 90879968Sobrien NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 90979968Sobrien NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 91079968Sobrien NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 91179968Sobrien NUMBER 91279968Sobrien { 91379968Sobrien#ifdef INET6 91479968Sobrien char *a, *p; 91579968Sobrien 91679968Sobrien memset(&data_dest, 0, sizeof(data_dest)); 91779968Sobrien data_dest.su_len = sizeof(struct sockaddr_in6); 91879968Sobrien data_dest.su_family = AF_INET6; 91979968Sobrien p = (char *)&data_dest.su_port; 92079968Sobrien p[0] = $39; p[1] = $41; 92179968Sobrien a = (char *)&data_dest.si_su.su_sin6.sin6_addr; 92279968Sobrien a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 92379968Sobrien a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19; 92479968Sobrien a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27; 92579968Sobrien a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35; 92679968Sobrien if (his_addr.su_family == AF_INET6) { 92779968Sobrien /* XXX: more sanity checks! */ 92879968Sobrien data_dest.su_scope_id = his_addr.su_scope_id; 92979968Sobrien } 93079968Sobrien#else 93179968Sobrien memset(&data_dest, 0, sizeof(data_dest)); 93279968Sobrien#endif /* INET6 */ 93379968Sobrien /* reject invalid LPRT command */ 93479968Sobrien if ($1 != 6 || $3 != 16 || $37 != 2) 93579968Sobrien memset(&data_dest, 0, sizeof(data_dest)); 93679968Sobrien } 93779968Sobrien ; 93879968Sobrien 93979968Sobrienform_code 94079968Sobrien : N 94179968Sobrien { 94279968Sobrien $$ = FORM_N; 94379968Sobrien } 94479968Sobrien 94579968Sobrien | T 94679968Sobrien { 94779968Sobrien $$ = FORM_T; 94879968Sobrien } 94979968Sobrien 95079968Sobrien | C 95179968Sobrien { 95279968Sobrien $$ = FORM_C; 95379968Sobrien } 95479968Sobrien ; 95579968Sobrien 95679968Sobrientype_code 95779968Sobrien : A 95879968Sobrien { 95979968Sobrien cmd_type = TYPE_A; 96079968Sobrien cmd_form = FORM_N; 96179968Sobrien } 96279968Sobrien 96379968Sobrien | A SP form_code 96479968Sobrien { 96579968Sobrien cmd_type = TYPE_A; 96679968Sobrien cmd_form = $3; 96779968Sobrien } 96879968Sobrien 96979968Sobrien | E 97079968Sobrien { 97179968Sobrien cmd_type = TYPE_E; 97279968Sobrien cmd_form = FORM_N; 97379968Sobrien } 97479968Sobrien 97579968Sobrien | E SP form_code 97679968Sobrien { 97779968Sobrien cmd_type = TYPE_E; 97879968Sobrien cmd_form = $3; 97979968Sobrien } 98079968Sobrien 98179968Sobrien | I 98279968Sobrien { 98379968Sobrien cmd_type = TYPE_I; 98479968Sobrien } 98579968Sobrien 98679968Sobrien | L 98779968Sobrien { 98879968Sobrien cmd_type = TYPE_L; 98979968Sobrien cmd_bytesz = NBBY; 99079968Sobrien } 99179968Sobrien 99279968Sobrien | L SP byte_size 99379968Sobrien { 99479968Sobrien cmd_type = TYPE_L; 99579968Sobrien cmd_bytesz = $3; 99679968Sobrien } 99779968Sobrien 99879968Sobrien /* this is for a bug in the BBN ftp */ 99979968Sobrien | L byte_size 100079968Sobrien { 100179968Sobrien cmd_type = TYPE_L; 100279968Sobrien cmd_bytesz = $2; 100379968Sobrien } 100479968Sobrien ; 100579968Sobrien 100679968Sobrienstruct_code 100779968Sobrien : F 100879968Sobrien { 100979968Sobrien $$ = STRU_F; 101079968Sobrien } 101179968Sobrien 101279968Sobrien | R 101379968Sobrien { 101479968Sobrien $$ = STRU_R; 101579968Sobrien } 101679968Sobrien 101779968Sobrien | P 101879968Sobrien { 101979968Sobrien $$ = STRU_P; 102079968Sobrien } 102179968Sobrien ; 102279968Sobrien 102379968Sobrienmode_code 102479968Sobrien : S 102579968Sobrien { 102679968Sobrien $$ = MODE_S; 102779968Sobrien } 102879968Sobrien 102979968Sobrien | B 103079968Sobrien { 103179968Sobrien $$ = MODE_B; 103279968Sobrien } 103379968Sobrien 103479968Sobrien | C 103579968Sobrien { 103679968Sobrien $$ = MODE_C; 103779968Sobrien } 103879968Sobrien ; 103979968Sobrien 104079968Sobrienpathname 104179968Sobrien : pathstring 104279968Sobrien { 104379968Sobrien /* 104479968Sobrien * Problem: this production is used for all pathname 104579968Sobrien * processing, but only gives a 550 error reply. 104679968Sobrien * This is a valid reply in some cases but not in 104779968Sobrien * others. 104879968Sobrien */ 104979968Sobrien if (logged_in && $1 && *$1 == '~') { 105079968Sobrien char *path, *home, *result; 105179968Sobrien size_t len; 105279968Sobrien 105379968Sobrien path = strchr($1 + 1, '/'); 105479968Sobrien if (path != NULL) 105579968Sobrien *path++ = '\0'; 105679968Sobrien if ($1[1] == '\0') 105779968Sobrien home = homedir; 105879968Sobrien else { 105992282Sobrien struct passwd *hpw; 106079968Sobrien 106192282Sobrien if ((hpw = getpwnam($1 + 1)) != NULL) 106292282Sobrien home = hpw->pw_dir; 106379968Sobrien else 106479968Sobrien home = $1; 106579968Sobrien } 106679968Sobrien len = strlen(home) + 1; 106779968Sobrien if (path != NULL) 106879968Sobrien len += strlen(path) + 1; 106979968Sobrien if ((result = malloc(len)) == NULL) 107079968Sobrien fatal("Local resource failure: malloc"); 107179968Sobrien strlcpy(result, home, len); 107279968Sobrien if (path != NULL) { 107379968Sobrien strlcat(result, "/", len); 107479968Sobrien strlcat(result, path, len); 107579968Sobrien } 107679968Sobrien $$ = result; 107779968Sobrien free($1); 107879968Sobrien } else 107979968Sobrien $$ = $1; 108079968Sobrien } 108179968Sobrien ; 108279968Sobrien 108379968Sobrienpathstring 108479968Sobrien : STRING 108579968Sobrien ; 108679968Sobrien 108779968Sobrienoctal_number 108879968Sobrien : NUMBER 108979968Sobrien { 109079968Sobrien int ret, dec, multby, digit; 109179968Sobrien 109279968Sobrien /* 109379968Sobrien * Convert a number that was read as decimal number 109479968Sobrien * to what it would be if it had been read as octal. 109579968Sobrien */ 109679968Sobrien dec = $1; 109779968Sobrien multby = 1; 109879968Sobrien ret = 0; 109979968Sobrien while (dec) { 110079968Sobrien digit = dec%10; 110179968Sobrien if (digit > 7) { 110279968Sobrien ret = -1; 110379968Sobrien break; 110479968Sobrien } 110579968Sobrien ret += digit * multby; 110679968Sobrien multby *= 8; 110779968Sobrien dec /= 10; 110879968Sobrien } 110979968Sobrien $$ = ret; 111079968Sobrien } 111179968Sobrien ; 111279968Sobrien 111379968Sobrienmechanism_name 111479968Sobrien : STRING 111579968Sobrien ; 111679968Sobrien 111779968Sobrienbase64data 111879968Sobrien : STRING 111979968Sobrien ; 112079968Sobrien 112179968Sobrienprot_code 112279968Sobrien : STRING 112379968Sobrien ; 112479968Sobrien 112579968Sobriendecimal_integer 112679968Sobrien : NUMBER 112779968Sobrien ; 112879968Sobrien 112979968Sobriencheck_login 113079968Sobrien : /* empty */ 113179968Sobrien { 113279968Sobrien if (logged_in) 113379968Sobrien $$ = 1; 113479968Sobrien else { 113579968Sobrien reply(530, "Please login with USER and PASS."); 113679968Sobrien $$ = 0; 113779968Sobrien hasyyerrored = 1; 113879968Sobrien } 113979968Sobrien } 114079968Sobrien ; 114179968Sobrien 114279968Sobrien%% 114379968Sobrien 114479968Sobrien#define CMD 0 /* beginning of command */ 114579968Sobrien#define ARGS 1 /* expect miscellaneous arguments */ 114679968Sobrien#define STR1 2 /* expect SP followed by STRING */ 114779968Sobrien#define STR2 3 /* expect STRING */ 114879968Sobrien#define OSTR 4 /* optional SP then STRING */ 114979968Sobrien#define ZSTR1 5 /* SP then optional STRING */ 115079968Sobrien#define ZSTR2 6 /* optional STRING after SP */ 115179968Sobrien#define SITECMD 7 /* SITE command */ 115279968Sobrien#define NSTR 8 /* Number followed by a string */ 115379968Sobrien#define NOARGS 9 /* No arguments allowed */ 115479968Sobrien#define EOLN 10 /* End of line */ 115579968Sobrien 115679968Sobrienstruct tab cmdtab[] = { 115779968Sobrien /* From RFC 959, in order defined (5.3.1) */ 115879968Sobrien { "USER", USER, STR1, 1, "<sp> username" }, 115979968Sobrien { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 116079968Sobrien { "ACCT", ACCT, STR1, 0, "(specify account)" }, 116179968Sobrien { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 116279968Sobrien { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)" }, 116379968Sobrien { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 116479968Sobrien { "QUIT", QUIT, NOARGS, 1, "(terminate service)" }, 116579968Sobrien { "REIN", REIN, NOARGS, 0, "(reinitialize server state)" }, 116679968Sobrien { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 116779968Sobrien { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 116879968Sobrien { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 116979968Sobrien { "PASV", PASV, NOARGS, 1, "(set server in passive mode)" }, 117079968Sobrien { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 117179968Sobrien { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 117279968Sobrien { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 117379968Sobrien { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 117479968Sobrien { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 117579968Sobrien { "RETR", RETR, STR1, 1, "<sp> file-name" }, 117679968Sobrien { "STOR", STOR, STR1, 1, "<sp> file-name" }, 117779968Sobrien { "STOU", STOU, STR1, 1, "<sp> file-name" }, 117879968Sobrien { "APPE", APPE, STR1, 1, "<sp> file-name" }, 117979968Sobrien { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 118079968Sobrien { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 118179968Sobrien { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 118279968Sobrien { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 118379968Sobrien { "ABOR", ABOR, NOARGS, 4, "(abort operation)" }, 118479968Sobrien { "DELE", DELE, STR1, 1, "<sp> file-name" }, 118579968Sobrien { "RMD", RMD, STR1, 1, "<sp> path-name" }, 118679968Sobrien { "MKD", MKD, STR1, 1, "<sp> path-name" }, 118779968Sobrien { "PWD", PWD, NOARGS, 1, "(return current directory)" }, 118879968Sobrien { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 118979968Sobrien { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 119079968Sobrien { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 119179968Sobrien { "SYST", SYST, NOARGS, 1, "(get type of operating system)" }, 119279968Sobrien { "STAT", STAT, OSTR, 4, "[ <sp> path-name ]" }, 119379968Sobrien { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 119479968Sobrien { "NOOP", NOOP, NOARGS, 2, "" }, 119579968Sobrien 119679968Sobrien /* From RFC 2228, in order defined */ 119779968Sobrien { "AUTH", AUTH, STR1, 1, "<sp> mechanism-name" }, 119879968Sobrien { "ADAT", ADAT, STR1, 1, "<sp> base-64-data" }, 119979968Sobrien { "PROT", PROT, STR1, 1, "<sp> prot-code" }, 120079968Sobrien { "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer" }, 120179968Sobrien { "CCC", CCC, NOARGS, 1, "(Disable data protection)" }, 120279968Sobrien { "MIC", MIC, STR1, 4, "<sp> base64data" }, 120379968Sobrien { "CONF", CONF, STR1, 4, "<sp> base64data" }, 120479968Sobrien { "ENC", ENC, STR1, 4, "<sp> base64data" }, 120579968Sobrien 120679968Sobrien /* From RFC 2389, in order defined */ 120779968Sobrien { "FEAT", FEAT, NOARGS, 1, "(display extended features)" }, 120879968Sobrien { "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]" }, 120979968Sobrien 121079968Sobrien /* from draft-ietf-ftpext-mlst-11 */ 121179968Sobrien { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 121279968Sobrien { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 121379968Sobrien { "MLST", MLST, OSTR, 2, "[ <sp> path-name ]" }, 121479968Sobrien { "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]" }, 121579968Sobrien 121679968Sobrien /* obsolete commands */ 121779968Sobrien { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 121879968Sobrien { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 121979968Sobrien { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 122079968Sobrien { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 122179968Sobrien { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 122279968Sobrien { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 122379968Sobrien { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 122479968Sobrien { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)" }, 122579968Sobrien { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 122679968Sobrien { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 122779968Sobrien { "XPWD", PWD, NOARGS, 1, "(return current directory)" }, 122879968Sobrien { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 122979968Sobrien 123079968Sobrien { NULL, 0, 0, 0, 0 } 123179968Sobrien}; 123279968Sobrien 123379968Sobrienstruct tab sitetab[] = { 123479968Sobrien { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 123579968Sobrien { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 123679968Sobrien { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 123779968Sobrien { "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]" }, 123879968Sobrien { "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]" }, 123979968Sobrien { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 124079968Sobrien { NULL, 0, 0, 0, NULL } 124179968Sobrien}; 124279968Sobrien 124379968Sobrienstatic int check_write(const char *, int); 124479968Sobrienstatic void help(struct tab *, const char *); 124579968Sobrienstatic void port_check(const char *, int); 124679968Sobrienstatic void toolong(int); 124779968Sobrienstatic int yylex(void); 124879968Sobrien 124979968Sobrienextern int epsvall; 125079968Sobrien 125179968Sobrien/* 125279968Sobrien * Check if a filename is allowed to be modified (isupload == 0) or 125379968Sobrien * uploaded (isupload == 1), and if necessary, check the filename is `sane'. 125479968Sobrien */ 125579968Sobrienstatic int 125679968Sobriencheck_write(const char *file, int isupload) 125779968Sobrien{ 125879968Sobrien if (file == NULL) 125979968Sobrien return (0); 126079968Sobrien if (! logged_in) { 126179968Sobrien reply(530, "Please login with USER and PASS."); 126279968Sobrien return (0); 126379968Sobrien } 126479968Sobrien /* checking modify */ 126579968Sobrien if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) { 126679968Sobrien reply(502, "No permission to use this command."); 126779968Sobrien return (0); 126879968Sobrien } 126979968Sobrien /* checking upload */ 127079968Sobrien if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) { 127179968Sobrien reply(502, "No permission to use this command."); 127279968Sobrien return (0); 127379968Sobrien } 127479968Sobrien /* checking sanenames */ 127579968Sobrien if (CURCLASS_FLAGS_ISSET(sanenames)) { 127679968Sobrien const char *p; 127779968Sobrien 127879968Sobrien if (file[0] == '.') 127979968Sobrien goto insane_name; 128079968Sobrien for (p = file; *p; p++) { 128179968Sobrien if (isalnum(*p) || *p == '-' || *p == '+' || 128279968Sobrien *p == ',' || *p == '.' || *p == '_') 128379968Sobrien continue; 128479968Sobrien insane_name: 128579968Sobrien reply(553, "File name `%s' not allowed.", file); 128679968Sobrien return (0); 128779968Sobrien } 128879968Sobrien } 128979968Sobrien return (1); 129079968Sobrien} 129179968Sobrien 129279968Sobrienstruct tab * 129379968Sobrienlookup(struct tab *p, const char *cmd) 129479968Sobrien{ 129579968Sobrien 129679968Sobrien for (; p->name != NULL; p++) 129779968Sobrien if (strcasecmp(cmd, p->name) == 0) 129879968Sobrien return (p); 129979968Sobrien return (0); 130079968Sobrien} 130179968Sobrien 130279968Sobrien#include <arpa/telnet.h> 130379968Sobrien 130479968Sobrien/* 130579968Sobrien * getline - a hacked up version of fgets to ignore TELNET escape codes. 130679968Sobrien */ 130779968Sobrienchar * 130879968Sobriengetline(char *s, int n, FILE *iop) 130979968Sobrien{ 131079968Sobrien int c; 131179968Sobrien char *cs; 131279968Sobrien 131379968Sobrien cs = s; 131479968Sobrien/* tmpline may contain saved command from urgent mode interruption */ 131579968Sobrien for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 131679968Sobrien *cs++ = tmpline[c]; 131779968Sobrien if (tmpline[c] == '\n') { 131879968Sobrien *cs++ = '\0'; 131979968Sobrien if (debug) 132079968Sobrien syslog(LOG_DEBUG, "command: %s", s); 132179968Sobrien tmpline[0] = '\0'; 132279968Sobrien return(s); 132379968Sobrien } 132479968Sobrien if (c == 0) 132579968Sobrien tmpline[0] = '\0'; 132679968Sobrien } 132779968Sobrien while ((c = getc(iop)) != EOF) { 132879968Sobrien total_bytes++; 132979968Sobrien total_bytes_in++; 133079968Sobrien c &= 0377; 133179968Sobrien if (c == IAC) { 133279968Sobrien if ((c = getc(iop)) != EOF) { 133379968Sobrien total_bytes++; 133479968Sobrien total_bytes_in++; 133579968Sobrien c &= 0377; 133679968Sobrien switch (c) { 133779968Sobrien case WILL: 133879968Sobrien case WONT: 133979968Sobrien c = getc(iop); 134079968Sobrien total_bytes++; 134179968Sobrien total_bytes_in++; 134279968Sobrien cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c); 134379968Sobrien (void) fflush(stdout); 134479968Sobrien continue; 134579968Sobrien case DO: 134679968Sobrien case DONT: 134779968Sobrien c = getc(iop); 134879968Sobrien total_bytes++; 134979968Sobrien total_bytes_in++; 135079968Sobrien cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c); 135179968Sobrien (void) fflush(stdout); 135279968Sobrien continue; 135379968Sobrien case IAC: 135479968Sobrien break; 135579968Sobrien default: 135679968Sobrien continue; /* ignore command */ 135779968Sobrien } 135879968Sobrien } 135979968Sobrien } 136079968Sobrien *cs++ = c; 136179968Sobrien if (--n <= 0 || c == '\n') 136279968Sobrien break; 136379968Sobrien } 136479968Sobrien if (c == EOF && cs == s) 136579968Sobrien return (NULL); 136679968Sobrien *cs++ = '\0'; 136779968Sobrien if (debug) { 136879968Sobrien if ((curclass.type != CLASS_GUEST && 136979968Sobrien strncasecmp(s, "PASS ", 5) == 0) || 137079968Sobrien strncasecmp(s, "ACCT ", 5) == 0) { 137179968Sobrien /* Don't syslog passwords */ 137279968Sobrien syslog(LOG_DEBUG, "command: %.4s ???", s); 137379968Sobrien } else { 137479968Sobrien char *cp; 137579968Sobrien int len; 137679968Sobrien 137779968Sobrien /* Don't syslog trailing CR-LF */ 137879968Sobrien len = strlen(s); 137979968Sobrien cp = s + len - 1; 138079968Sobrien while (cp >= s && (*cp == '\n' || *cp == '\r')) { 138179968Sobrien --cp; 138279968Sobrien --len; 138379968Sobrien } 138479968Sobrien syslog(LOG_DEBUG, "command: %.*s", len, s); 138579968Sobrien } 138679968Sobrien } 138779968Sobrien return (s); 138879968Sobrien} 138979968Sobrien 139079968Sobrienstatic void 139179968Sobrientoolong(int signo) 139279968Sobrien{ 139379968Sobrien 139479968Sobrien reply(421, 139579968Sobrien "Timeout (%d seconds): closing control connection.", 139679968Sobrien curclass.timeout); 139779968Sobrien if (logging) 139879968Sobrien syslog(LOG_INFO, "User %s timed out after %d seconds", 139979968Sobrien (pw ? pw->pw_name : "unknown"), curclass.timeout); 140079968Sobrien dologout(1); 140179968Sobrien} 140279968Sobrien 140379968Sobrienvoid 140479968Sobrienftp_handle_line(char *cp) 140579968Sobrien{ 140679968Sobrien 140779968Sobrien cmdp = cp; 140879968Sobrien yyparse(); 140979968Sobrien} 141079968Sobrien 141179968Sobrienvoid 141279968Sobrienftp_loop(void) 141379968Sobrien{ 141479968Sobrien 141579968Sobrien while (1) { 141679968Sobrien (void) signal(SIGALRM, toolong); 141779968Sobrien (void) alarm(curclass.timeout); 141879968Sobrien if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 141979968Sobrien reply(221, "You could at least say goodbye."); 142079968Sobrien dologout(0); 142179968Sobrien } 142279968Sobrien (void) alarm(0); 142379968Sobrien ftp_handle_line(cbuf); 142479968Sobrien } 142579968Sobrien /*NOTREACHED*/ 142679968Sobrien} 142779968Sobrien 142879968Sobrienstatic int 142979968Sobrienyylex(void) 143079968Sobrien{ 143179968Sobrien static int cpos, state; 143279968Sobrien char *cp, *cp2; 143379968Sobrien struct tab *p; 143479968Sobrien int n; 143579968Sobrien char c; 143679968Sobrien 143779968Sobrien switch (state) { 143879968Sobrien 143979968Sobrien case CMD: 144079968Sobrien hasyyerrored = 0; 144179968Sobrien if ((cp = strchr(cmdp, '\r'))) { 144279968Sobrien *cp = '\0'; 144379968Sobrien#if HAVE_SETPROCTITLE 144479968Sobrien if (strncasecmp(cmdp, "PASS", 4) != 0 && 144579968Sobrien strncasecmp(cmdp, "ACCT", 4) != 0) 144679968Sobrien setproctitle("%s: %s", proctitle, cmdp); 144779968Sobrien#endif /* HAVE_SETPROCTITLE */ 144879968Sobrien *cp++ = '\n'; 144979968Sobrien *cp = '\0'; 145079968Sobrien } 145179968Sobrien if ((cp = strpbrk(cmdp, " \n"))) 145279968Sobrien cpos = cp - cmdp; 145379968Sobrien if (cpos == 0) 145479968Sobrien cpos = 4; 145579968Sobrien c = cmdp[cpos]; 145679968Sobrien cmdp[cpos] = '\0'; 145779968Sobrien p = lookup(cmdtab, cmdp); 145879968Sobrien cmdp[cpos] = c; 145979968Sobrien if (p != NULL) { 146079968Sobrien if (is_oob && ! CMD_OOB(p)) { 146179968Sobrien /* command will be handled in-band */ 146279968Sobrien return (0); 146379968Sobrien } else if (! CMD_IMPLEMENTED(p)) { 146479968Sobrien reply(502, "%s command not implemented.", 146579968Sobrien p->name); 146679968Sobrien hasyyerrored = 1; 146779968Sobrien break; 146879968Sobrien } 146979968Sobrien state = p->state; 147079968Sobrien yylval.s = p->name; 147179968Sobrien return (p->token); 147279968Sobrien } 147379968Sobrien break; 147479968Sobrien 147579968Sobrien case SITECMD: 147679968Sobrien if (cmdp[cpos] == ' ') { 147779968Sobrien cpos++; 147879968Sobrien return (SP); 147979968Sobrien } 148079968Sobrien cp = &cmdp[cpos]; 148179968Sobrien if ((cp2 = strpbrk(cp, " \n"))) 148279968Sobrien cpos = cp2 - cmdp; 148379968Sobrien c = cmdp[cpos]; 148479968Sobrien cmdp[cpos] = '\0'; 148579968Sobrien p = lookup(sitetab, cp); 148679968Sobrien cmdp[cpos] = c; 148779968Sobrien if (p != NULL) { 148879968Sobrien if (!CMD_IMPLEMENTED(p)) { 148979968Sobrien reply(502, "SITE %s command not implemented.", 149079968Sobrien p->name); 149179968Sobrien hasyyerrored = 1; 149279968Sobrien break; 149379968Sobrien } 149479968Sobrien state = p->state; 149579968Sobrien yylval.s = p->name; 149679968Sobrien return (p->token); 149779968Sobrien } 149879968Sobrien break; 149979968Sobrien 150079968Sobrien case OSTR: 150179968Sobrien if (cmdp[cpos] == '\n') { 150279968Sobrien state = EOLN; 150379968Sobrien return (CRLF); 150479968Sobrien } 150579968Sobrien /* FALLTHROUGH */ 150679968Sobrien 150779968Sobrien case STR1: 150879968Sobrien case ZSTR1: 150979968Sobrien dostr1: 151079968Sobrien if (cmdp[cpos] == ' ') { 151179968Sobrien cpos++; 151279968Sobrien state = state == OSTR ? STR2 : state+1; 151379968Sobrien return (SP); 151479968Sobrien } 151579968Sobrien break; 151679968Sobrien 151779968Sobrien case ZSTR2: 151879968Sobrien if (cmdp[cpos] == '\n') { 151979968Sobrien state = EOLN; 152079968Sobrien return (CRLF); 152179968Sobrien } 152279968Sobrien /* FALLTHROUGH */ 152379968Sobrien 152479968Sobrien case STR2: 152579968Sobrien cp = &cmdp[cpos]; 152679968Sobrien n = strlen(cp); 152779968Sobrien cpos += n - 1; 152879968Sobrien /* 152979968Sobrien * Make sure the string is nonempty and \n terminated. 153079968Sobrien */ 153179968Sobrien if (n > 1 && cmdp[cpos] == '\n') { 153279968Sobrien cmdp[cpos] = '\0'; 153379968Sobrien yylval.s = xstrdup(cp); 153479968Sobrien cmdp[cpos] = '\n'; 153579968Sobrien state = ARGS; 153679968Sobrien return (STRING); 153779968Sobrien } 153879968Sobrien break; 153979968Sobrien 154079968Sobrien case NSTR: 154179968Sobrien if (cmdp[cpos] == ' ') { 154279968Sobrien cpos++; 154379968Sobrien return (SP); 154479968Sobrien } 154579968Sobrien if (isdigit(cmdp[cpos])) { 154679968Sobrien cp = &cmdp[cpos]; 154779968Sobrien while (isdigit(cmdp[++cpos])) 154879968Sobrien ; 154979968Sobrien c = cmdp[cpos]; 155079968Sobrien cmdp[cpos] = '\0'; 155179968Sobrien yylval.i = atoi(cp); 155279968Sobrien cmdp[cpos] = c; 155379968Sobrien state = STR1; 155479968Sobrien return (NUMBER); 155579968Sobrien } 155679968Sobrien state = STR1; 155779968Sobrien goto dostr1; 155879968Sobrien 155979968Sobrien case ARGS: 156079968Sobrien if (isdigit(cmdp[cpos])) { 156179968Sobrien cp = &cmdp[cpos]; 156279968Sobrien while (isdigit(cmdp[++cpos])) 156379968Sobrien ; 156479968Sobrien c = cmdp[cpos]; 156579968Sobrien cmdp[cpos] = '\0'; 156679968Sobrien yylval.i = atoi(cp); 156779968Sobrien cmdp[cpos] = c; 156879968Sobrien return (NUMBER); 156979968Sobrien } 157079968Sobrien if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0 157179968Sobrien && !isalnum(cmdp[cpos + 3])) { 157279968Sobrien yylval.s = xstrdup("ALL"); 157379968Sobrien cpos += 3; 157479968Sobrien return ALL; 157579968Sobrien } 157679968Sobrien switch (cmdp[cpos++]) { 157779968Sobrien 157879968Sobrien case '\n': 157979968Sobrien state = EOLN; 158079968Sobrien return (CRLF); 158179968Sobrien 158279968Sobrien case ' ': 158379968Sobrien return (SP); 158479968Sobrien 158579968Sobrien case ',': 158679968Sobrien return (COMMA); 158779968Sobrien 158879968Sobrien case 'A': 158979968Sobrien case 'a': 159079968Sobrien return (A); 159179968Sobrien 159279968Sobrien case 'B': 159379968Sobrien case 'b': 159479968Sobrien return (B); 159579968Sobrien 159679968Sobrien case 'C': 159779968Sobrien case 'c': 159879968Sobrien return (C); 159979968Sobrien 160079968Sobrien case 'E': 160179968Sobrien case 'e': 160279968Sobrien return (E); 160379968Sobrien 160479968Sobrien case 'F': 160579968Sobrien case 'f': 160679968Sobrien return (F); 160779968Sobrien 160879968Sobrien case 'I': 160979968Sobrien case 'i': 161079968Sobrien return (I); 161179968Sobrien 161279968Sobrien case 'L': 161379968Sobrien case 'l': 161479968Sobrien return (L); 161579968Sobrien 161679968Sobrien case 'N': 161779968Sobrien case 'n': 161879968Sobrien return (N); 161979968Sobrien 162079968Sobrien case 'P': 162179968Sobrien case 'p': 162279968Sobrien return (P); 162379968Sobrien 162479968Sobrien case 'R': 162579968Sobrien case 'r': 162679968Sobrien return (R); 162779968Sobrien 162879968Sobrien case 'S': 162979968Sobrien case 's': 163079968Sobrien return (S); 163179968Sobrien 163279968Sobrien case 'T': 163379968Sobrien case 't': 163479968Sobrien return (T); 163579968Sobrien 163679968Sobrien } 163779968Sobrien break; 163879968Sobrien 163979968Sobrien case NOARGS: 164079968Sobrien if (cmdp[cpos] == '\n') { 164179968Sobrien state = EOLN; 164279968Sobrien return (CRLF); 164379968Sobrien } 164479968Sobrien c = cmdp[cpos]; 164579968Sobrien cmdp[cpos] = '\0'; 164679968Sobrien reply(501, "'%s' command does not take any arguments.", cmdp); 164779968Sobrien hasyyerrored = 1; 164879968Sobrien cmdp[cpos] = c; 164979968Sobrien break; 165079968Sobrien 165179968Sobrien case EOLN: 165279968Sobrien state = CMD; 165379968Sobrien return (0); 165479968Sobrien 165579968Sobrien default: 165679968Sobrien fatal("Unknown state in scanner."); 165779968Sobrien } 165879968Sobrien yyerror(NULL); 165979968Sobrien state = CMD; 166079968Sobrien is_oob = 0; 166179968Sobrien longjmp(errcatch, 0); 166279968Sobrien /* NOTREACHED */ 166379968Sobrien} 166479968Sobrien 166579968Sobrien/* ARGSUSED */ 166679968Sobrienvoid 166779968Sobrienyyerror(char *s) 166879968Sobrien{ 166979968Sobrien char *cp; 167079968Sobrien 167179968Sobrien if (hasyyerrored || is_oob) 167279968Sobrien return; 167379968Sobrien if ((cp = strchr(cmdp,'\n')) != NULL) 167479968Sobrien *cp = '\0'; 167579968Sobrien reply(500, "'%s': command not understood.", cmdp); 167679968Sobrien hasyyerrored = 1; 167779968Sobrien} 167879968Sobrien 167979968Sobrienstatic void 168079968Sobrienhelp(struct tab *ctab, const char *s) 168179968Sobrien{ 168279968Sobrien struct tab *c; 168379968Sobrien int width, NCMDS; 168492282Sobrien char *htype; 168579968Sobrien 168679968Sobrien if (ctab == sitetab) 168792282Sobrien htype = "SITE "; 168879968Sobrien else 168992282Sobrien htype = ""; 169079968Sobrien width = 0, NCMDS = 0; 169179968Sobrien for (c = ctab; c->name != NULL; c++) { 169279968Sobrien int len = strlen(c->name); 169379968Sobrien 169479968Sobrien if (len > width) 169579968Sobrien width = len; 169679968Sobrien NCMDS++; 169779968Sobrien } 169879968Sobrien width = (width + 8) &~ 7; 169979968Sobrien if (s == 0) { 170079968Sobrien int i, j, w; 170179968Sobrien int columns, lines; 170279968Sobrien 170379968Sobrien reply(-214, "%s", ""); 170492282Sobrien reply(0, "The following %scommands are recognized.", htype); 170579968Sobrien reply(0, "(`-' = not implemented, `+' = supports options)"); 170679968Sobrien columns = 76 / width; 170779968Sobrien if (columns == 0) 170879968Sobrien columns = 1; 170979968Sobrien lines = (NCMDS + columns - 1) / columns; 171079968Sobrien for (i = 0; i < lines; i++) { 171179968Sobrien cprintf(stdout, " "); 171279968Sobrien for (j = 0; j < columns; j++) { 171379968Sobrien c = ctab + j * lines + i; 171479968Sobrien cprintf(stdout, "%s", c->name); 171579968Sobrien w = strlen(c->name); 171679968Sobrien if (! CMD_IMPLEMENTED(c)) { 171779968Sobrien CPUTC('-', stdout); 171879968Sobrien w++; 171979968Sobrien } 172079968Sobrien if (CMD_HAS_OPTIONS(c)) { 172179968Sobrien CPUTC('+', stdout); 172279968Sobrien w++; 172379968Sobrien } 172479968Sobrien if (c + lines >= &ctab[NCMDS]) 172579968Sobrien break; 172679968Sobrien while (w < width) { 172779968Sobrien CPUTC(' ', stdout); 172879968Sobrien w++; 172979968Sobrien } 173079968Sobrien } 173179968Sobrien cprintf(stdout, "\r\n"); 173279968Sobrien } 173379968Sobrien (void) fflush(stdout); 173479968Sobrien reply(214, "Direct comments to ftp-bugs@%s.", hostname); 173579968Sobrien return; 173679968Sobrien } 173779968Sobrien c = lookup(ctab, s); 173879968Sobrien if (c == (struct tab *)0) { 173979968Sobrien reply(502, "Unknown command %s.", s); 174079968Sobrien return; 174179968Sobrien } 174279968Sobrien if (CMD_IMPLEMENTED(c)) 174392282Sobrien reply(214, "Syntax: %s%s %s", htype, c->name, c->help); 174479968Sobrien else 174592282Sobrien reply(214, "%s%-*s\t%s; not implemented.", htype, width, 174679968Sobrien c->name, c->help); 174779968Sobrien} 174879968Sobrien 174979968Sobrien/* 175079968Sobrien * Check that the structures used for a PORT, LPRT or EPRT command are 175179968Sobrien * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks. 175279968Sobrien * If family != -1 check that his_addr.su_family == family. 175379968Sobrien */ 175479968Sobrienstatic void 175579968Sobrienport_check(const char *cmd, int family) 175679968Sobrien{ 175779968Sobrien char h1[NI_MAXHOST], h2[NI_MAXHOST]; 175879968Sobrien char s1[NI_MAXHOST], s2[NI_MAXHOST]; 175979968Sobrien#ifdef NI_WITHSCOPEID 176079968Sobrien const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID; 176179968Sobrien#else 176279968Sobrien const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; 176379968Sobrien#endif 176479968Sobrien 176579968Sobrien if (epsvall) { 176679968Sobrien reply(501, "%s disallowed after EPSV ALL", cmd); 176779968Sobrien return; 176879968Sobrien } 176979968Sobrien 177079968Sobrien if (family != -1 && his_addr.su_family != family) { 177179968Sobrien port_check_fail: 177279968Sobrien reply(500, "Illegal %s command rejected", cmd); 177379968Sobrien return; 177479968Sobrien } 177579968Sobrien 177679968Sobrien if (data_dest.su_family != his_addr.su_family) 177779968Sobrien goto port_check_fail; 177879968Sobrien 177979968Sobrien /* be paranoid, if told so */ 178079968Sobrien if (CURCLASS_FLAGS_ISSET(checkportcmd)) { 178179968Sobrien#ifdef INET6 178279968Sobrien /* 178379968Sobrien * be paranoid, there are getnameinfo implementation that does 178479968Sobrien * not present scopeid portion 178579968Sobrien */ 178679968Sobrien if (data_dest.su_family == AF_INET6 && 178779968Sobrien data_dest.su_scope_id != his_addr.su_scope_id) 178879968Sobrien goto port_check_fail; 178979968Sobrien#endif 179079968Sobrien 179179968Sobrien if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len, 179279968Sobrien h1, sizeof(h1), s1, sizeof(s1), niflags)) 179379968Sobrien goto port_check_fail; 179479968Sobrien if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 179579968Sobrien h2, sizeof(h2), s2, sizeof(s2), niflags)) 179679968Sobrien goto port_check_fail; 179779968Sobrien 179879968Sobrien if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0) 179979968Sobrien goto port_check_fail; 180079968Sobrien } 180179968Sobrien 180279968Sobrien usedefault = 0; 180379968Sobrien if (pdata >= 0) { 180479968Sobrien (void) close(pdata); 180579968Sobrien pdata = -1; 180679968Sobrien } 180779968Sobrien reply(200, "%s command successful.", cmd); 180879968Sobrien} 1809