1*0a6a1f1dSLionel Sambuc /* $NetBSD: ftpcmd.y,v 1.94 2015/08/10 07:45:50 shm Exp $ */ 262da0113SBen Gras 362da0113SBen Gras /*- 462da0113SBen Gras * Copyright (c) 1997-2009 The NetBSD Foundation, Inc. 562da0113SBen Gras * All rights reserved. 662da0113SBen Gras * 762da0113SBen Gras * This code is derived from software contributed to The NetBSD Foundation 862da0113SBen Gras * by Luke Mewburn. 962da0113SBen Gras * 1062da0113SBen Gras * Redistribution and use in source and binary forms, with or without 1162da0113SBen Gras * modification, are permitted provided that the following conditions 1262da0113SBen Gras * are met: 1362da0113SBen Gras * 1. Redistributions of source code must retain the above copyright 1462da0113SBen Gras * notice, this list of conditions and the following disclaimer. 1562da0113SBen Gras * 2. Redistributions in binary form must reproduce the above copyright 1662da0113SBen Gras * notice, this list of conditions and the following disclaimer in the 1762da0113SBen Gras * documentation and/or other materials provided with the distribution. 1862da0113SBen Gras * 1962da0113SBen Gras * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2062da0113SBen Gras * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2162da0113SBen Gras * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2262da0113SBen Gras * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2362da0113SBen Gras * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2462da0113SBen Gras * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2562da0113SBen Gras * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2662da0113SBen Gras * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2762da0113SBen Gras * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2862da0113SBen Gras * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2962da0113SBen Gras * POSSIBILITY OF SUCH DAMAGE. 3062da0113SBen Gras */ 3162da0113SBen Gras 3262da0113SBen Gras /* 3362da0113SBen Gras * Copyright (c) 1985, 1988, 1993, 1994 3462da0113SBen Gras * The Regents of the University of California. All rights reserved. 3562da0113SBen Gras * 3662da0113SBen Gras * Redistribution and use in source and binary forms, with or without 3762da0113SBen Gras * modification, are permitted provided that the following conditions 3862da0113SBen Gras * are met: 3962da0113SBen Gras * 1. Redistributions of source code must retain the above copyright 4062da0113SBen Gras * notice, this list of conditions and the following disclaimer. 4162da0113SBen Gras * 2. Redistributions in binary form must reproduce the above copyright 4262da0113SBen Gras * notice, this list of conditions and the following disclaimer in the 4362da0113SBen Gras * documentation and/or other materials provided with the distribution. 4462da0113SBen Gras * 3. Neither the name of the University nor the names of its contributors 4562da0113SBen Gras * may be used to endorse or promote products derived from this software 4662da0113SBen Gras * without specific prior written permission. 4762da0113SBen Gras * 4862da0113SBen Gras * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 4962da0113SBen Gras * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5062da0113SBen Gras * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5162da0113SBen Gras * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5262da0113SBen Gras * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5362da0113SBen Gras * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5462da0113SBen Gras * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 5562da0113SBen Gras * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 5662da0113SBen Gras * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 5762da0113SBen Gras * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 5862da0113SBen Gras * SUCH DAMAGE. 5962da0113SBen Gras * 6062da0113SBen Gras * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 6162da0113SBen Gras */ 6262da0113SBen Gras 6362da0113SBen Gras /* 6462da0113SBen Gras * Grammar for FTP commands. 6562da0113SBen Gras * See RFC 959. 6662da0113SBen Gras */ 6762da0113SBen Gras 6862da0113SBen Gras %{ 6962da0113SBen Gras #include <sys/cdefs.h> 7062da0113SBen Gras 7162da0113SBen Gras #ifndef lint 7262da0113SBen Gras #if 0 7362da0113SBen Gras static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; 7462da0113SBen Gras #else 75*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: ftpcmd.y,v 1.94 2015/08/10 07:45:50 shm Exp $"); 7662da0113SBen Gras #endif 7762da0113SBen Gras #endif /* not lint */ 7862da0113SBen Gras 7962da0113SBen Gras #include <sys/param.h> 8062da0113SBen Gras #include <sys/socket.h> 8162da0113SBen Gras #include <sys/stat.h> 8262da0113SBen Gras 8362da0113SBen Gras #include <netinet/in.h> 8462da0113SBen Gras #include <arpa/ftp.h> 8562da0113SBen Gras #include <arpa/inet.h> 8662da0113SBen Gras 8762da0113SBen Gras #include <ctype.h> 8862da0113SBen Gras #include <errno.h> 8962da0113SBen Gras #include <pwd.h> 9062da0113SBen Gras #include <stdio.h> 9162da0113SBen Gras #include <stdlib.h> 9262da0113SBen Gras #include <string.h> 9362da0113SBen Gras #include <syslog.h> 9462da0113SBen Gras #include <time.h> 9562da0113SBen Gras #include <tzfile.h> 9662da0113SBen Gras #include <unistd.h> 9762da0113SBen Gras #include <netdb.h> 9862da0113SBen Gras 9962da0113SBen Gras #ifdef KERBEROS5 10062da0113SBen Gras #include <krb5/krb5.h> 10162da0113SBen Gras #endif 10262da0113SBen Gras 10362da0113SBen Gras #include "extern.h" 10462da0113SBen Gras #include "version.h" 10562da0113SBen Gras 10662da0113SBen Gras static int cmd_type; 10762da0113SBen Gras static int cmd_form; 10862da0113SBen Gras static int cmd_bytesz; 10962da0113SBen Gras 11062da0113SBen Gras char cbuf[FTP_BUFLEN]; 11162da0113SBen Gras char *cmdp; 11262da0113SBen Gras char *fromname; 11362da0113SBen Gras 11462da0113SBen Gras extern int epsvall; 11562da0113SBen Gras struct tab sitetab[]; 11662da0113SBen Gras 11762da0113SBen Gras static int check_write(const char *, int); 11862da0113SBen Gras static void help(struct tab *, const char *); 11962da0113SBen Gras static void port_check(const char *, int); 12062da0113SBen Gras int yylex(void); 12162da0113SBen Gras 12262da0113SBen Gras %} 12362da0113SBen Gras 12462da0113SBen Gras %union { 12562da0113SBen Gras struct { 12662da0113SBen Gras LLT ll; 12762da0113SBen Gras int i; 12862da0113SBen Gras } u; 12962da0113SBen Gras char *s; 13062da0113SBen Gras const char *cs; 13162da0113SBen Gras } 13262da0113SBen Gras 13362da0113SBen Gras %token 13462da0113SBen Gras A B C E F I 13562da0113SBen Gras L N P R S T 13662da0113SBen Gras 13762da0113SBen Gras SP CRLF COMMA ALL 13862da0113SBen Gras 13962da0113SBen Gras USER PASS ACCT CWD CDUP SMNT 14062da0113SBen Gras QUIT REIN PORT PASV TYPE STRU 14162da0113SBen Gras MODE RETR STOR STOU APPE ALLO 14262da0113SBen Gras REST RNFR RNTO ABOR DELE RMD 14362da0113SBen Gras MKD PWD LIST NLST SITE SYST 14462da0113SBen Gras STAT HELP NOOP 14562da0113SBen Gras 14662da0113SBen Gras AUTH ADAT PROT PBSZ CCC MIC 14762da0113SBen Gras CONF ENC 14862da0113SBen Gras 14962da0113SBen Gras FEAT OPTS 15062da0113SBen Gras 15162da0113SBen Gras SIZE MDTM MLST MLSD 15262da0113SBen Gras 15362da0113SBen Gras LPRT LPSV EPRT EPSV 15462da0113SBen Gras 15562da0113SBen Gras MAIL MLFL MRCP MRSQ MSAM MSND 15662da0113SBen Gras MSOM 15762da0113SBen Gras 15862da0113SBen Gras CHMOD IDLE RATEGET RATEPUT UMASK 15962da0113SBen Gras 16062da0113SBen Gras LEXERR 16162da0113SBen Gras 16262da0113SBen Gras %token <s> STRING 16362da0113SBen Gras %token <u> NUMBER 16462da0113SBen Gras 16562da0113SBen Gras %type <u.i> check_login octal_number byte_size 16662da0113SBen Gras %type <u.i> struct_code mode_code type_code form_code decimal_integer 16762da0113SBen Gras %type <s> pathstring pathname password username 16862da0113SBen Gras %type <s> mechanism_name base64data prot_code 16962da0113SBen Gras 17062da0113SBen Gras %start cmd_sel 17162da0113SBen Gras 17262da0113SBen Gras %% 17362da0113SBen Gras 17462da0113SBen Gras cmd_sel 17562da0113SBen Gras : cmd 17662da0113SBen Gras { 17762da0113SBen Gras REASSIGN(fromname, NULL); 17862da0113SBen Gras restart_point = (off_t) 0; 17962da0113SBen Gras } 18062da0113SBen Gras 18162da0113SBen Gras | rcmd 18262da0113SBen Gras 18362da0113SBen Gras ; 18462da0113SBen Gras 18562da0113SBen Gras cmd 18662da0113SBen Gras /* RFC 959 */ 18762da0113SBen Gras : USER SP username CRLF 18862da0113SBen Gras { 18962da0113SBen Gras user($3); 19062da0113SBen Gras free($3); 19162da0113SBen Gras } 19262da0113SBen Gras 19362da0113SBen Gras | PASS SP password CRLF 19462da0113SBen Gras { 19562da0113SBen Gras pass($3); 196*0a6a1f1dSLionel Sambuc explicit_memset($3, 0, strlen($3)); 19762da0113SBen Gras free($3); 19862da0113SBen Gras } 19962da0113SBen Gras 20062da0113SBen Gras | CWD check_login CRLF 20162da0113SBen Gras { 20262da0113SBen Gras if ($2) 20362da0113SBen Gras cwd(homedir); 20462da0113SBen Gras } 20562da0113SBen Gras 20662da0113SBen Gras | CWD check_login SP pathname CRLF 20762da0113SBen Gras { 20862da0113SBen Gras if ($2 && $4 != NULL) 20962da0113SBen Gras cwd($4); 21062da0113SBen Gras if ($4 != NULL) 21162da0113SBen Gras free($4); 21262da0113SBen Gras } 21362da0113SBen Gras 21462da0113SBen Gras | CDUP check_login CRLF 21562da0113SBen Gras { 21662da0113SBen Gras if ($2) 21762da0113SBen Gras cwd(".."); 21862da0113SBen Gras } 21962da0113SBen Gras 22062da0113SBen Gras | QUIT CRLF 22162da0113SBen Gras { 22262da0113SBen Gras if (logged_in) { 22362da0113SBen Gras reply(-221, "%s", ""); 22462da0113SBen Gras reply(0, 22562da0113SBen Gras "Data traffic for this session was " LLF " byte%s in " LLF " file%s.", 22662da0113SBen Gras (LLT)total_data, PLURAL(total_data), 22762da0113SBen Gras (LLT)total_files, PLURAL(total_files)); 22862da0113SBen Gras reply(0, 22962da0113SBen Gras "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.", 23062da0113SBen Gras (LLT)total_bytes, PLURAL(total_bytes), 23162da0113SBen Gras (LLT)total_xfers, PLURAL(total_xfers)); 23262da0113SBen Gras } 23362da0113SBen Gras reply(221, 23462da0113SBen Gras "Thank you for using the FTP service on %s.", 23562da0113SBen Gras hostname); 23662da0113SBen Gras if (logged_in && logging) { 23762da0113SBen Gras syslog(LOG_INFO, 23862da0113SBen Gras "Data traffic: " LLF " byte%s in " LLF " file%s", 23962da0113SBen Gras (LLT)total_data, PLURAL(total_data), 24062da0113SBen Gras (LLT)total_files, PLURAL(total_files)); 24162da0113SBen Gras syslog(LOG_INFO, 24262da0113SBen Gras "Total traffic: " LLF " byte%s in " LLF " transfer%s", 24362da0113SBen Gras (LLT)total_bytes, PLURAL(total_bytes), 24462da0113SBen Gras (LLT)total_xfers, PLURAL(total_xfers)); 24562da0113SBen Gras } 24662da0113SBen Gras 24762da0113SBen Gras dologout(0); 24862da0113SBen Gras } 24962da0113SBen Gras 25062da0113SBen Gras | PORT check_login SP host_port CRLF 25162da0113SBen Gras { 25262da0113SBen Gras if ($2) 25362da0113SBen Gras port_check("PORT", AF_INET); 25462da0113SBen Gras } 25562da0113SBen Gras 25662da0113SBen Gras | LPRT check_login SP host_long_port4 CRLF 25762da0113SBen Gras { 25862da0113SBen Gras if ($2) 25962da0113SBen Gras port_check("LPRT", AF_INET); 26062da0113SBen Gras } 26162da0113SBen Gras 26262da0113SBen Gras | LPRT check_login SP host_long_port6 CRLF 26362da0113SBen Gras { 26462da0113SBen Gras #ifdef INET6 26562da0113SBen Gras if ($2) 26662da0113SBen Gras port_check("LPRT", AF_INET6); 26762da0113SBen Gras #else 26862da0113SBen Gras reply(500, "IPv6 support not available."); 26962da0113SBen Gras #endif 27062da0113SBen Gras } 27162da0113SBen Gras 27262da0113SBen Gras | EPRT check_login SP STRING CRLF 27362da0113SBen Gras { 27462da0113SBen Gras if ($2) { 27562da0113SBen Gras if (extended_port($4) == 0) 27662da0113SBen Gras port_check("EPRT", -1); 27762da0113SBen Gras } 27862da0113SBen Gras free($4); 27962da0113SBen Gras } 28062da0113SBen Gras 28162da0113SBen Gras | PASV check_login CRLF 28262da0113SBen Gras { 28362da0113SBen Gras if ($2) { 28462da0113SBen Gras if (CURCLASS_FLAGS_ISSET(passive)) 28562da0113SBen Gras passive(); 28662da0113SBen Gras else 28762da0113SBen Gras reply(500, "PASV mode not available."); 28862da0113SBen Gras } 28962da0113SBen Gras } 29062da0113SBen Gras 29162da0113SBen Gras | LPSV check_login CRLF 29262da0113SBen Gras { 29362da0113SBen Gras if ($2) { 29462da0113SBen Gras if (CURCLASS_FLAGS_ISSET(passive)) { 29562da0113SBen Gras if (epsvall) 29662da0113SBen Gras reply(501, 29762da0113SBen Gras "LPSV disallowed after EPSV ALL"); 29862da0113SBen Gras else 29962da0113SBen Gras long_passive("LPSV", PF_UNSPEC); 30062da0113SBen Gras } else 30162da0113SBen Gras reply(500, "LPSV mode not available."); 30262da0113SBen Gras } 30362da0113SBen Gras } 30462da0113SBen Gras 30562da0113SBen Gras | EPSV check_login SP NUMBER CRLF 30662da0113SBen Gras { 30762da0113SBen Gras if ($2) { 30862da0113SBen Gras if (CURCLASS_FLAGS_ISSET(passive)) 30962da0113SBen Gras long_passive("EPSV", 31062da0113SBen Gras epsvproto2af($4.i)); 31162da0113SBen Gras else 31262da0113SBen Gras reply(500, "EPSV mode not available."); 31362da0113SBen Gras } 31462da0113SBen Gras } 31562da0113SBen Gras 31662da0113SBen Gras | EPSV check_login SP ALL CRLF 31762da0113SBen Gras { 31862da0113SBen Gras if ($2) { 31962da0113SBen Gras if (CURCLASS_FLAGS_ISSET(passive)) { 32062da0113SBen Gras reply(200, 32162da0113SBen Gras "EPSV ALL command successful."); 32262da0113SBen Gras epsvall++; 32362da0113SBen Gras } else 32462da0113SBen Gras reply(500, "EPSV mode not available."); 32562da0113SBen Gras } 32662da0113SBen Gras } 32762da0113SBen Gras 32862da0113SBen Gras | EPSV check_login CRLF 32962da0113SBen Gras { 33062da0113SBen Gras if ($2) { 33162da0113SBen Gras if (CURCLASS_FLAGS_ISSET(passive)) 33262da0113SBen Gras long_passive("EPSV", PF_UNSPEC); 33362da0113SBen Gras else 33462da0113SBen Gras reply(500, "EPSV mode not available."); 33562da0113SBen Gras } 33662da0113SBen Gras } 33762da0113SBen Gras 33862da0113SBen Gras | TYPE check_login SP type_code CRLF 33962da0113SBen Gras { 34062da0113SBen Gras if ($2) { 34162da0113SBen Gras 34262da0113SBen Gras switch (cmd_type) { 34362da0113SBen Gras 34462da0113SBen Gras case TYPE_A: 34562da0113SBen Gras if (cmd_form == FORM_N) { 34662da0113SBen Gras reply(200, "Type set to A."); 34762da0113SBen Gras type = cmd_type; 34862da0113SBen Gras form = cmd_form; 34962da0113SBen Gras } else 35062da0113SBen Gras reply(504, "Form must be N."); 35162da0113SBen Gras break; 35262da0113SBen Gras 35362da0113SBen Gras case TYPE_E: 35462da0113SBen Gras reply(504, "Type E not implemented."); 35562da0113SBen Gras break; 35662da0113SBen Gras 35762da0113SBen Gras case TYPE_I: 35862da0113SBen Gras reply(200, "Type set to I."); 35962da0113SBen Gras type = cmd_type; 36062da0113SBen Gras break; 36162da0113SBen Gras 36262da0113SBen Gras case TYPE_L: 36362da0113SBen Gras #if NBBY == 8 36462da0113SBen Gras if (cmd_bytesz == 8) { 36562da0113SBen Gras reply(200, 36662da0113SBen Gras "Type set to L (byte size 8)."); 36762da0113SBen Gras type = cmd_type; 36862da0113SBen Gras } else 36962da0113SBen Gras reply(504, "Byte size must be 8."); 37062da0113SBen Gras #else /* NBBY == 8 */ 37162da0113SBen Gras UNIMPLEMENTED for NBBY != 8 37262da0113SBen Gras #endif /* NBBY == 8 */ 37362da0113SBen Gras } 37462da0113SBen Gras 37562da0113SBen Gras } 37662da0113SBen Gras } 37762da0113SBen Gras 37862da0113SBen Gras | STRU check_login SP struct_code CRLF 37962da0113SBen Gras { 38062da0113SBen Gras if ($2) { 38162da0113SBen Gras switch ($4) { 38262da0113SBen Gras 38362da0113SBen Gras case STRU_F: 38462da0113SBen Gras reply(200, "STRU F ok."); 38562da0113SBen Gras break; 38662da0113SBen Gras 38762da0113SBen Gras default: 38862da0113SBen Gras reply(504, "Unimplemented STRU type."); 38962da0113SBen Gras } 39062da0113SBen Gras } 39162da0113SBen Gras } 39262da0113SBen Gras 39362da0113SBen Gras | MODE check_login SP mode_code CRLF 39462da0113SBen Gras { 39562da0113SBen Gras if ($2) { 39662da0113SBen Gras switch ($4) { 39762da0113SBen Gras 39862da0113SBen Gras case MODE_S: 39962da0113SBen Gras reply(200, "MODE S ok."); 40062da0113SBen Gras break; 40162da0113SBen Gras 40262da0113SBen Gras default: 40362da0113SBen Gras reply(502, "Unimplemented MODE type."); 40462da0113SBen Gras } 40562da0113SBen Gras } 40662da0113SBen Gras } 40762da0113SBen Gras 40862da0113SBen Gras | RETR check_login SP pathname CRLF 40962da0113SBen Gras { 41062da0113SBen Gras if ($2 && $4 != NULL) 41162da0113SBen Gras retrieve(NULL, $4); 41262da0113SBen Gras if ($4 != NULL) 41362da0113SBen Gras free($4); 41462da0113SBen Gras } 41562da0113SBen Gras 41662da0113SBen Gras | STOR SP pathname CRLF 41762da0113SBen Gras { 41862da0113SBen Gras if (check_write($3, 1)) 41962da0113SBen Gras store($3, "w", 0); 42062da0113SBen Gras if ($3 != NULL) 42162da0113SBen Gras free($3); 42262da0113SBen Gras } 42362da0113SBen Gras 42462da0113SBen Gras | STOU SP pathname CRLF 42562da0113SBen Gras { 42662da0113SBen Gras if (check_write($3, 1)) 42762da0113SBen Gras store($3, "w", 1); 42862da0113SBen Gras if ($3 != NULL) 42962da0113SBen Gras free($3); 43062da0113SBen Gras } 43162da0113SBen Gras 43262da0113SBen Gras | APPE SP pathname CRLF 43362da0113SBen Gras { 43462da0113SBen Gras if (check_write($3, 1)) 43562da0113SBen Gras store($3, "a", 0); 43662da0113SBen Gras if ($3 != NULL) 43762da0113SBen Gras free($3); 43862da0113SBen Gras } 43962da0113SBen Gras 44062da0113SBen Gras | ALLO check_login SP NUMBER CRLF 44162da0113SBen Gras { 44262da0113SBen Gras if ($2) 44362da0113SBen Gras reply(202, "ALLO command ignored."); 44462da0113SBen Gras } 44562da0113SBen Gras 44662da0113SBen Gras | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 44762da0113SBen Gras { 44862da0113SBen Gras if ($2) 44962da0113SBen Gras reply(202, "ALLO command ignored."); 45062da0113SBen Gras } 45162da0113SBen Gras 45262da0113SBen Gras | RNTO SP pathname CRLF 45362da0113SBen Gras { 45462da0113SBen Gras if (check_write($3, 0)) { 45562da0113SBen Gras if (fromname) { 45662da0113SBen Gras renamecmd(fromname, $3); 45762da0113SBen Gras REASSIGN(fromname, NULL); 45862da0113SBen Gras } else { 45962da0113SBen Gras reply(503, "Bad sequence of commands."); 46062da0113SBen Gras } 46162da0113SBen Gras } 46262da0113SBen Gras if ($3 != NULL) 46362da0113SBen Gras free($3); 46462da0113SBen Gras } 46562da0113SBen Gras 46662da0113SBen Gras | ABOR check_login CRLF 46762da0113SBen Gras { 46862da0113SBen Gras if (is_oob) 46962da0113SBen Gras abor(); 47062da0113SBen Gras else if ($2) 47162da0113SBen Gras reply(225, "ABOR command successful."); 47262da0113SBen Gras } 47362da0113SBen Gras 47462da0113SBen Gras | DELE SP pathname CRLF 47562da0113SBen Gras { 47662da0113SBen Gras if (check_write($3, 0)) 47762da0113SBen Gras delete($3); 47862da0113SBen Gras if ($3 != NULL) 47962da0113SBen Gras free($3); 48062da0113SBen Gras } 48162da0113SBen Gras 48262da0113SBen Gras | RMD SP pathname CRLF 48362da0113SBen Gras { 48462da0113SBen Gras if (check_write($3, 0)) 48562da0113SBen Gras removedir($3); 48662da0113SBen Gras if ($3 != NULL) 48762da0113SBen Gras free($3); 48862da0113SBen Gras } 48962da0113SBen Gras 49062da0113SBen Gras | MKD SP pathname CRLF 49162da0113SBen Gras { 49262da0113SBen Gras if (check_write($3, 0)) 49362da0113SBen Gras makedir($3); 49462da0113SBen Gras if ($3 != NULL) 49562da0113SBen Gras free($3); 49662da0113SBen Gras } 49762da0113SBen Gras 49862da0113SBen Gras | PWD check_login CRLF 49962da0113SBen Gras { 50062da0113SBen Gras if ($2) 50162da0113SBen Gras pwd(); 50262da0113SBen Gras } 50362da0113SBen Gras 50462da0113SBen Gras | LIST check_login CRLF 50562da0113SBen Gras { 50662da0113SBen Gras const char *argv[] = { INTERNAL_LS, "-lgA", NULL }; 50762da0113SBen Gras 50862da0113SBen Gras if (CURCLASS_FLAGS_ISSET(hidesymlinks)) 50962da0113SBen Gras argv[1] = "-LlgA"; 51062da0113SBen Gras if ($2) 51162da0113SBen Gras retrieve(argv, ""); 51262da0113SBen Gras } 51362da0113SBen Gras 51462da0113SBen Gras | LIST check_login SP pathname CRLF 51562da0113SBen Gras { 51662da0113SBen Gras const char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL }; 51762da0113SBen Gras 51862da0113SBen Gras if (CURCLASS_FLAGS_ISSET(hidesymlinks)) 51962da0113SBen Gras argv[1] = "-LlgA"; 52062da0113SBen Gras if ($2 && $4 != NULL) { 52162da0113SBen Gras argv[2] = $4; 52262da0113SBen Gras retrieve(argv, $4); 52362da0113SBen Gras } 52462da0113SBen Gras if ($4 != NULL) 52562da0113SBen Gras free($4); 52662da0113SBen Gras } 52762da0113SBen Gras 52862da0113SBen Gras | NLST check_login CRLF 52962da0113SBen Gras { 53062da0113SBen Gras if ($2) 53162da0113SBen Gras send_file_list("."); 53262da0113SBen Gras } 53362da0113SBen Gras 53462da0113SBen Gras | NLST check_login SP pathname CRLF 53562da0113SBen Gras { 53662da0113SBen Gras if ($2) 53762da0113SBen Gras send_file_list($4); 53862da0113SBen Gras free($4); 53962da0113SBen Gras } 54062da0113SBen Gras 54162da0113SBen Gras | SITE SP HELP CRLF 54262da0113SBen Gras { 54362da0113SBen Gras help(sitetab, NULL); 54462da0113SBen Gras } 54562da0113SBen Gras 54662da0113SBen Gras | SITE SP CHMOD SP octal_number SP pathname CRLF 54762da0113SBen Gras { 54862da0113SBen Gras if (check_write($7, 0)) { 54962da0113SBen Gras if (($5 == -1) || ($5 > 0777)) 55062da0113SBen Gras reply(501, 55162da0113SBen Gras "CHMOD: Mode value must be between 0 and 0777"); 55262da0113SBen Gras else if (chmod($7, $5) < 0) 55362da0113SBen Gras perror_reply(550, $7); 55462da0113SBen Gras else 55562da0113SBen Gras reply(200, "CHMOD command successful."); 55662da0113SBen Gras } 55762da0113SBen Gras if ($7 != NULL) 55862da0113SBen Gras free($7); 55962da0113SBen Gras } 56062da0113SBen Gras 56162da0113SBen Gras | SITE SP HELP SP STRING CRLF 56262da0113SBen Gras { 56362da0113SBen Gras help(sitetab, $5); 56462da0113SBen Gras free($5); 56562da0113SBen Gras } 56662da0113SBen Gras 56762da0113SBen Gras | SITE SP IDLE check_login CRLF 56862da0113SBen Gras { 56962da0113SBen Gras if ($4) { 57062da0113SBen Gras reply(200, 57162da0113SBen Gras "Current IDLE time limit is " LLF 57262da0113SBen Gras " seconds; max " LLF, 57362da0113SBen Gras (LLT)curclass.timeout, 57462da0113SBen Gras (LLT)curclass.maxtimeout); 57562da0113SBen Gras } 57662da0113SBen Gras } 57762da0113SBen Gras 57862da0113SBen Gras | SITE SP IDLE check_login SP NUMBER CRLF 57962da0113SBen Gras { 58062da0113SBen Gras if ($4) { 58162da0113SBen Gras if ($6.i < 30 || $6.i > curclass.maxtimeout) { 58262da0113SBen Gras reply(501, 58362da0113SBen Gras "IDLE time limit must be between 30 and " 58462da0113SBen Gras LLF " seconds", 58562da0113SBen Gras (LLT)curclass.maxtimeout); 58662da0113SBen Gras } else { 58762da0113SBen Gras curclass.timeout = $6.i; 58862da0113SBen Gras (void) alarm(curclass.timeout); 58962da0113SBen Gras reply(200, 59062da0113SBen Gras "IDLE time limit set to " 59162da0113SBen Gras LLF " seconds", 59262da0113SBen Gras (LLT)curclass.timeout); 59362da0113SBen Gras } 59462da0113SBen Gras } 59562da0113SBen Gras } 59662da0113SBen Gras 59762da0113SBen Gras | SITE SP RATEGET check_login CRLF 59862da0113SBen Gras { 59962da0113SBen Gras if ($4) { 60062da0113SBen Gras reply(200, 60162da0113SBen Gras "Current RATEGET is " LLF " bytes/sec", 60262da0113SBen Gras (LLT)curclass.rateget); 60362da0113SBen Gras } 60462da0113SBen Gras } 60562da0113SBen Gras 60662da0113SBen Gras | SITE SP RATEGET check_login SP STRING CRLF 60762da0113SBen Gras { 60862da0113SBen Gras char errbuf[100]; 60962da0113SBen Gras char *p = $6; 61062da0113SBen Gras LLT rate; 61162da0113SBen Gras 61262da0113SBen Gras if ($4) { 61362da0113SBen Gras rate = strsuftollx("RATEGET", p, 0, 61462da0113SBen Gras curclass.maxrateget 61562da0113SBen Gras ? curclass.maxrateget 61662da0113SBen Gras : LLTMAX, errbuf, sizeof(errbuf)); 61762da0113SBen Gras if (errbuf[0]) 61862da0113SBen Gras reply(501, "%s", errbuf); 61962da0113SBen Gras else { 62062da0113SBen Gras curclass.rateget = rate; 62162da0113SBen Gras reply(200, 62262da0113SBen Gras "RATEGET set to " LLF " bytes/sec", 62362da0113SBen Gras (LLT)curclass.rateget); 62462da0113SBen Gras } 62562da0113SBen Gras } 62662da0113SBen Gras free($6); 62762da0113SBen Gras } 62862da0113SBen Gras 62962da0113SBen Gras | SITE SP RATEPUT check_login CRLF 63062da0113SBen Gras { 63162da0113SBen Gras if ($4) { 63262da0113SBen Gras reply(200, 63362da0113SBen Gras "Current RATEPUT is " LLF " bytes/sec", 63462da0113SBen Gras (LLT)curclass.rateput); 63562da0113SBen Gras } 63662da0113SBen Gras } 63762da0113SBen Gras 63862da0113SBen Gras | SITE SP RATEPUT check_login SP STRING CRLF 63962da0113SBen Gras { 64062da0113SBen Gras char errbuf[100]; 64162da0113SBen Gras char *p = $6; 64262da0113SBen Gras LLT rate; 64362da0113SBen Gras 64462da0113SBen Gras if ($4) { 64562da0113SBen Gras rate = strsuftollx("RATEPUT", p, 0, 64662da0113SBen Gras curclass.maxrateput 64762da0113SBen Gras ? curclass.maxrateput 64862da0113SBen Gras : LLTMAX, errbuf, sizeof(errbuf)); 64962da0113SBen Gras if (errbuf[0]) 65062da0113SBen Gras reply(501, "%s", errbuf); 65162da0113SBen Gras else { 65262da0113SBen Gras curclass.rateput = rate; 65362da0113SBen Gras reply(200, 65462da0113SBen Gras "RATEPUT set to " LLF " bytes/sec", 65562da0113SBen Gras (LLT)curclass.rateput); 65662da0113SBen Gras } 65762da0113SBen Gras } 65862da0113SBen Gras free($6); 65962da0113SBen Gras } 66062da0113SBen Gras 66162da0113SBen Gras | SITE SP UMASK check_login CRLF 66262da0113SBen Gras { 66362da0113SBen Gras int oldmask; 66462da0113SBen Gras 66562da0113SBen Gras if ($4) { 66662da0113SBen Gras oldmask = umask(0); 66762da0113SBen Gras (void) umask(oldmask); 66862da0113SBen Gras reply(200, "Current UMASK is %03o", oldmask); 66962da0113SBen Gras } 67062da0113SBen Gras } 67162da0113SBen Gras 67262da0113SBen Gras | SITE SP UMASK check_login SP octal_number CRLF 67362da0113SBen Gras { 67462da0113SBen Gras int oldmask; 67562da0113SBen Gras 67662da0113SBen Gras if ($4 && check_write("", 0)) { 67762da0113SBen Gras if (($6 == -1) || ($6 > 0777)) { 67862da0113SBen Gras reply(501, "Bad UMASK value"); 67962da0113SBen Gras } else { 68062da0113SBen Gras oldmask = umask($6); 68162da0113SBen Gras reply(200, 68262da0113SBen Gras "UMASK set to %03o (was %03o)", 68362da0113SBen Gras $6, oldmask); 68462da0113SBen Gras } 68562da0113SBen Gras } 68662da0113SBen Gras } 68762da0113SBen Gras 68862da0113SBen Gras | SYST CRLF 68962da0113SBen Gras { 69062da0113SBen Gras if (EMPTYSTR(version)) 69162da0113SBen Gras reply(215, "UNIX Type: L%d", NBBY); 69262da0113SBen Gras else 69362da0113SBen Gras reply(215, "UNIX Type: L%d Version: %s", NBBY, 69462da0113SBen Gras version); 69562da0113SBen Gras } 69662da0113SBen Gras 69762da0113SBen Gras | STAT check_login SP pathname CRLF 69862da0113SBen Gras { 69962da0113SBen Gras if ($2 && $4 != NULL) 70062da0113SBen Gras statfilecmd($4); 70162da0113SBen Gras if ($4 != NULL) 70262da0113SBen Gras free($4); 70362da0113SBen Gras } 70462da0113SBen Gras 70562da0113SBen Gras | STAT CRLF 70662da0113SBen Gras { 70762da0113SBen Gras if (is_oob) 70862da0113SBen Gras statxfer(); 70962da0113SBen Gras else 71062da0113SBen Gras statcmd(); 71162da0113SBen Gras } 71262da0113SBen Gras 71362da0113SBen Gras | HELP CRLF 71462da0113SBen Gras { 71562da0113SBen Gras help(cmdtab, NULL); 71662da0113SBen Gras } 71762da0113SBen Gras 71862da0113SBen Gras | HELP SP STRING CRLF 71962da0113SBen Gras { 72062da0113SBen Gras char *cp = $3; 72162da0113SBen Gras 72262da0113SBen Gras if (strncasecmp(cp, "SITE", 4) == 0) { 72362da0113SBen Gras cp = $3 + 4; 72462da0113SBen Gras if (*cp == ' ') 72562da0113SBen Gras cp++; 72662da0113SBen Gras if (*cp) 72762da0113SBen Gras help(sitetab, cp); 72862da0113SBen Gras else 72962da0113SBen Gras help(sitetab, NULL); 73062da0113SBen Gras } else 73162da0113SBen Gras help(cmdtab, $3); 73262da0113SBen Gras free($3); 73362da0113SBen Gras } 73462da0113SBen Gras 73562da0113SBen Gras | NOOP CRLF 73662da0113SBen Gras { 73762da0113SBen Gras reply(200, "NOOP command successful."); 73862da0113SBen Gras } 73962da0113SBen Gras 74062da0113SBen Gras /* RFC 2228 */ 74162da0113SBen Gras | AUTH SP mechanism_name CRLF 74262da0113SBen Gras { 74362da0113SBen Gras reply(502, "RFC 2228 authentication not implemented."); 74462da0113SBen Gras free($3); 74562da0113SBen Gras } 74662da0113SBen Gras 74762da0113SBen Gras | ADAT SP base64data CRLF 74862da0113SBen Gras { 74962da0113SBen Gras reply(503, 75062da0113SBen Gras "Please set authentication state with AUTH."); 75162da0113SBen Gras free($3); 75262da0113SBen Gras } 75362da0113SBen Gras 75462da0113SBen Gras | PROT SP prot_code CRLF 75562da0113SBen Gras { 75662da0113SBen Gras reply(503, 75762da0113SBen Gras "Please set protection buffer size with PBSZ."); 75862da0113SBen Gras free($3); 75962da0113SBen Gras } 76062da0113SBen Gras 76162da0113SBen Gras | PBSZ SP decimal_integer CRLF 76262da0113SBen Gras { 76362da0113SBen Gras reply(503, 76462da0113SBen Gras "Please set authentication state with AUTH."); 76562da0113SBen Gras } 76662da0113SBen Gras 76762da0113SBen Gras | CCC CRLF 76862da0113SBen Gras { 76962da0113SBen Gras reply(533, "No protection enabled."); 77062da0113SBen Gras } 77162da0113SBen Gras 77262da0113SBen Gras | MIC SP base64data CRLF 77362da0113SBen Gras { 77462da0113SBen Gras reply(502, "RFC 2228 authentication not implemented."); 77562da0113SBen Gras free($3); 77662da0113SBen Gras } 77762da0113SBen Gras 77862da0113SBen Gras | CONF SP base64data CRLF 77962da0113SBen Gras { 78062da0113SBen Gras reply(502, "RFC 2228 authentication not implemented."); 78162da0113SBen Gras free($3); 78262da0113SBen Gras } 78362da0113SBen Gras 78462da0113SBen Gras | ENC SP base64data CRLF 78562da0113SBen Gras { 78662da0113SBen Gras reply(502, "RFC 2228 authentication not implemented."); 78762da0113SBen Gras free($3); 78862da0113SBen Gras } 78962da0113SBen Gras 79062da0113SBen Gras /* RFC 2389 */ 79162da0113SBen Gras | FEAT CRLF 79262da0113SBen Gras { 79362da0113SBen Gras 79462da0113SBen Gras feat(); 79562da0113SBen Gras } 79662da0113SBen Gras 79762da0113SBen Gras | OPTS SP STRING CRLF 79862da0113SBen Gras { 79962da0113SBen Gras 80062da0113SBen Gras opts($3); 80162da0113SBen Gras free($3); 80262da0113SBen Gras } 80362da0113SBen Gras 80462da0113SBen Gras 80562da0113SBen Gras /* RFC 3659 */ 80662da0113SBen Gras 80762da0113SBen Gras /* 80862da0113SBen Gras * Return size of file in a format suitable for 80962da0113SBen Gras * using with RESTART (we just count bytes). 81062da0113SBen Gras */ 81162da0113SBen Gras | SIZE check_login SP pathname CRLF 81262da0113SBen Gras { 81362da0113SBen Gras if ($2 && $4 != NULL) 81462da0113SBen Gras sizecmd($4); 81562da0113SBen Gras if ($4 != NULL) 81662da0113SBen Gras free($4); 81762da0113SBen Gras } 81862da0113SBen Gras 81962da0113SBen Gras /* 82062da0113SBen Gras * Return modification time of file as an ISO 3307 82162da0113SBen Gras * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 82262da0113SBen Gras * where xxx is the fractional second (of any precision, 82362da0113SBen Gras * not necessarily 3 digits) 82462da0113SBen Gras */ 82562da0113SBen Gras | MDTM check_login SP pathname CRLF 82662da0113SBen Gras { 82762da0113SBen Gras if ($2 && $4 != NULL) { 82862da0113SBen Gras struct stat stbuf; 82962da0113SBen Gras if (stat($4, &stbuf) < 0) 83062da0113SBen Gras perror_reply(550, $4); 83162da0113SBen Gras else if (!S_ISREG(stbuf.st_mode)) { 83262da0113SBen Gras reply(550, "%s: not a plain file.", $4); 83362da0113SBen Gras } else { 83462da0113SBen Gras struct tm *t; 83562da0113SBen Gras 83662da0113SBen Gras t = gmtime(&stbuf.st_mtime); 83762da0113SBen Gras reply(213, 83862da0113SBen Gras "%04d%02d%02d%02d%02d%02d", 83962da0113SBen Gras TM_YEAR_BASE + t->tm_year, 84062da0113SBen Gras t->tm_mon+1, t->tm_mday, 84162da0113SBen Gras t->tm_hour, t->tm_min, t->tm_sec); 84262da0113SBen Gras } 84362da0113SBen Gras } 84462da0113SBen Gras if ($4 != NULL) 84562da0113SBen Gras free($4); 84662da0113SBen Gras } 84762da0113SBen Gras 84862da0113SBen Gras | MLST check_login SP pathname CRLF 84962da0113SBen Gras { 85062da0113SBen Gras if ($2 && $4 != NULL) 85162da0113SBen Gras mlst($4); 85262da0113SBen Gras if ($4 != NULL) 85362da0113SBen Gras free($4); 85462da0113SBen Gras } 85562da0113SBen Gras 85662da0113SBen Gras | MLST check_login CRLF 85762da0113SBen Gras { 85862da0113SBen Gras mlst(NULL); 85962da0113SBen Gras } 86062da0113SBen Gras 86162da0113SBen Gras | MLSD check_login SP pathname CRLF 86262da0113SBen Gras { 86362da0113SBen Gras if ($2 && $4 != NULL) 86462da0113SBen Gras mlsd($4); 86562da0113SBen Gras if ($4 != NULL) 86662da0113SBen Gras free($4); 86762da0113SBen Gras } 86862da0113SBen Gras 86962da0113SBen Gras | MLSD check_login CRLF 87062da0113SBen Gras { 87162da0113SBen Gras mlsd(NULL); 87262da0113SBen Gras } 87362da0113SBen Gras 87462da0113SBen Gras | error CRLF 87562da0113SBen Gras { 87662da0113SBen Gras yyerrok; 87762da0113SBen Gras } 87862da0113SBen Gras ; 87962da0113SBen Gras 88062da0113SBen Gras rcmd 88162da0113SBen Gras : REST check_login SP NUMBER CRLF 88262da0113SBen Gras { 88362da0113SBen Gras if ($2) { 88462da0113SBen Gras REASSIGN(fromname, NULL); 88562da0113SBen Gras restart_point = (off_t)$4.ll; 88662da0113SBen Gras reply(350, 88762da0113SBen Gras "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.", 88862da0113SBen Gras (LLT)restart_point); 88962da0113SBen Gras } 89062da0113SBen Gras } 89162da0113SBen Gras 89262da0113SBen Gras | RNFR SP pathname CRLF 89362da0113SBen Gras { 89462da0113SBen Gras restart_point = (off_t) 0; 89562da0113SBen Gras if (check_write($3, 0)) { 89662da0113SBen Gras REASSIGN(fromname, NULL); 89762da0113SBen Gras fromname = renamefrom($3); 89862da0113SBen Gras } 89962da0113SBen Gras if ($3 != NULL) 90062da0113SBen Gras free($3); 90162da0113SBen Gras } 90262da0113SBen Gras ; 90362da0113SBen Gras 90462da0113SBen Gras username 90562da0113SBen Gras : STRING 90662da0113SBen Gras ; 90762da0113SBen Gras 90862da0113SBen Gras password 90962da0113SBen Gras : /* empty */ 91062da0113SBen Gras { 91162da0113SBen Gras $$ = (char *)calloc(1, sizeof(char)); 91262da0113SBen Gras } 91362da0113SBen Gras 91462da0113SBen Gras | STRING 91562da0113SBen Gras ; 91662da0113SBen Gras 91762da0113SBen Gras byte_size 91862da0113SBen Gras : NUMBER 91962da0113SBen Gras { 92062da0113SBen Gras $$ = $1.i; 92162da0113SBen Gras } 92262da0113SBen Gras ; 92362da0113SBen Gras 92462da0113SBen Gras host_port 92562da0113SBen Gras : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 92662da0113SBen Gras NUMBER COMMA NUMBER 92762da0113SBen Gras { 92862da0113SBen Gras char *a, *p; 92962da0113SBen Gras 93062da0113SBen Gras memset(&data_dest, 0, sizeof(data_dest)); 93162da0113SBen Gras data_dest.su_len = sizeof(struct sockaddr_in); 93262da0113SBen Gras data_dest.su_family = AF_INET; 93362da0113SBen Gras p = (char *)&data_dest.su_port; 93462da0113SBen Gras p[0] = $9.i; p[1] = $11.i; 93562da0113SBen Gras a = (char *)&data_dest.su_addr; 93662da0113SBen Gras a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; 93762da0113SBen Gras } 93862da0113SBen Gras ; 93962da0113SBen Gras 94062da0113SBen Gras host_long_port4 94162da0113SBen Gras : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 94262da0113SBen Gras NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 94362da0113SBen Gras NUMBER 94462da0113SBen Gras { 94562da0113SBen Gras char *a, *p; 94662da0113SBen Gras 94762da0113SBen Gras memset(&data_dest, 0, sizeof(data_dest)); 94862da0113SBen Gras data_dest.su_len = sizeof(struct sockaddr_in); 94962da0113SBen Gras data_dest.su_family = AF_INET; 95062da0113SBen Gras p = (char *)&data_dest.su_port; 95162da0113SBen Gras p[0] = $15.i; p[1] = $17.i; 95262da0113SBen Gras a = (char *)&data_dest.su_addr; 95362da0113SBen Gras a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 95462da0113SBen Gras 95562da0113SBen Gras /* reject invalid LPRT command */ 95662da0113SBen Gras if ($1.i != 4 || $3.i != 4 || $13.i != 2) 95762da0113SBen Gras memset(&data_dest, 0, sizeof(data_dest)); 95862da0113SBen Gras } 95962da0113SBen Gras ; 96062da0113SBen Gras 96162da0113SBen Gras host_long_port6 96262da0113SBen Gras : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 96362da0113SBen Gras NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 96462da0113SBen Gras NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 96562da0113SBen Gras NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 96662da0113SBen Gras NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 96762da0113SBen Gras NUMBER 96862da0113SBen Gras { 96962da0113SBen Gras #ifdef INET6 97062da0113SBen Gras unsigned char buf[16]; 97162da0113SBen Gras 97262da0113SBen Gras (void)memset(&data_dest, 0, sizeof(data_dest)); 97362da0113SBen Gras data_dest.su_len = sizeof(struct sockaddr_in6); 97462da0113SBen Gras data_dest.su_family = AF_INET6; 97562da0113SBen Gras buf[0] = $39.i; buf[1] = $41.i; 97662da0113SBen Gras (void)memcpy(&data_dest.su_port, buf, 97762da0113SBen Gras sizeof(data_dest.su_port)); 97862da0113SBen Gras buf[0] = $5.i; buf[1] = $7.i; 97962da0113SBen Gras buf[2] = $9.i; buf[3] = $11.i; 98062da0113SBen Gras buf[4] = $13.i; buf[5] = $15.i; 98162da0113SBen Gras buf[6] = $17.i; buf[7] = $19.i; 98262da0113SBen Gras buf[8] = $21.i; buf[9] = $23.i; 98362da0113SBen Gras buf[10] = $25.i; buf[11] = $27.i; 98462da0113SBen Gras buf[12] = $29.i; buf[13] = $31.i; 98562da0113SBen Gras buf[14] = $33.i; buf[15] = $35.i; 98662da0113SBen Gras (void)memcpy(&data_dest.si_su.su_sin6.sin6_addr, 98762da0113SBen Gras buf, sizeof(data_dest.si_su.su_sin6.sin6_addr)); 98862da0113SBen Gras if (his_addr.su_family == AF_INET6) { 98962da0113SBen Gras /* XXX: more sanity checks! */ 99062da0113SBen Gras data_dest.su_scope_id = his_addr.su_scope_id; 99162da0113SBen Gras } 99262da0113SBen Gras #else 99362da0113SBen Gras memset(&data_dest, 0, sizeof(data_dest)); 99462da0113SBen Gras #endif /* INET6 */ 99562da0113SBen Gras /* reject invalid LPRT command */ 99662da0113SBen Gras if ($1.i != 6 || $3.i != 16 || $37.i != 2) 99762da0113SBen Gras memset(&data_dest, 0, sizeof(data_dest)); 99862da0113SBen Gras } 99962da0113SBen Gras ; 100062da0113SBen Gras 100162da0113SBen Gras form_code 100262da0113SBen Gras : N 100362da0113SBen Gras { 100462da0113SBen Gras $$ = FORM_N; 100562da0113SBen Gras } 100662da0113SBen Gras 100762da0113SBen Gras | T 100862da0113SBen Gras { 100962da0113SBen Gras $$ = FORM_T; 101062da0113SBen Gras } 101162da0113SBen Gras 101262da0113SBen Gras | C 101362da0113SBen Gras { 101462da0113SBen Gras $$ = FORM_C; 101562da0113SBen Gras } 101662da0113SBen Gras ; 101762da0113SBen Gras 101862da0113SBen Gras type_code 101962da0113SBen Gras : A 102062da0113SBen Gras { 102162da0113SBen Gras cmd_type = TYPE_A; 102262da0113SBen Gras cmd_form = FORM_N; 102362da0113SBen Gras } 102462da0113SBen Gras 102562da0113SBen Gras | A SP form_code 102662da0113SBen Gras { 102762da0113SBen Gras cmd_type = TYPE_A; 102862da0113SBen Gras cmd_form = $3; 102962da0113SBen Gras } 103062da0113SBen Gras 103162da0113SBen Gras | E 103262da0113SBen Gras { 103362da0113SBen Gras cmd_type = TYPE_E; 103462da0113SBen Gras cmd_form = FORM_N; 103562da0113SBen Gras } 103662da0113SBen Gras 103762da0113SBen Gras | E SP form_code 103862da0113SBen Gras { 103962da0113SBen Gras cmd_type = TYPE_E; 104062da0113SBen Gras cmd_form = $3; 104162da0113SBen Gras } 104262da0113SBen Gras 104362da0113SBen Gras | I 104462da0113SBen Gras { 104562da0113SBen Gras cmd_type = TYPE_I; 104662da0113SBen Gras } 104762da0113SBen Gras 104862da0113SBen Gras | L 104962da0113SBen Gras { 105062da0113SBen Gras cmd_type = TYPE_L; 105162da0113SBen Gras cmd_bytesz = NBBY; 105262da0113SBen Gras } 105362da0113SBen Gras 105462da0113SBen Gras | L SP byte_size 105562da0113SBen Gras { 105662da0113SBen Gras cmd_type = TYPE_L; 105762da0113SBen Gras cmd_bytesz = $3; 105862da0113SBen Gras } 105962da0113SBen Gras 106062da0113SBen Gras /* this is for a bug in the BBN ftp */ 106162da0113SBen Gras | L byte_size 106262da0113SBen Gras { 106362da0113SBen Gras cmd_type = TYPE_L; 106462da0113SBen Gras cmd_bytesz = $2; 106562da0113SBen Gras } 106662da0113SBen Gras ; 106762da0113SBen Gras 106862da0113SBen Gras struct_code 106962da0113SBen Gras : F 107062da0113SBen Gras { 107162da0113SBen Gras $$ = STRU_F; 107262da0113SBen Gras } 107362da0113SBen Gras 107462da0113SBen Gras | R 107562da0113SBen Gras { 107662da0113SBen Gras $$ = STRU_R; 107762da0113SBen Gras } 107862da0113SBen Gras 107962da0113SBen Gras | P 108062da0113SBen Gras { 108162da0113SBen Gras $$ = STRU_P; 108262da0113SBen Gras } 108362da0113SBen Gras ; 108462da0113SBen Gras 108562da0113SBen Gras mode_code 108662da0113SBen Gras : S 108762da0113SBen Gras { 108862da0113SBen Gras $$ = MODE_S; 108962da0113SBen Gras } 109062da0113SBen Gras 109162da0113SBen Gras | B 109262da0113SBen Gras { 109362da0113SBen Gras $$ = MODE_B; 109462da0113SBen Gras } 109562da0113SBen Gras 109662da0113SBen Gras | C 109762da0113SBen Gras { 109862da0113SBen Gras $$ = MODE_C; 109962da0113SBen Gras } 110062da0113SBen Gras ; 110162da0113SBen Gras 110262da0113SBen Gras pathname 110362da0113SBen Gras : pathstring 110462da0113SBen Gras { 110562da0113SBen Gras /* 110662da0113SBen Gras * Problem: this production is used for all pathname 110762da0113SBen Gras * processing, but only gives a 550 error reply. 110862da0113SBen Gras * This is a valid reply in some cases but not in 110962da0113SBen Gras * others. 111062da0113SBen Gras */ 111162da0113SBen Gras if (logged_in && $1 && *$1 == '~') { 111262da0113SBen Gras char *path, *home, *result; 111362da0113SBen Gras size_t len; 111462da0113SBen Gras 111562da0113SBen Gras path = strchr($1 + 1, '/'); 111662da0113SBen Gras if (path != NULL) 111762da0113SBen Gras *path++ = '\0'; 111862da0113SBen Gras if ($1[1] == '\0') 111962da0113SBen Gras home = homedir; 112062da0113SBen Gras else { 112162da0113SBen Gras struct passwd *hpw; 112262da0113SBen Gras 112362da0113SBen Gras if ((hpw = getpwnam($1 + 1)) != NULL) 112462da0113SBen Gras home = hpw->pw_dir; 112562da0113SBen Gras else 112662da0113SBen Gras home = $1; 112762da0113SBen Gras } 112862da0113SBen Gras len = strlen(home) + 1; 112962da0113SBen Gras if (path != NULL) 113062da0113SBen Gras len += strlen(path) + 1; 113162da0113SBen Gras if ((result = malloc(len)) == NULL) 113262da0113SBen Gras fatal("Local resource failure: malloc"); 113362da0113SBen Gras strlcpy(result, home, len); 113462da0113SBen Gras if (path != NULL) { 113562da0113SBen Gras strlcat(result, "/", len); 113662da0113SBen Gras strlcat(result, path, len); 113762da0113SBen Gras } 113862da0113SBen Gras $$ = result; 113962da0113SBen Gras free($1); 114062da0113SBen Gras } else 114162da0113SBen Gras $$ = $1; 114262da0113SBen Gras } 114362da0113SBen Gras ; 114462da0113SBen Gras 114562da0113SBen Gras pathstring 114662da0113SBen Gras : STRING 114762da0113SBen Gras ; 114862da0113SBen Gras 114962da0113SBen Gras octal_number 115062da0113SBen Gras : NUMBER 115162da0113SBen Gras { 115262da0113SBen Gras int ret, dec, multby, digit; 115362da0113SBen Gras 115462da0113SBen Gras /* 115562da0113SBen Gras * Convert a number that was read as decimal number 115662da0113SBen Gras * to what it would be if it had been read as octal. 115762da0113SBen Gras */ 115862da0113SBen Gras dec = $1.i; 115962da0113SBen Gras multby = 1; 116062da0113SBen Gras ret = 0; 116162da0113SBen Gras while (dec) { 116262da0113SBen Gras digit = dec%10; 116362da0113SBen Gras if (digit > 7) { 116462da0113SBen Gras ret = -1; 116562da0113SBen Gras break; 116662da0113SBen Gras } 116762da0113SBen Gras ret += digit * multby; 116862da0113SBen Gras multby *= 8; 116962da0113SBen Gras dec /= 10; 117062da0113SBen Gras } 117162da0113SBen Gras $$ = ret; 117262da0113SBen Gras } 117362da0113SBen Gras ; 117462da0113SBen Gras 117562da0113SBen Gras mechanism_name 117662da0113SBen Gras : STRING 117762da0113SBen Gras ; 117862da0113SBen Gras 117962da0113SBen Gras base64data 118062da0113SBen Gras : STRING 118162da0113SBen Gras ; 118262da0113SBen Gras 118362da0113SBen Gras prot_code 118462da0113SBen Gras : STRING 118562da0113SBen Gras ; 118662da0113SBen Gras 118762da0113SBen Gras decimal_integer 118862da0113SBen Gras : NUMBER 118962da0113SBen Gras { 119062da0113SBen Gras $$ = $1.i; 119162da0113SBen Gras } 119262da0113SBen Gras ; 119362da0113SBen Gras 119462da0113SBen Gras check_login 119562da0113SBen Gras : /* empty */ 119662da0113SBen Gras { 119762da0113SBen Gras if (logged_in) 119862da0113SBen Gras $$ = 1; 119962da0113SBen Gras else { 120062da0113SBen Gras reply(530, "Please login with USER and PASS."); 120162da0113SBen Gras $$ = 0; 120262da0113SBen Gras hasyyerrored = 1; 120362da0113SBen Gras } 120462da0113SBen Gras } 120562da0113SBen Gras ; 120662da0113SBen Gras 120762da0113SBen Gras %% 120862da0113SBen Gras 120962da0113SBen Gras #define CMD 0 /* beginning of command */ 121062da0113SBen Gras #define ARGS 1 /* expect miscellaneous arguments */ 121162da0113SBen Gras #define STR1 2 /* expect SP followed by STRING */ 121262da0113SBen Gras #define STR2 3 /* expect STRING */ 121362da0113SBen Gras #define OSTR 4 /* optional SP then STRING */ 121462da0113SBen Gras #define ZSTR1 5 /* SP then optional STRING */ 121562da0113SBen Gras #define ZSTR2 6 /* optional STRING after SP */ 121662da0113SBen Gras #define SITECMD 7 /* SITE command */ 121762da0113SBen Gras #define NSTR 8 /* Number followed by a string */ 121862da0113SBen Gras #define NOARGS 9 /* No arguments allowed */ 121962da0113SBen Gras #define EOLN 10 /* End of line */ 122062da0113SBen Gras 122162da0113SBen Gras struct tab cmdtab[] = { 122262da0113SBen Gras /* From RFC 959, in order defined (5.3.1) */ 122362da0113SBen Gras { "USER", USER, STR1, 1, "<sp> username", 0, }, 122462da0113SBen Gras { "PASS", PASS, ZSTR1, 1, "<sp> password", 0, }, 122562da0113SBen Gras { "ACCT", ACCT, STR1, 0, "(specify account)", 0, }, 122662da0113SBen Gras { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]", 0, }, 122762da0113SBen Gras { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)", 0, }, 122862da0113SBen Gras { "SMNT", SMNT, ARGS, 0, "(structure mount)", 0, }, 122962da0113SBen Gras { "QUIT", QUIT, NOARGS, 1, "(terminate service)", 0, }, 123062da0113SBen Gras { "REIN", REIN, NOARGS, 0, "(reinitialize server state)", 0, }, 123162da0113SBen Gras { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5", 0, }, 123262da0113SBen Gras { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2...", 0, }, 123362da0113SBen Gras { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|", 0, }, 123462da0113SBen Gras { "PASV", PASV, NOARGS, 1, "(set server in passive mode)", 0, }, 123562da0113SBen Gras { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)", 0, }, 123662da0113SBen Gras { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]", 0, }, 123762da0113SBen Gras { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]", 0, }, 123862da0113SBen Gras { "STRU", STRU, ARGS, 1, "(specify file structure)", 0, }, 123962da0113SBen Gras { "MODE", MODE, ARGS, 1, "(specify transfer mode)", 0, }, 124062da0113SBen Gras { "RETR", RETR, STR1, 1, "<sp> file-name", 0, }, 124162da0113SBen Gras { "STOR", STOR, STR1, 1, "<sp> file-name", 0, }, 124262da0113SBen Gras { "STOU", STOU, STR1, 1, "<sp> file-name", 0, }, 124362da0113SBen Gras { "APPE", APPE, STR1, 1, "<sp> file-name", 0, }, 124462da0113SBen Gras { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)", 0, }, 124562da0113SBen Gras { "REST", REST, ARGS, 1, "<sp> offset (restart command)", 0, }, 124662da0113SBen Gras { "RNFR", RNFR, STR1, 1, "<sp> file-name", 0, }, 124762da0113SBen Gras { "RNTO", RNTO, STR1, 1, "<sp> file-name", 0, }, 124862da0113SBen Gras { "ABOR", ABOR, NOARGS, 4, "(abort operation)", 0, }, 124962da0113SBen Gras { "DELE", DELE, STR1, 1, "<sp> file-name", 0, }, 125062da0113SBen Gras { "RMD", RMD, STR1, 1, "<sp> path-name", 0, }, 125162da0113SBen Gras { "MKD", MKD, STR1, 1, "<sp> path-name", 0, }, 125262da0113SBen Gras { "PWD", PWD, NOARGS, 1, "(return current directory)", 0, }, 125362da0113SBen Gras { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]", 0, }, 125462da0113SBen Gras { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]", 0, }, 125562da0113SBen Gras { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]", 0, }, 125662da0113SBen Gras { "SYST", SYST, NOARGS, 1, "(get type of operating system)", 0, }, 125762da0113SBen Gras { "STAT", STAT, OSTR, 4, "[ <sp> path-name ]", 0, }, 125862da0113SBen Gras { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]", 0, }, 125962da0113SBen Gras { "NOOP", NOOP, NOARGS, 2, "", 0, }, 126062da0113SBen Gras 126162da0113SBen Gras /* From RFC 2228, in order defined */ 126262da0113SBen Gras { "AUTH", AUTH, STR1, 1, "<sp> mechanism-name", 0, }, 126362da0113SBen Gras { "ADAT", ADAT, STR1, 1, "<sp> base-64-data", 0, }, 126462da0113SBen Gras { "PROT", PROT, STR1, 1, "<sp> prot-code", 0, }, 126562da0113SBen Gras { "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer", 0, }, 126662da0113SBen Gras { "CCC", CCC, NOARGS, 1, "(Disable data protection)", 0, }, 126762da0113SBen Gras { "MIC", MIC, STR1, 4, "<sp> base64data", 0, }, 126862da0113SBen Gras { "CONF", CONF, STR1, 4, "<sp> base64data", 0, }, 126962da0113SBen Gras { "ENC", ENC, STR1, 4, "<sp> base64data", 0, }, 127062da0113SBen Gras 127162da0113SBen Gras /* From RFC 2389, in order defined */ 127262da0113SBen Gras { "FEAT", FEAT, NOARGS, 1, "(display extended features)", 0, }, 127362da0113SBen Gras { "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]", 0, }, 127462da0113SBen Gras 127562da0113SBen Gras /* From RFC 3659, in order defined */ 127662da0113SBen Gras { "MDTM", MDTM, OSTR, 1, "<sp> path-name", 0, }, 127762da0113SBen Gras { "SIZE", SIZE, OSTR, 1, "<sp> path-name", 0, }, 127862da0113SBen Gras { "MLST", MLST, OSTR, 2, "[ <sp> path-name ]", 0, }, 127962da0113SBen Gras { "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]", 0, }, 128062da0113SBen Gras 128162da0113SBen Gras /* obsolete commands */ 128262da0113SBen Gras { "MAIL", MAIL, OSTR, 0, "(mail to user)", 0, }, 128362da0113SBen Gras { "MLFL", MLFL, OSTR, 0, "(mail file)", 0, }, 128462da0113SBen Gras { "MRCP", MRCP, STR1, 0, "(mail recipient)", 0, }, 128562da0113SBen Gras { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)", 0, }, 128662da0113SBen Gras { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)", 0, }, 128762da0113SBen Gras { "MSND", MSND, OSTR, 0, "(mail send to terminal)", 0, }, 128862da0113SBen Gras { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)", 0, }, 128962da0113SBen Gras { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)", 0, }, 129062da0113SBen Gras { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]", 0, }, 129162da0113SBen Gras { "XMKD", MKD, STR1, 1, "<sp> path-name", 0, }, 129262da0113SBen Gras { "XPWD", PWD, NOARGS, 1, "(return current directory)", 0, }, 129362da0113SBen Gras { "XRMD", RMD, STR1, 1, "<sp> path-name", 0, }, 129462da0113SBen Gras 129562da0113SBen Gras { NULL, 0, 0, 0, 0, 0, } 129662da0113SBen Gras }; 129762da0113SBen Gras 129862da0113SBen Gras struct tab sitetab[] = { 129962da0113SBen Gras { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name", 0, }, 130062da0113SBen Gras { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]", 0, }, 130162da0113SBen Gras { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]", 0, }, 130262da0113SBen Gras { "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]", 0, }, 130362da0113SBen Gras { "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]", 0, }, 130462da0113SBen Gras { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]", 0, }, 130562da0113SBen Gras { NULL, 0, 0, 0, 0, 0, } 130662da0113SBen Gras }; 130762da0113SBen Gras 130862da0113SBen Gras /* 130962da0113SBen Gras * Check if a filename is allowed to be modified (isupload == 0) or 131062da0113SBen Gras * uploaded (isupload == 1), and if necessary, check the filename is `sane'. 131162da0113SBen Gras * If the filename is NULL, fail. 131262da0113SBen Gras * If the filename is "", don't do the sane name check. 131362da0113SBen Gras */ 131462da0113SBen Gras static int 131562da0113SBen Gras check_write(const char *file, int isupload) 131662da0113SBen Gras { 131762da0113SBen Gras if (file == NULL) 131862da0113SBen Gras return (0); 131962da0113SBen Gras if (! logged_in) { 132062da0113SBen Gras reply(530, "Please login with USER and PASS."); 132162da0113SBen Gras return (0); 132262da0113SBen Gras } 132362da0113SBen Gras /* checking modify */ 132462da0113SBen Gras if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) { 132562da0113SBen Gras reply(502, "No permission to use this command."); 132662da0113SBen Gras return (0); 132762da0113SBen Gras } 132862da0113SBen Gras /* checking upload */ 132962da0113SBen Gras if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) { 133062da0113SBen Gras reply(502, "No permission to use this command."); 133162da0113SBen Gras return (0); 133262da0113SBen Gras } 133362da0113SBen Gras 133462da0113SBen Gras /* checking sanenames */ 133562da0113SBen Gras if (file[0] != '\0' && CURCLASS_FLAGS_ISSET(sanenames)) { 133662da0113SBen Gras const char *p; 133762da0113SBen Gras 133862da0113SBen Gras if (file[0] == '.') 133962da0113SBen Gras goto insane_name; 134062da0113SBen Gras for (p = file; *p; p++) { 134162da0113SBen Gras if (isalnum((unsigned char)*p) || *p == '-' || *p == '+' || 134262da0113SBen Gras *p == ',' || *p == '.' || *p == '_') 134362da0113SBen Gras continue; 134462da0113SBen Gras insane_name: 134562da0113SBen Gras reply(553, "File name `%s' not allowed.", file); 134662da0113SBen Gras return (0); 134762da0113SBen Gras } 134862da0113SBen Gras } 134962da0113SBen Gras return (1); 135062da0113SBen Gras } 135162da0113SBen Gras 135262da0113SBen Gras struct tab * 135362da0113SBen Gras lookup(struct tab *p, const char *cmd) 135462da0113SBen Gras { 135562da0113SBen Gras 135662da0113SBen Gras for (; p->name != NULL; p++) 135762da0113SBen Gras if (strcasecmp(cmd, p->name) == 0) 135862da0113SBen Gras return (p); 135962da0113SBen Gras return (0); 136062da0113SBen Gras } 136162da0113SBen Gras 136262da0113SBen Gras #include <arpa/telnet.h> 136362da0113SBen Gras 136462da0113SBen Gras /* 136562da0113SBen Gras * get_line - a hacked up version of fgets to ignore TELNET escape codes. 136662da0113SBen Gras * `s' is the buffer to read into. 136762da0113SBen Gras * `n' is the 1 less than the size of the buffer, to allow trailing NUL 136862da0113SBen Gras * `iop' is the FILE to read from. 136962da0113SBen Gras * Returns 0 on success, -1 on EOF, -2 if the command was too long. 137062da0113SBen Gras */ 137162da0113SBen Gras int 137262da0113SBen Gras get_line(char *s, int n, FILE *iop) 137362da0113SBen Gras { 137462da0113SBen Gras int c; 137562da0113SBen Gras char *cs; 137662da0113SBen Gras 137762da0113SBen Gras cs = s; 137862da0113SBen Gras /* tmpline may contain saved command from urgent mode interruption */ 137962da0113SBen Gras for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 138062da0113SBen Gras *cs++ = tmpline[c]; 138162da0113SBen Gras if (tmpline[c] == '\n') { 138262da0113SBen Gras *cs++ = '\0'; 138362da0113SBen Gras if (ftpd_debug) 138462da0113SBen Gras syslog(LOG_DEBUG, "command: %s", s); 138562da0113SBen Gras tmpline[0] = '\0'; 138662da0113SBen Gras return(0); 138762da0113SBen Gras } 138862da0113SBen Gras if (c == 0) 138962da0113SBen Gras tmpline[0] = '\0'; 139062da0113SBen Gras } 139162da0113SBen Gras while ((c = getc(iop)) != EOF) { 139262da0113SBen Gras total_bytes++; 139362da0113SBen Gras total_bytes_in++; 139462da0113SBen Gras c &= 0377; 139562da0113SBen Gras if (c == IAC) { 139662da0113SBen Gras if ((c = getc(iop)) != EOF) { 139762da0113SBen Gras total_bytes++; 139862da0113SBen Gras total_bytes_in++; 139962da0113SBen Gras c &= 0377; 140062da0113SBen Gras switch (c) { 140162da0113SBen Gras case WILL: 140262da0113SBen Gras case WONT: 140362da0113SBen Gras c = getc(iop); 140462da0113SBen Gras total_bytes++; 140562da0113SBen Gras total_bytes_in++; 140662da0113SBen Gras cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c); 140762da0113SBen Gras (void) fflush(stdout); 140862da0113SBen Gras continue; 140962da0113SBen Gras case DO: 141062da0113SBen Gras case DONT: 141162da0113SBen Gras c = getc(iop); 141262da0113SBen Gras total_bytes++; 141362da0113SBen Gras total_bytes_in++; 141462da0113SBen Gras cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c); 141562da0113SBen Gras (void) fflush(stdout); 141662da0113SBen Gras continue; 141762da0113SBen Gras case IAC: 141862da0113SBen Gras break; 141962da0113SBen Gras default: 142062da0113SBen Gras continue; /* ignore command */ 142162da0113SBen Gras } 142262da0113SBen Gras } 142362da0113SBen Gras } 142462da0113SBen Gras *cs++ = c; 142562da0113SBen Gras if (--n <= 0) { 142662da0113SBen Gras /* 142762da0113SBen Gras * If command doesn't fit into buffer, discard the 142862da0113SBen Gras * rest of the command and indicate truncation. 142962da0113SBen Gras * This prevents the command to be split up into 143062da0113SBen Gras * multiple commands. 143162da0113SBen Gras */ 143262da0113SBen Gras if (ftpd_debug) 143362da0113SBen Gras syslog(LOG_DEBUG, 143462da0113SBen Gras "command too long, last char: %d", c); 143562da0113SBen Gras while (c != '\n' && (c = getc(iop)) != EOF) 143662da0113SBen Gras continue; 143762da0113SBen Gras return (-2); 143862da0113SBen Gras } 143962da0113SBen Gras if (c == '\n') 144062da0113SBen Gras break; 144162da0113SBen Gras } 144262da0113SBen Gras if (c == EOF && cs == s) 144362da0113SBen Gras return (-1); 144462da0113SBen Gras *cs++ = '\0'; 144562da0113SBen Gras if (ftpd_debug) { 144662da0113SBen Gras if ((curclass.type != CLASS_GUEST && 144762da0113SBen Gras strncasecmp(s, "PASS ", 5) == 0) || 144862da0113SBen Gras strncasecmp(s, "ACCT ", 5) == 0) { 144962da0113SBen Gras /* Don't syslog passwords */ 145062da0113SBen Gras syslog(LOG_DEBUG, "command: %.4s ???", s); 145162da0113SBen Gras } else { 145262da0113SBen Gras char *cp; 145362da0113SBen Gras int len; 145462da0113SBen Gras 145562da0113SBen Gras /* Don't syslog trailing CR-LF */ 145662da0113SBen Gras len = strlen(s); 145762da0113SBen Gras cp = s + len - 1; 145862da0113SBen Gras while (cp >= s && (*cp == '\n' || *cp == '\r')) { 145962da0113SBen Gras --cp; 146062da0113SBen Gras --len; 146162da0113SBen Gras } 146262da0113SBen Gras syslog(LOG_DEBUG, "command: %.*s", len, s); 146362da0113SBen Gras } 146462da0113SBen Gras } 146562da0113SBen Gras return (0); 146662da0113SBen Gras } 146762da0113SBen Gras 146862da0113SBen Gras void 146962da0113SBen Gras ftp_handle_line(char *cp) 147062da0113SBen Gras { 147162da0113SBen Gras 147262da0113SBen Gras cmdp = cp; 147362da0113SBen Gras yyparse(); 147462da0113SBen Gras } 147562da0113SBen Gras 147662da0113SBen Gras void 147762da0113SBen Gras ftp_loop(void) 147862da0113SBen Gras { 147962da0113SBen Gras int ret; 148062da0113SBen Gras 148162da0113SBen Gras while (1) { 148262da0113SBen Gras (void) alarm(curclass.timeout); 148362da0113SBen Gras ret = get_line(cbuf, sizeof(cbuf)-1, stdin); 148462da0113SBen Gras (void) alarm(0); 148562da0113SBen Gras if (ret == -1) { 148662da0113SBen Gras reply(221, "You could at least say goodbye."); 148762da0113SBen Gras dologout(0); 148862da0113SBen Gras } else if (ret == -2) { 148962da0113SBen Gras reply(500, "Command too long."); 149062da0113SBen Gras } else { 149162da0113SBen Gras ftp_handle_line(cbuf); 149262da0113SBen Gras } 149362da0113SBen Gras } 149462da0113SBen Gras /*NOTREACHED*/ 149562da0113SBen Gras } 149662da0113SBen Gras 149762da0113SBen Gras int 149862da0113SBen Gras yylex(void) 149962da0113SBen Gras { 150062da0113SBen Gras static int cpos, state; 150162da0113SBen Gras char *cp, *cp2; 150262da0113SBen Gras struct tab *p; 150362da0113SBen Gras int n; 150462da0113SBen Gras char c; 150562da0113SBen Gras 150662da0113SBen Gras switch (state) { 150762da0113SBen Gras 150862da0113SBen Gras case CMD: 150962da0113SBen Gras hasyyerrored = 0; 151062da0113SBen Gras if ((cp = strchr(cmdp, '\r'))) { 151162da0113SBen Gras *cp = '\0'; 151262da0113SBen Gras #if defined(HAVE_SETPROCTITLE) 151362da0113SBen Gras if (strncasecmp(cmdp, "PASS", 4) != 0 && 151462da0113SBen Gras strncasecmp(cmdp, "ACCT", 4) != 0) 151562da0113SBen Gras setproctitle("%s: %s", proctitle, cmdp); 151662da0113SBen Gras #endif /* defined(HAVE_SETPROCTITLE) */ 151762da0113SBen Gras *cp++ = '\n'; 151862da0113SBen Gras *cp = '\0'; 151962da0113SBen Gras } 152062da0113SBen Gras if ((cp = strpbrk(cmdp, " \n"))) 152162da0113SBen Gras cpos = cp - cmdp; 152262da0113SBen Gras if (cpos == 0) 152362da0113SBen Gras cpos = 4; 152462da0113SBen Gras c = cmdp[cpos]; 152562da0113SBen Gras cmdp[cpos] = '\0'; 152662da0113SBen Gras p = lookup(cmdtab, cmdp); 152762da0113SBen Gras cmdp[cpos] = c; 152862da0113SBen Gras if (p != NULL) { 152962da0113SBen Gras if (is_oob && ! CMD_OOB(p)) { 153062da0113SBen Gras /* command will be handled in-band */ 153162da0113SBen Gras return (0); 153262da0113SBen Gras } else if (! CMD_IMPLEMENTED(p)) { 153362da0113SBen Gras reply(502, "%s command not implemented.", 153462da0113SBen Gras p->name); 153562da0113SBen Gras hasyyerrored = 1; 153662da0113SBen Gras break; 153762da0113SBen Gras } 153862da0113SBen Gras state = p->state; 153962da0113SBen Gras yylval.cs = p->name; 154062da0113SBen Gras return (p->token); 154162da0113SBen Gras } 154262da0113SBen Gras break; 154362da0113SBen Gras 154462da0113SBen Gras case SITECMD: 154562da0113SBen Gras if (cmdp[cpos] == ' ') { 154662da0113SBen Gras cpos++; 154762da0113SBen Gras return (SP); 154862da0113SBen Gras } 154962da0113SBen Gras cp = &cmdp[cpos]; 155062da0113SBen Gras if ((cp2 = strpbrk(cp, " \n"))) 155162da0113SBen Gras cpos = cp2 - cmdp; 155262da0113SBen Gras c = cmdp[cpos]; 155362da0113SBen Gras cmdp[cpos] = '\0'; 155462da0113SBen Gras p = lookup(sitetab, cp); 155562da0113SBen Gras cmdp[cpos] = c; 155662da0113SBen Gras if (p != NULL) { 155762da0113SBen Gras if (!CMD_IMPLEMENTED(p)) { 155862da0113SBen Gras reply(502, "SITE %s command not implemented.", 155962da0113SBen Gras p->name); 156062da0113SBen Gras hasyyerrored = 1; 156162da0113SBen Gras break; 156262da0113SBen Gras } 156362da0113SBen Gras state = p->state; 156462da0113SBen Gras yylval.cs = p->name; 156562da0113SBen Gras return (p->token); 156662da0113SBen Gras } 156762da0113SBen Gras break; 156862da0113SBen Gras 156962da0113SBen Gras case OSTR: 157062da0113SBen Gras if (cmdp[cpos] == '\n') { 157162da0113SBen Gras state = EOLN; 157262da0113SBen Gras return (CRLF); 157362da0113SBen Gras } 157462da0113SBen Gras /* FALLTHROUGH */ 157562da0113SBen Gras 157662da0113SBen Gras case STR1: 157762da0113SBen Gras case ZSTR1: 157862da0113SBen Gras dostr1: 157962da0113SBen Gras if (cmdp[cpos] == ' ') { 158062da0113SBen Gras cpos++; 158162da0113SBen Gras state = state == OSTR ? STR2 : state+1; 158262da0113SBen Gras return (SP); 158362da0113SBen Gras } 158462da0113SBen Gras break; 158562da0113SBen Gras 158662da0113SBen Gras case ZSTR2: 158762da0113SBen Gras if (cmdp[cpos] == '\n') { 158862da0113SBen Gras state = EOLN; 158962da0113SBen Gras return (CRLF); 159062da0113SBen Gras } 159162da0113SBen Gras /* FALLTHROUGH */ 159262da0113SBen Gras 159362da0113SBen Gras case STR2: 159462da0113SBen Gras cp = &cmdp[cpos]; 159562da0113SBen Gras n = strlen(cp); 159662da0113SBen Gras cpos += n - 1; 159762da0113SBen Gras /* 159862da0113SBen Gras * Make sure the string is nonempty and \n terminated. 159962da0113SBen Gras */ 160062da0113SBen Gras if (n > 1 && cmdp[cpos] == '\n') { 160162da0113SBen Gras cmdp[cpos] = '\0'; 160262da0113SBen Gras yylval.s = ftpd_strdup(cp); 160362da0113SBen Gras cmdp[cpos] = '\n'; 160462da0113SBen Gras state = ARGS; 160562da0113SBen Gras return (STRING); 160662da0113SBen Gras } 160762da0113SBen Gras break; 160862da0113SBen Gras 160962da0113SBen Gras case NSTR: 161062da0113SBen Gras if (cmdp[cpos] == ' ') { 161162da0113SBen Gras cpos++; 161262da0113SBen Gras return (SP); 161362da0113SBen Gras } 161462da0113SBen Gras if (isdigit((unsigned char)cmdp[cpos])) { 161562da0113SBen Gras cp = &cmdp[cpos]; 161662da0113SBen Gras while (isdigit((unsigned char)cmdp[++cpos])) 161762da0113SBen Gras ; 161862da0113SBen Gras c = cmdp[cpos]; 161962da0113SBen Gras cmdp[cpos] = '\0'; 162062da0113SBen Gras yylval.u.i = atoi(cp); 162162da0113SBen Gras cmdp[cpos] = c; 162262da0113SBen Gras state = STR1; 162362da0113SBen Gras return (NUMBER); 162462da0113SBen Gras } 162562da0113SBen Gras state = STR1; 162662da0113SBen Gras goto dostr1; 162762da0113SBen Gras 162862da0113SBen Gras case ARGS: 162962da0113SBen Gras if (isdigit((unsigned char)cmdp[cpos])) { 163062da0113SBen Gras cp = &cmdp[cpos]; 163162da0113SBen Gras while (isdigit((unsigned char)cmdp[++cpos])) 163262da0113SBen Gras ; 163362da0113SBen Gras c = cmdp[cpos]; 163462da0113SBen Gras cmdp[cpos] = '\0'; 163562da0113SBen Gras yylval.u.i = atoi(cp); 163662da0113SBen Gras yylval.u.ll = STRTOLL(cp, NULL, 10); 163762da0113SBen Gras cmdp[cpos] = c; 163862da0113SBen Gras return (NUMBER); 163962da0113SBen Gras } 164062da0113SBen Gras if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0 164162da0113SBen Gras && !isalnum((unsigned char)cmdp[cpos + 3])) { 164262da0113SBen Gras cpos += 3; 164362da0113SBen Gras return (ALL); 164462da0113SBen Gras } 164562da0113SBen Gras switch (cmdp[cpos++]) { 164662da0113SBen Gras 164762da0113SBen Gras case '\n': 164862da0113SBen Gras state = EOLN; 164962da0113SBen Gras return (CRLF); 165062da0113SBen Gras 165162da0113SBen Gras case ' ': 165262da0113SBen Gras return (SP); 165362da0113SBen Gras 165462da0113SBen Gras case ',': 165562da0113SBen Gras return (COMMA); 165662da0113SBen Gras 165762da0113SBen Gras case 'A': 165862da0113SBen Gras case 'a': 165962da0113SBen Gras return (A); 166062da0113SBen Gras 166162da0113SBen Gras case 'B': 166262da0113SBen Gras case 'b': 166362da0113SBen Gras return (B); 166462da0113SBen Gras 166562da0113SBen Gras case 'C': 166662da0113SBen Gras case 'c': 166762da0113SBen Gras return (C); 166862da0113SBen Gras 166962da0113SBen Gras case 'E': 167062da0113SBen Gras case 'e': 167162da0113SBen Gras return (E); 167262da0113SBen Gras 167362da0113SBen Gras case 'F': 167462da0113SBen Gras case 'f': 167562da0113SBen Gras return (F); 167662da0113SBen Gras 167762da0113SBen Gras case 'I': 167862da0113SBen Gras case 'i': 167962da0113SBen Gras return (I); 168062da0113SBen Gras 168162da0113SBen Gras case 'L': 168262da0113SBen Gras case 'l': 168362da0113SBen Gras return (L); 168462da0113SBen Gras 168562da0113SBen Gras case 'N': 168662da0113SBen Gras case 'n': 168762da0113SBen Gras return (N); 168862da0113SBen Gras 168962da0113SBen Gras case 'P': 169062da0113SBen Gras case 'p': 169162da0113SBen Gras return (P); 169262da0113SBen Gras 169362da0113SBen Gras case 'R': 169462da0113SBen Gras case 'r': 169562da0113SBen Gras return (R); 169662da0113SBen Gras 169762da0113SBen Gras case 'S': 169862da0113SBen Gras case 's': 169962da0113SBen Gras return (S); 170062da0113SBen Gras 170162da0113SBen Gras case 'T': 170262da0113SBen Gras case 't': 170362da0113SBen Gras return (T); 170462da0113SBen Gras 170562da0113SBen Gras } 170662da0113SBen Gras break; 170762da0113SBen Gras 170862da0113SBen Gras case NOARGS: 170962da0113SBen Gras if (cmdp[cpos] == '\n') { 171062da0113SBen Gras state = EOLN; 171162da0113SBen Gras return (CRLF); 171262da0113SBen Gras } 171362da0113SBen Gras c = cmdp[cpos]; 171462da0113SBen Gras cmdp[cpos] = '\0'; 171562da0113SBen Gras reply(501, "'%s' command does not take any arguments.", cmdp); 171662da0113SBen Gras hasyyerrored = 1; 171762da0113SBen Gras cmdp[cpos] = c; 171862da0113SBen Gras break; 171962da0113SBen Gras 172062da0113SBen Gras case EOLN: 172162da0113SBen Gras state = CMD; 172262da0113SBen Gras return (0); 172362da0113SBen Gras 172462da0113SBen Gras default: 172562da0113SBen Gras fatal("Unknown state in scanner."); 172662da0113SBen Gras } 172762da0113SBen Gras yyerror(NULL); 172862da0113SBen Gras state = CMD; 172962da0113SBen Gras return (0); 173062da0113SBen Gras } 173162da0113SBen Gras 173262da0113SBen Gras /* ARGSUSED */ 173362da0113SBen Gras void 173462da0113SBen Gras yyerror(const char *s) 173562da0113SBen Gras { 173662da0113SBen Gras char *cp; 173762da0113SBen Gras 173862da0113SBen Gras if (hasyyerrored || is_oob) 173962da0113SBen Gras return; 174062da0113SBen Gras if ((cp = strchr(cmdp,'\n')) != NULL) 174162da0113SBen Gras *cp = '\0'; 174262da0113SBen Gras reply(500, "'%s': command not understood.", cmdp); 174362da0113SBen Gras hasyyerrored = 1; 174462da0113SBen Gras } 174562da0113SBen Gras 174662da0113SBen Gras static void 174762da0113SBen Gras help(struct tab *ctab, const char *s) 174862da0113SBen Gras { 174962da0113SBen Gras struct tab *c; 175062da0113SBen Gras int width, NCMDS; 175162da0113SBen Gras const char *htype; 175262da0113SBen Gras 175362da0113SBen Gras if (ctab == sitetab) 175462da0113SBen Gras htype = "SITE "; 175562da0113SBen Gras else 175662da0113SBen Gras htype = ""; 175762da0113SBen Gras width = 0, NCMDS = 0; 175862da0113SBen Gras for (c = ctab; c->name != NULL; c++) { 175962da0113SBen Gras int len = strlen(c->name); 176062da0113SBen Gras 176162da0113SBen Gras if (len > width) 176262da0113SBen Gras width = len; 176362da0113SBen Gras NCMDS++; 176462da0113SBen Gras } 176562da0113SBen Gras width = (width + 8) &~ 7; 176662da0113SBen Gras if (s == 0) { 176762da0113SBen Gras int i, j, w; 176862da0113SBen Gras int columns, lines; 176962da0113SBen Gras 177062da0113SBen Gras reply(-214, "%s", ""); 177162da0113SBen Gras reply(0, "The following %scommands are recognized.", htype); 177262da0113SBen Gras reply(0, "(`-' = not implemented, `+' = supports options)"); 177362da0113SBen Gras columns = 76 / width; 177462da0113SBen Gras if (columns == 0) 177562da0113SBen Gras columns = 1; 177662da0113SBen Gras lines = (NCMDS + columns - 1) / columns; 177762da0113SBen Gras for (i = 0; i < lines; i++) { 177862da0113SBen Gras cprintf(stdout, " "); 177962da0113SBen Gras for (j = 0; j < columns; j++) { 178062da0113SBen Gras c = ctab + j * lines + i; 178162da0113SBen Gras cprintf(stdout, "%s", c->name); 178262da0113SBen Gras w = strlen(c->name); 178362da0113SBen Gras if (! CMD_IMPLEMENTED(c)) { 178462da0113SBen Gras CPUTC('-', stdout); 178562da0113SBen Gras w++; 178662da0113SBen Gras } 178762da0113SBen Gras if (CMD_HAS_OPTIONS(c)) { 178862da0113SBen Gras CPUTC('+', stdout); 178962da0113SBen Gras w++; 179062da0113SBen Gras } 179162da0113SBen Gras if (c + lines >= &ctab[NCMDS]) 179262da0113SBen Gras break; 179362da0113SBen Gras while (w < width) { 179462da0113SBen Gras CPUTC(' ', stdout); 179562da0113SBen Gras w++; 179662da0113SBen Gras } 179762da0113SBen Gras } 179862da0113SBen Gras cprintf(stdout, "\r\n"); 179962da0113SBen Gras } 180062da0113SBen Gras (void) fflush(stdout); 180162da0113SBen Gras reply(214, "Direct comments to ftp-bugs@%s.", hostname); 180262da0113SBen Gras return; 180362da0113SBen Gras } 180462da0113SBen Gras c = lookup(ctab, s); 180562da0113SBen Gras if (c == (struct tab *)0) { 180662da0113SBen Gras reply(502, "Unknown command '%s'.", s); 180762da0113SBen Gras return; 180862da0113SBen Gras } 180962da0113SBen Gras if (CMD_IMPLEMENTED(c)) 181062da0113SBen Gras reply(214, "Syntax: %s%s %s", htype, c->name, c->help); 181162da0113SBen Gras else 181262da0113SBen Gras reply(504, "%s%-*s\t%s; not implemented.", htype, width, 181362da0113SBen Gras c->name, c->help); 181462da0113SBen Gras } 181562da0113SBen Gras 181662da0113SBen Gras /* 181762da0113SBen Gras * Check that the structures used for a PORT, LPRT or EPRT command are 181862da0113SBen Gras * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks. 181962da0113SBen Gras * If family != -1 check that his_addr.su_family == family. 182062da0113SBen Gras */ 182162da0113SBen Gras static void 182262da0113SBen Gras port_check(const char *cmd, int family) 182362da0113SBen Gras { 182462da0113SBen Gras char h1[NI_MAXHOST], h2[NI_MAXHOST]; 182562da0113SBen Gras char s1[NI_MAXHOST], s2[NI_MAXHOST]; 182662da0113SBen Gras #ifdef NI_WITHSCOPEID 182762da0113SBen Gras const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID; 182862da0113SBen Gras #else 182962da0113SBen Gras const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; 183062da0113SBen Gras #endif 183162da0113SBen Gras 183262da0113SBen Gras if (epsvall) { 183362da0113SBen Gras reply(501, "%s disallowed after EPSV ALL", cmd); 183462da0113SBen Gras return; 183562da0113SBen Gras } 183662da0113SBen Gras 183762da0113SBen Gras if (family != -1 && his_addr.su_family != family) { 183862da0113SBen Gras port_check_fail: 183962da0113SBen Gras reply(500, "Illegal %s command rejected", cmd); 184062da0113SBen Gras return; 184162da0113SBen Gras } 184262da0113SBen Gras 184362da0113SBen Gras if (data_dest.su_family != his_addr.su_family) 184462da0113SBen Gras goto port_check_fail; 184562da0113SBen Gras 184662da0113SBen Gras /* be paranoid, if told so */ 184762da0113SBen Gras if (CURCLASS_FLAGS_ISSET(checkportcmd)) { 184862da0113SBen Gras #ifdef INET6 184962da0113SBen Gras /* 185062da0113SBen Gras * be paranoid, there are getnameinfo implementation that does 185162da0113SBen Gras * not present scopeid portion 185262da0113SBen Gras */ 185362da0113SBen Gras if (data_dest.su_family == AF_INET6 && 185462da0113SBen Gras data_dest.su_scope_id != his_addr.su_scope_id) 185562da0113SBen Gras goto port_check_fail; 185662da0113SBen Gras #endif 185762da0113SBen Gras 185862da0113SBen Gras if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len, 185962da0113SBen Gras h1, sizeof(h1), s1, sizeof(s1), niflags)) 186062da0113SBen Gras goto port_check_fail; 186162da0113SBen Gras if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 186262da0113SBen Gras h2, sizeof(h2), s2, sizeof(s2), niflags)) 186362da0113SBen Gras goto port_check_fail; 186462da0113SBen Gras 186562da0113SBen Gras if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0) 186662da0113SBen Gras goto port_check_fail; 186762da0113SBen Gras } 186862da0113SBen Gras 186962da0113SBen Gras usedefault = 0; 187062da0113SBen Gras if (pdata >= 0) { 187162da0113SBen Gras (void) close(pdata); 187262da0113SBen Gras pdata = -1; 187362da0113SBen Gras } 187462da0113SBen Gras reply(200, "%s command successful.", cmd); 187562da0113SBen Gras } 1876