1161764Sobrien/* $NetBSD: ftpcmd.y,v 1.84 2006/02/01 14:20:12 christos Exp $ */ 279968Sobrien 379968Sobrien/*- 4161764Sobrien * Copyright (c) 1997-2005 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. 51133936Sobrien * 3. Neither the name of the University nor the names of its contributors 5279968Sobrien * may be used to endorse or promote products derived from this software 5379968Sobrien * without specific prior written permission. 5479968Sobrien * 5579968Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 5679968Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5779968Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5879968Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5979968Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 6079968Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 6179968Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 6279968Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 6379968Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 6479968Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 6579968Sobrien * SUCH DAMAGE. 6679968Sobrien * 6779968Sobrien * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 6879968Sobrien */ 6979968Sobrien 7079968Sobrien/* 7179968Sobrien * Grammar for FTP commands. 7279968Sobrien * See RFC 959. 7379968Sobrien */ 7479968Sobrien 7579968Sobrien%{ 76108746Sobrien#include <sys/cdefs.h> 7779968Sobrien 78108746Sobrien#ifndef lint 79108746Sobrien#if 0 80108746Sobrienstatic char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; 81108746Sobrien#else 82161764Sobrien__RCSID("$NetBSD: ftpcmd.y,v 1.84 2006/02/01 14:20:12 christos Exp $"); 83108746Sobrien#endif 84108746Sobrien#endif /* not lint */ 85108746Sobrien 86108746Sobrien#include <sys/param.h> 87108746Sobrien#include <sys/socket.h> 88108746Sobrien#include <sys/stat.h> 89108746Sobrien 90108746Sobrien#include <netinet/in.h> 91108746Sobrien#include <arpa/ftp.h> 92108746Sobrien#include <arpa/inet.h> 93108746Sobrien 94108746Sobrien#include <ctype.h> 95108746Sobrien#include <errno.h> 96108746Sobrien#include <pwd.h> 97108746Sobrien#include <stdio.h> 98108746Sobrien#include <stdlib.h> 99108746Sobrien#include <string.h> 100108746Sobrien#include <syslog.h> 101108746Sobrien#include <time.h> 102108746Sobrien#include <tzfile.h> 103108746Sobrien#include <unistd.h> 104108746Sobrien#include <netdb.h> 105108746Sobrien 106108746Sobrien#ifdef KERBEROS5 107108746Sobrien#include <krb5/krb5.h> 108108746Sobrien#endif 109108746Sobrien 11079968Sobrien#include "extern.h" 11179968Sobrien#include "version.h" 11279968Sobrien 11379968Sobrienstatic int cmd_type; 11479968Sobrienstatic int cmd_form; 11579968Sobrienstatic int cmd_bytesz; 11679968Sobrien 11779968Sobrienchar cbuf[FTP_BUFLEN]; 11879968Sobrienchar *cmdp; 11979968Sobrienchar *fromname; 12079968Sobrien 121161764Sobrienextern int epsvall; 122161764Sobrienstruct tab sitetab[]; 123161764Sobrien 124161764Sobrienstatic int check_write(const char *, int); 125161764Sobrienstatic void help(struct tab *, const char *); 126161764Sobrienstatic void port_check(const char *, int); 127161764Sobrien int yylex(void); 128161764Sobrien 12979968Sobrien%} 13079968Sobrien 13179968Sobrien%union { 132108746Sobrien struct { 133133936Sobrien LLT ll; 134108746Sobrien int i; 135108746Sobrien } u; 13679968Sobrien char *s; 13779968Sobrien} 13879968Sobrien 13979968Sobrien%token 14079968Sobrien A B C E F I 14179968Sobrien L N P R S T 14279968Sobrien 143133936Sobrien SP CRLF COMMA ALL 14479968Sobrien 14579968Sobrien USER PASS ACCT CWD CDUP SMNT 14679968Sobrien QUIT REIN PORT PASV TYPE STRU 14779968Sobrien MODE RETR STOR STOU APPE ALLO 14879968Sobrien REST RNFR RNTO ABOR DELE RMD 14979968Sobrien MKD PWD LIST NLST SITE SYST 15079968Sobrien STAT HELP NOOP 15179968Sobrien 15279968Sobrien AUTH ADAT PROT PBSZ CCC MIC 15379968Sobrien CONF ENC 15479968Sobrien 15579968Sobrien FEAT OPTS 15679968Sobrien 15779968Sobrien SIZE MDTM MLST MLSD 15879968Sobrien 15979968Sobrien LPRT LPSV EPRT EPSV 16079968Sobrien 16179968Sobrien MAIL MLFL MRCP MRSQ MSAM MSND 16279968Sobrien MSOM 16379968Sobrien 16479968Sobrien CHMOD IDLE RATEGET RATEPUT UMASK 16579968Sobrien 16679968Sobrien LEXERR 16779968Sobrien 16879968Sobrien%token <s> STRING 169108746Sobrien%token <u> NUMBER 17079968Sobrien 171108746Sobrien%type <u.i> check_login octal_number byte_size 172108746Sobrien%type <u.i> struct_code mode_code type_code form_code decimal_integer 17379968Sobrien%type <s> pathstring pathname password username 17479968Sobrien%type <s> mechanism_name base64data prot_code 17579968Sobrien 17679968Sobrien%start cmd_sel 17779968Sobrien 17879968Sobrien%% 17979968Sobrien 18079968Sobriencmd_sel 18179968Sobrien : cmd 18279968Sobrien { 183133936Sobrien REASSIGN(fromname, NULL); 18479968Sobrien restart_point = (off_t) 0; 18579968Sobrien } 18679968Sobrien 18779968Sobrien | rcmd 18879968Sobrien 18979968Sobrien ; 19079968Sobrien 19179968Sobriencmd 19279968Sobrien /* RFC 959 */ 19379968Sobrien : USER SP username CRLF 19479968Sobrien { 19579968Sobrien user($3); 19679968Sobrien free($3); 19779968Sobrien } 19879968Sobrien 19979968Sobrien | PASS SP password CRLF 20079968Sobrien { 20179968Sobrien pass($3); 20279968Sobrien memset($3, 0, strlen($3)); 20379968Sobrien free($3); 20479968Sobrien } 20579968Sobrien 20679968Sobrien | CWD check_login CRLF 20779968Sobrien { 20879968Sobrien if ($2) 20979968Sobrien cwd(homedir); 21079968Sobrien } 21179968Sobrien 21279968Sobrien | CWD check_login SP pathname CRLF 21379968Sobrien { 21479968Sobrien if ($2 && $4 != NULL) 21579968Sobrien cwd($4); 21679968Sobrien if ($4 != NULL) 21779968Sobrien free($4); 21879968Sobrien } 21979968Sobrien 22079968Sobrien | CDUP check_login CRLF 22179968Sobrien { 22279968Sobrien if ($2) 22379968Sobrien cwd(".."); 22479968Sobrien } 22579968Sobrien 22679968Sobrien | QUIT CRLF 22779968Sobrien { 22879968Sobrien if (logged_in) { 22979968Sobrien reply(-221, "%s", ""); 23079968Sobrien reply(0, 23179968Sobrien "Data traffic for this session was " LLF " byte%s in " LLF " file%s.", 23279968Sobrien (LLT)total_data, PLURAL(total_data), 23379968Sobrien (LLT)total_files, PLURAL(total_files)); 23479968Sobrien reply(0, 23579968Sobrien "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.", 23679968Sobrien (LLT)total_bytes, PLURAL(total_bytes), 23779968Sobrien (LLT)total_xfers, PLURAL(total_xfers)); 23879968Sobrien } 23979968Sobrien reply(221, 24079968Sobrien "Thank you for using the FTP service on %s.", 24179968Sobrien hostname); 24279968Sobrien if (logged_in && logging) { 24379968Sobrien syslog(LOG_INFO, 24479968Sobrien "Data traffic: " LLF " byte%s in " LLF " file%s", 24579968Sobrien (LLT)total_data, PLURAL(total_data), 24679968Sobrien (LLT)total_files, PLURAL(total_files)); 24779968Sobrien syslog(LOG_INFO, 24879968Sobrien "Total traffic: " LLF " byte%s in " LLF " transfer%s", 24979968Sobrien (LLT)total_bytes, PLURAL(total_bytes), 25079968Sobrien (LLT)total_xfers, PLURAL(total_xfers)); 25179968Sobrien } 25279968Sobrien 25379968Sobrien dologout(0); 25479968Sobrien } 25579968Sobrien 25679968Sobrien | PORT check_login SP host_port CRLF 25779968Sobrien { 25879968Sobrien if ($2) 25979968Sobrien port_check("PORT", AF_INET); 26079968Sobrien } 26179968Sobrien 26279968Sobrien | LPRT check_login SP host_long_port4 CRLF 26379968Sobrien { 26479968Sobrien if ($2) 26579968Sobrien port_check("LPRT", AF_INET); 26679968Sobrien } 26779968Sobrien 26879968Sobrien | LPRT check_login SP host_long_port6 CRLF 26979968Sobrien { 27079968Sobrien#ifdef INET6 27179968Sobrien if ($2) 27279968Sobrien port_check("LPRT", AF_INET6); 27379968Sobrien#else 27479968Sobrien reply(500, "IPv6 support not available."); 27579968Sobrien#endif 27679968Sobrien } 27779968Sobrien 27879968Sobrien | EPRT check_login SP STRING CRLF 27979968Sobrien { 28079968Sobrien if ($2) { 28179968Sobrien if (extended_port($4) == 0) 28279968Sobrien port_check("EPRT", -1); 28379968Sobrien } 28479968Sobrien free($4); 28579968Sobrien } 28679968Sobrien 28779968Sobrien | PASV check_login CRLF 28879968Sobrien { 28979968Sobrien if ($2) { 29079968Sobrien if (CURCLASS_FLAGS_ISSET(passive)) 29179968Sobrien passive(); 29279968Sobrien else 29379968Sobrien reply(500, "PASV mode not available."); 29479968Sobrien } 29579968Sobrien } 29679968Sobrien 29779968Sobrien | LPSV check_login CRLF 29879968Sobrien { 29979968Sobrien if ($2) { 300108746Sobrien if (CURCLASS_FLAGS_ISSET(passive)) { 301108746Sobrien if (epsvall) 302108746Sobrien reply(501, 303108746Sobrien "LPSV disallowed after EPSV ALL"); 304108746Sobrien else 305108746Sobrien long_passive("LPSV", PF_UNSPEC); 306108746Sobrien } else 307108746Sobrien reply(500, "LPSV mode not available."); 30879968Sobrien } 30979968Sobrien } 31079968Sobrien 31179968Sobrien | EPSV check_login SP NUMBER CRLF 31279968Sobrien { 313108746Sobrien if ($2) { 314108746Sobrien if (CURCLASS_FLAGS_ISSET(passive)) 315108746Sobrien long_passive("EPSV", 316108746Sobrien epsvproto2af($4.i)); 317108746Sobrien else 318108746Sobrien reply(500, "EPSV mode not available."); 319108746Sobrien } 32079968Sobrien } 32179968Sobrien 32279968Sobrien | EPSV check_login SP ALL CRLF 32379968Sobrien { 32479968Sobrien if ($2) { 325108746Sobrien if (CURCLASS_FLAGS_ISSET(passive)) { 326108746Sobrien reply(200, 327108746Sobrien "EPSV ALL command successful."); 328108746Sobrien epsvall++; 329108746Sobrien } else 330108746Sobrien reply(500, "EPSV mode not available."); 33179968Sobrien } 33279968Sobrien } 33379968Sobrien 33479968Sobrien | EPSV check_login CRLF 33579968Sobrien { 336108746Sobrien if ($2) { 337108746Sobrien if (CURCLASS_FLAGS_ISSET(passive)) 338108746Sobrien long_passive("EPSV", PF_UNSPEC); 339108746Sobrien else 340108746Sobrien reply(500, "EPSV mode not available."); 341108746Sobrien } 34279968Sobrien } 34379968Sobrien 34479968Sobrien | TYPE check_login SP type_code CRLF 34579968Sobrien { 34679968Sobrien if ($2) { 34779968Sobrien 34879968Sobrien switch (cmd_type) { 34979968Sobrien 35079968Sobrien case TYPE_A: 35179968Sobrien if (cmd_form == FORM_N) { 35279968Sobrien reply(200, "Type set to A."); 35379968Sobrien type = cmd_type; 35479968Sobrien form = cmd_form; 35579968Sobrien } else 35679968Sobrien reply(504, "Form must be N."); 35779968Sobrien break; 35879968Sobrien 35979968Sobrien case TYPE_E: 36079968Sobrien reply(504, "Type E not implemented."); 36179968Sobrien break; 36279968Sobrien 36379968Sobrien case TYPE_I: 36479968Sobrien reply(200, "Type set to I."); 36579968Sobrien type = cmd_type; 36679968Sobrien break; 36779968Sobrien 36879968Sobrien case TYPE_L: 36979968Sobrien#if NBBY == 8 37079968Sobrien if (cmd_bytesz == 8) { 37179968Sobrien reply(200, 37279968Sobrien "Type set to L (byte size 8)."); 37379968Sobrien type = cmd_type; 37479968Sobrien } else 37579968Sobrien reply(504, "Byte size must be 8."); 37679968Sobrien#else /* NBBY == 8 */ 37779968Sobrien UNIMPLEMENTED for NBBY != 8 37879968Sobrien#endif /* NBBY == 8 */ 37979968Sobrien } 38079968Sobrien 38179968Sobrien } 38279968Sobrien } 38379968Sobrien 38479968Sobrien | STRU check_login SP struct_code CRLF 38579968Sobrien { 38679968Sobrien if ($2) { 38779968Sobrien switch ($4) { 38879968Sobrien 38979968Sobrien case STRU_F: 39079968Sobrien reply(200, "STRU F ok."); 39179968Sobrien break; 39279968Sobrien 39379968Sobrien default: 39479968Sobrien reply(504, "Unimplemented STRU type."); 39579968Sobrien } 39679968Sobrien } 39779968Sobrien } 39879968Sobrien 39979968Sobrien | MODE check_login SP mode_code CRLF 40079968Sobrien { 40179968Sobrien if ($2) { 40279968Sobrien switch ($4) { 40379968Sobrien 40479968Sobrien case MODE_S: 40579968Sobrien reply(200, "MODE S ok."); 40679968Sobrien break; 40779968Sobrien 40879968Sobrien default: 40979968Sobrien reply(502, "Unimplemented MODE type."); 41079968Sobrien } 41179968Sobrien } 41279968Sobrien } 41379968Sobrien 41479968Sobrien | RETR check_login SP pathname CRLF 41579968Sobrien { 41679968Sobrien if ($2 && $4 != NULL) 41779968Sobrien retrieve(NULL, $4); 41879968Sobrien if ($4 != NULL) 41979968Sobrien free($4); 42079968Sobrien } 42179968Sobrien 42279968Sobrien | STOR SP pathname CRLF 42379968Sobrien { 42479968Sobrien if (check_write($3, 1)) 42579968Sobrien store($3, "w", 0); 42679968Sobrien if ($3 != NULL) 42779968Sobrien free($3); 42879968Sobrien } 42979968Sobrien 43079968Sobrien | STOU SP pathname CRLF 43179968Sobrien { 43279968Sobrien if (check_write($3, 1)) 43379968Sobrien store($3, "w", 1); 43479968Sobrien if ($3 != NULL) 43579968Sobrien free($3); 43679968Sobrien } 43779968Sobrien 43879968Sobrien | APPE SP pathname CRLF 43979968Sobrien { 44079968Sobrien if (check_write($3, 1)) 44179968Sobrien store($3, "a", 0); 44279968Sobrien if ($3 != NULL) 44379968Sobrien free($3); 44479968Sobrien } 44579968Sobrien 44679968Sobrien | ALLO check_login SP NUMBER CRLF 44779968Sobrien { 44879968Sobrien if ($2) 44979968Sobrien reply(202, "ALLO command ignored."); 45079968Sobrien } 45179968Sobrien 45279968Sobrien | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 45379968Sobrien { 45479968Sobrien if ($2) 45579968Sobrien reply(202, "ALLO command ignored."); 45679968Sobrien } 45779968Sobrien 45879968Sobrien | RNTO SP pathname CRLF 45979968Sobrien { 46079968Sobrien if (check_write($3, 0)) { 46179968Sobrien if (fromname) { 46279968Sobrien renamecmd(fromname, $3); 463133936Sobrien REASSIGN(fromname, NULL); 46479968Sobrien } else { 46579968Sobrien reply(503, "Bad sequence of commands."); 46679968Sobrien } 46779968Sobrien } 46879968Sobrien if ($3 != NULL) 46979968Sobrien free($3); 47079968Sobrien } 47179968Sobrien 47279968Sobrien | ABOR check_login CRLF 47379968Sobrien { 47479968Sobrien if (is_oob) 47579968Sobrien abor(); 47679968Sobrien else if ($2) 47779968Sobrien reply(225, "ABOR command successful."); 47879968Sobrien } 47979968Sobrien 48079968Sobrien | DELE SP pathname CRLF 48179968Sobrien { 48279968Sobrien if (check_write($3, 0)) 48379968Sobrien delete($3); 48479968Sobrien if ($3 != NULL) 48579968Sobrien free($3); 48679968Sobrien } 48779968Sobrien 48879968Sobrien | RMD SP pathname CRLF 48979968Sobrien { 49079968Sobrien if (check_write($3, 0)) 49179968Sobrien removedir($3); 49279968Sobrien if ($3 != NULL) 49379968Sobrien free($3); 49479968Sobrien } 49579968Sobrien 49679968Sobrien | MKD SP pathname CRLF 49779968Sobrien { 49879968Sobrien if (check_write($3, 0)) 49979968Sobrien makedir($3); 50079968Sobrien if ($3 != NULL) 50179968Sobrien free($3); 50279968Sobrien } 50379968Sobrien 50479968Sobrien | PWD check_login CRLF 50579968Sobrien { 50679968Sobrien if ($2) 50779968Sobrien pwd(); 50879968Sobrien } 50979968Sobrien 51079968Sobrien | LIST check_login CRLF 51179968Sobrien { 51279968Sobrien char *argv[] = { INTERNAL_LS, "-lgA", NULL }; 51379968Sobrien 514161764Sobrien if (CURCLASS_FLAGS_ISSET(hidesymlinks)) 515161764Sobrien argv[1] = "-LlgA"; 51679968Sobrien if ($2) 51779968Sobrien retrieve(argv, ""); 51879968Sobrien } 51979968Sobrien 52079968Sobrien | LIST check_login SP pathname CRLF 52179968Sobrien { 52279968Sobrien char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL }; 52379968Sobrien 524161764Sobrien if (CURCLASS_FLAGS_ISSET(hidesymlinks)) 525161764Sobrien argv[1] = "-LlgA"; 52679968Sobrien if ($2 && $4 != NULL) { 52779968Sobrien argv[2] = $4; 52879968Sobrien retrieve(argv, $4); 52979968Sobrien } 53079968Sobrien if ($4 != NULL) 53179968Sobrien free($4); 53279968Sobrien } 53379968Sobrien 53479968Sobrien | NLST check_login CRLF 53579968Sobrien { 53679968Sobrien if ($2) 53779968Sobrien send_file_list("."); 53879968Sobrien } 53979968Sobrien 54079968Sobrien | NLST check_login SP pathname CRLF 54179968Sobrien { 54279968Sobrien if ($2) 54379968Sobrien send_file_list($4); 54479968Sobrien free($4); 54579968Sobrien } 54679968Sobrien 54779968Sobrien | SITE SP HELP CRLF 54879968Sobrien { 54979968Sobrien help(sitetab, NULL); 55079968Sobrien } 55179968Sobrien 55279968Sobrien | SITE SP CHMOD SP octal_number SP pathname CRLF 55379968Sobrien { 55479968Sobrien if (check_write($7, 0)) { 555133936Sobrien if (($5 == -1) || ($5 > 0777)) 55679968Sobrien reply(501, 55779968Sobrien "CHMOD: Mode value must be between 0 and 0777"); 55879968Sobrien else if (chmod($7, $5) < 0) 55979968Sobrien perror_reply(550, $7); 56079968Sobrien else 56179968Sobrien reply(200, "CHMOD command successful."); 56279968Sobrien } 56379968Sobrien if ($7 != NULL) 56479968Sobrien free($7); 56579968Sobrien } 56679968Sobrien 56779968Sobrien | SITE SP HELP SP STRING CRLF 56879968Sobrien { 56979968Sobrien help(sitetab, $5); 57079968Sobrien free($5); 57179968Sobrien } 57279968Sobrien 57379968Sobrien | SITE SP IDLE check_login CRLF 57479968Sobrien { 57579968Sobrien if ($4) { 57679968Sobrien reply(200, 577108746Sobrien "Current IDLE time limit is " LLF 578108746Sobrien " seconds; max " LLF, 579108746Sobrien (LLT)curclass.timeout, 580108746Sobrien (LLT)curclass.maxtimeout); 58179968Sobrien } 58279968Sobrien } 58379968Sobrien 58479968Sobrien | SITE SP IDLE check_login SP NUMBER CRLF 58579968Sobrien { 58679968Sobrien if ($4) { 587108746Sobrien if ($6.i < 30 || $6.i > curclass.maxtimeout) { 58879968Sobrien reply(501, 589108746Sobrien "IDLE time limit must be between 30 and " 590108746Sobrien LLF " seconds", 591108746Sobrien (LLT)curclass.maxtimeout); 59279968Sobrien } else { 593108746Sobrien curclass.timeout = $6.i; 59479968Sobrien (void) alarm(curclass.timeout); 59579968Sobrien reply(200, 596108746Sobrien "IDLE time limit set to " 597108746Sobrien LLF " seconds", 598108746Sobrien (LLT)curclass.timeout); 59979968Sobrien } 60079968Sobrien } 60179968Sobrien } 60279968Sobrien 60379968Sobrien | SITE SP RATEGET check_login CRLF 60479968Sobrien { 60579968Sobrien if ($4) { 60679968Sobrien reply(200, 60779968Sobrien "Current RATEGET is " LLF " bytes/sec", 60879968Sobrien (LLT)curclass.rateget); 60979968Sobrien } 61079968Sobrien } 61179968Sobrien 61279968Sobrien | SITE SP RATEGET check_login SP STRING CRLF 61379968Sobrien { 614108746Sobrien char errbuf[100]; 61579968Sobrien char *p = $6; 61679968Sobrien LLT rate; 61779968Sobrien 61879968Sobrien if ($4) { 619108746Sobrien rate = strsuftollx("RATEGET", p, 0, 620108746Sobrien curclass.maxrateget 621108746Sobrien ? curclass.maxrateget 622108746Sobrien : LLTMAX, errbuf, sizeof(errbuf)); 623108746Sobrien if (errbuf[0]) 624108746Sobrien reply(501, "%s", errbuf); 62579968Sobrien else { 62679968Sobrien curclass.rateget = rate; 62779968Sobrien reply(200, 62879968Sobrien "RATEGET set to " LLF " bytes/sec", 62979968Sobrien (LLT)curclass.rateget); 63079968Sobrien } 63179968Sobrien } 63279968Sobrien free($6); 63379968Sobrien } 63479968Sobrien 63579968Sobrien | SITE SP RATEPUT check_login CRLF 63679968Sobrien { 63779968Sobrien if ($4) { 63879968Sobrien reply(200, 63979968Sobrien "Current RATEPUT is " LLF " bytes/sec", 64079968Sobrien (LLT)curclass.rateput); 64179968Sobrien } 64279968Sobrien } 64379968Sobrien 64479968Sobrien | SITE SP RATEPUT check_login SP STRING CRLF 64579968Sobrien { 646108746Sobrien char errbuf[100]; 64779968Sobrien char *p = $6; 64879968Sobrien LLT rate; 64979968Sobrien 65079968Sobrien if ($4) { 651108746Sobrien rate = strsuftollx("RATEPUT", p, 0, 652108746Sobrien curclass.maxrateput 653108746Sobrien ? curclass.maxrateput 654108746Sobrien : LLTMAX, errbuf, sizeof(errbuf)); 655108746Sobrien if (errbuf[0]) 656108746Sobrien reply(501, "%s", errbuf); 65779968Sobrien else { 65879968Sobrien curclass.rateput = rate; 65979968Sobrien reply(200, 66079968Sobrien "RATEPUT set to " LLF " bytes/sec", 66179968Sobrien (LLT)curclass.rateput); 66279968Sobrien } 66379968Sobrien } 66479968Sobrien free($6); 66579968Sobrien } 66679968Sobrien 66779968Sobrien | SITE SP UMASK check_login CRLF 66879968Sobrien { 66979968Sobrien int oldmask; 67079968Sobrien 67179968Sobrien if ($4) { 67279968Sobrien oldmask = umask(0); 67379968Sobrien (void) umask(oldmask); 67479968Sobrien reply(200, "Current UMASK is %03o", oldmask); 67579968Sobrien } 67679968Sobrien } 67779968Sobrien 67879968Sobrien | SITE SP UMASK check_login SP octal_number CRLF 67979968Sobrien { 68079968Sobrien int oldmask; 68179968Sobrien 682108746Sobrien if ($4 && check_write("", 0)) { 68379968Sobrien if (($6 == -1) || ($6 > 0777)) { 68479968Sobrien reply(501, "Bad UMASK value"); 68579968Sobrien } else { 68679968Sobrien oldmask = umask($6); 68779968Sobrien reply(200, 68879968Sobrien "UMASK set to %03o (was %03o)", 68979968Sobrien $6, oldmask); 69079968Sobrien } 69179968Sobrien } 69279968Sobrien } 69379968Sobrien 69479968Sobrien | SYST CRLF 69579968Sobrien { 69679968Sobrien if (EMPTYSTR(version)) 69779968Sobrien reply(215, "UNIX Type: L%d", NBBY); 69879968Sobrien else 69979968Sobrien reply(215, "UNIX Type: L%d Version: %s", NBBY, 70079968Sobrien version); 70179968Sobrien } 70279968Sobrien 70379968Sobrien | STAT check_login SP pathname CRLF 70479968Sobrien { 70579968Sobrien if ($2 && $4 != NULL) 70679968Sobrien statfilecmd($4); 70779968Sobrien if ($4 != NULL) 70879968Sobrien free($4); 70979968Sobrien } 71079968Sobrien 71179968Sobrien | STAT CRLF 71279968Sobrien { 71379968Sobrien if (is_oob) 71479968Sobrien statxfer(); 71579968Sobrien else 71679968Sobrien statcmd(); 71779968Sobrien } 71879968Sobrien 71979968Sobrien | HELP CRLF 72079968Sobrien { 72179968Sobrien help(cmdtab, NULL); 72279968Sobrien } 72379968Sobrien 72479968Sobrien | HELP SP STRING CRLF 72579968Sobrien { 72679968Sobrien char *cp = $3; 72779968Sobrien 72879968Sobrien if (strncasecmp(cp, "SITE", 4) == 0) { 72979968Sobrien cp = $3 + 4; 73079968Sobrien if (*cp == ' ') 73179968Sobrien cp++; 73279968Sobrien if (*cp) 73379968Sobrien help(sitetab, cp); 73479968Sobrien else 73579968Sobrien help(sitetab, NULL); 73679968Sobrien } else 73779968Sobrien help(cmdtab, $3); 73879968Sobrien free($3); 73979968Sobrien } 74079968Sobrien 74179968Sobrien | NOOP CRLF 74279968Sobrien { 74379968Sobrien reply(200, "NOOP command successful."); 74479968Sobrien } 74579968Sobrien 74679968Sobrien /* RFC 2228 */ 74779968Sobrien | AUTH SP mechanism_name CRLF 74879968Sobrien { 74979968Sobrien reply(502, "RFC 2228 authentication not implemented."); 75079968Sobrien free($3); 75179968Sobrien } 75279968Sobrien 75379968Sobrien | ADAT SP base64data CRLF 75479968Sobrien { 75579968Sobrien reply(503, 75679968Sobrien "Please set authentication state with AUTH."); 75779968Sobrien free($3); 75879968Sobrien } 75979968Sobrien 76079968Sobrien | PROT SP prot_code CRLF 76179968Sobrien { 76279968Sobrien reply(503, 76379968Sobrien "Please set protection buffer size with PBSZ."); 76479968Sobrien free($3); 76579968Sobrien } 76679968Sobrien 76779968Sobrien | PBSZ SP decimal_integer CRLF 76879968Sobrien { 76979968Sobrien reply(503, 77079968Sobrien "Please set authentication state with AUTH."); 77179968Sobrien } 77279968Sobrien 77379968Sobrien | CCC CRLF 77479968Sobrien { 77579968Sobrien reply(533, "No protection enabled."); 77679968Sobrien } 77779968Sobrien 77879968Sobrien | MIC SP base64data CRLF 77979968Sobrien { 78079968Sobrien reply(502, "RFC 2228 authentication not implemented."); 78179968Sobrien free($3); 78279968Sobrien } 78379968Sobrien 78479968Sobrien | CONF SP base64data CRLF 78579968Sobrien { 78679968Sobrien reply(502, "RFC 2228 authentication not implemented."); 78779968Sobrien free($3); 78879968Sobrien } 78979968Sobrien 79079968Sobrien | ENC SP base64data CRLF 79179968Sobrien { 79279968Sobrien reply(502, "RFC 2228 authentication not implemented."); 79379968Sobrien free($3); 79479968Sobrien } 79579968Sobrien 79679968Sobrien /* RFC 2389 */ 79779968Sobrien | FEAT CRLF 79879968Sobrien { 79979968Sobrien 80079968Sobrien feat(); 80179968Sobrien } 80279968Sobrien 80379968Sobrien | OPTS SP STRING CRLF 80479968Sobrien { 80579968Sobrien 80679968Sobrien opts($3); 80779968Sobrien free($3); 80879968Sobrien } 80979968Sobrien 81079968Sobrien 81179968Sobrien /* extensions from draft-ietf-ftpext-mlst-11 */ 81279968Sobrien 81379968Sobrien /* 81479968Sobrien * Return size of file in a format suitable for 81579968Sobrien * using with RESTART (we just count bytes). 81679968Sobrien */ 81779968Sobrien | SIZE check_login SP pathname CRLF 81879968Sobrien { 81979968Sobrien if ($2 && $4 != NULL) 82079968Sobrien sizecmd($4); 82179968Sobrien if ($4 != NULL) 82279968Sobrien free($4); 82379968Sobrien } 82479968Sobrien 82579968Sobrien /* 82679968Sobrien * Return modification time of file as an ISO 3307 82779968Sobrien * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 82879968Sobrien * where xxx is the fractional second (of any precision, 82979968Sobrien * not necessarily 3 digits) 83079968Sobrien */ 83179968Sobrien | MDTM check_login SP pathname CRLF 83279968Sobrien { 83379968Sobrien if ($2 && $4 != NULL) { 83479968Sobrien struct stat stbuf; 83579968Sobrien if (stat($4, &stbuf) < 0) 83679968Sobrien perror_reply(550, $4); 83779968Sobrien else if (!S_ISREG(stbuf.st_mode)) { 83879968Sobrien reply(550, "%s: not a plain file.", $4); 83979968Sobrien } else { 84079968Sobrien struct tm *t; 84179968Sobrien 84279968Sobrien t = gmtime(&stbuf.st_mtime); 84379968Sobrien reply(213, 84479968Sobrien "%04d%02d%02d%02d%02d%02d", 84579968Sobrien TM_YEAR_BASE + t->tm_year, 84679968Sobrien t->tm_mon+1, t->tm_mday, 84779968Sobrien t->tm_hour, t->tm_min, t->tm_sec); 84879968Sobrien } 84979968Sobrien } 85079968Sobrien if ($4 != NULL) 85179968Sobrien free($4); 85279968Sobrien } 85379968Sobrien 85479968Sobrien | MLST check_login SP pathname CRLF 85579968Sobrien { 85679968Sobrien if ($2 && $4 != NULL) 85779968Sobrien mlst($4); 85879968Sobrien if ($4 != NULL) 85979968Sobrien free($4); 86079968Sobrien } 86179968Sobrien 86279968Sobrien | MLST check_login CRLF 86379968Sobrien { 86479968Sobrien mlst(NULL); 86579968Sobrien } 86679968Sobrien 86779968Sobrien | MLSD check_login SP pathname CRLF 86879968Sobrien { 86979968Sobrien if ($2 && $4 != NULL) 87079968Sobrien mlsd($4); 87179968Sobrien if ($4 != NULL) 87279968Sobrien free($4); 87379968Sobrien } 87479968Sobrien 87579968Sobrien | MLSD check_login CRLF 87679968Sobrien { 87779968Sobrien mlsd(NULL); 87879968Sobrien } 87979968Sobrien 88079968Sobrien | error CRLF 88179968Sobrien { 88279968Sobrien yyerrok; 88379968Sobrien } 88479968Sobrien ; 88579968Sobrien 88679968Sobrienrcmd 887108746Sobrien : REST check_login SP NUMBER CRLF 88879968Sobrien { 88979968Sobrien if ($2) { 890133936Sobrien REASSIGN(fromname, NULL); 891133936Sobrien restart_point = (off_t)$4.ll; 89279968Sobrien reply(350, 89379968Sobrien "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.", 89479968Sobrien (LLT)restart_point); 89579968Sobrien } 89679968Sobrien } 89779968Sobrien 89879968Sobrien | RNFR SP pathname CRLF 89979968Sobrien { 90079968Sobrien restart_point = (off_t) 0; 901133936Sobrien if (check_write($3, 0)) { 902133936Sobrien REASSIGN(fromname, NULL); 90379968Sobrien fromname = renamefrom($3); 904133936Sobrien } 90579968Sobrien if ($3 != NULL) 90679968Sobrien free($3); 90779968Sobrien } 90879968Sobrien ; 90979968Sobrien 91079968Sobrienusername 91179968Sobrien : STRING 91279968Sobrien ; 91379968Sobrien 91479968Sobrienpassword 91579968Sobrien : /* empty */ 91679968Sobrien { 91779968Sobrien $$ = (char *)calloc(1, sizeof(char)); 91879968Sobrien } 91979968Sobrien 92079968Sobrien | STRING 92179968Sobrien ; 92279968Sobrien 92379968Sobrienbyte_size 92479968Sobrien : NUMBER 925108746Sobrien { 926108746Sobrien $$ = $1.i; 927108746Sobrien } 92879968Sobrien ; 92979968Sobrien 93079968Sobrienhost_port 93179968Sobrien : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 93279968Sobrien NUMBER COMMA NUMBER 93379968Sobrien { 93479968Sobrien char *a, *p; 93579968Sobrien 93679968Sobrien memset(&data_dest, 0, sizeof(data_dest)); 93779968Sobrien data_dest.su_len = sizeof(struct sockaddr_in); 93879968Sobrien data_dest.su_family = AF_INET; 93979968Sobrien p = (char *)&data_dest.su_port; 940108746Sobrien p[0] = $9.i; p[1] = $11.i; 94179968Sobrien a = (char *)&data_dest.su_addr; 942108746Sobrien a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; 94379968Sobrien } 94479968Sobrien ; 94579968Sobrien 94679968Sobrienhost_long_port4 94779968Sobrien : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 94879968Sobrien NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 94979968Sobrien NUMBER 95079968Sobrien { 95179968Sobrien char *a, *p; 95279968Sobrien 95379968Sobrien memset(&data_dest, 0, sizeof(data_dest)); 95479968Sobrien data_dest.su_len = sizeof(struct sockaddr_in); 95579968Sobrien data_dest.su_family = AF_INET; 95679968Sobrien p = (char *)&data_dest.su_port; 957108746Sobrien p[0] = $15.i; p[1] = $17.i; 95879968Sobrien a = (char *)&data_dest.su_addr; 959108746Sobrien a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 96079968Sobrien 96179968Sobrien /* reject invalid LPRT command */ 962108746Sobrien if ($1.i != 4 || $3.i != 4 || $13.i != 2) 96379968Sobrien memset(&data_dest, 0, sizeof(data_dest)); 96479968Sobrien } 96579968Sobrien ; 96679968Sobrien 96779968Sobrienhost_long_port6 96879968Sobrien : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 96979968Sobrien NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 97079968Sobrien NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 97179968Sobrien NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 97279968Sobrien NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 97379968Sobrien NUMBER 97479968Sobrien { 97579968Sobrien#ifdef INET6 97679968Sobrien char *a, *p; 97779968Sobrien 97879968Sobrien memset(&data_dest, 0, sizeof(data_dest)); 97979968Sobrien data_dest.su_len = sizeof(struct sockaddr_in6); 98079968Sobrien data_dest.su_family = AF_INET6; 98179968Sobrien p = (char *)&data_dest.su_port; 982108746Sobrien p[0] = $39.i; p[1] = $41.i; 98379968Sobrien a = (char *)&data_dest.si_su.su_sin6.sin6_addr; 984108746Sobrien a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 985108746Sobrien a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i; 986108746Sobrien a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i; 987108746Sobrien a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i; 98879968Sobrien if (his_addr.su_family == AF_INET6) { 98979968Sobrien /* XXX: more sanity checks! */ 99079968Sobrien data_dest.su_scope_id = his_addr.su_scope_id; 99179968Sobrien } 99279968Sobrien#else 99379968Sobrien memset(&data_dest, 0, sizeof(data_dest)); 99479968Sobrien#endif /* INET6 */ 99579968Sobrien /* reject invalid LPRT command */ 996133936Sobrien if ($1.i != 6 || $3.i != 16 || $37.i != 2) 99779968Sobrien memset(&data_dest, 0, sizeof(data_dest)); 99879968Sobrien } 99979968Sobrien ; 100079968Sobrien 100179968Sobrienform_code 100279968Sobrien : N 100379968Sobrien { 100479968Sobrien $$ = FORM_N; 100579968Sobrien } 100679968Sobrien 100779968Sobrien | T 100879968Sobrien { 100979968Sobrien $$ = FORM_T; 101079968Sobrien } 101179968Sobrien 101279968Sobrien | C 101379968Sobrien { 101479968Sobrien $$ = FORM_C; 101579968Sobrien } 101679968Sobrien ; 101779968Sobrien 101879968Sobrientype_code 101979968Sobrien : A 102079968Sobrien { 102179968Sobrien cmd_type = TYPE_A; 102279968Sobrien cmd_form = FORM_N; 102379968Sobrien } 102479968Sobrien 102579968Sobrien | A SP form_code 102679968Sobrien { 102779968Sobrien cmd_type = TYPE_A; 102879968Sobrien cmd_form = $3; 102979968Sobrien } 103079968Sobrien 103179968Sobrien | E 103279968Sobrien { 103379968Sobrien cmd_type = TYPE_E; 103479968Sobrien cmd_form = FORM_N; 103579968Sobrien } 103679968Sobrien 103779968Sobrien | E SP form_code 103879968Sobrien { 103979968Sobrien cmd_type = TYPE_E; 104079968Sobrien cmd_form = $3; 104179968Sobrien } 104279968Sobrien 104379968Sobrien | I 104479968Sobrien { 104579968Sobrien cmd_type = TYPE_I; 104679968Sobrien } 104779968Sobrien 104879968Sobrien | L 104979968Sobrien { 105079968Sobrien cmd_type = TYPE_L; 105179968Sobrien cmd_bytesz = NBBY; 105279968Sobrien } 105379968Sobrien 105479968Sobrien | L SP byte_size 105579968Sobrien { 105679968Sobrien cmd_type = TYPE_L; 105779968Sobrien cmd_bytesz = $3; 105879968Sobrien } 105979968Sobrien 106079968Sobrien /* this is for a bug in the BBN ftp */ 106179968Sobrien | L byte_size 106279968Sobrien { 106379968Sobrien cmd_type = TYPE_L; 106479968Sobrien cmd_bytesz = $2; 106579968Sobrien } 106679968Sobrien ; 106779968Sobrien 106879968Sobrienstruct_code 106979968Sobrien : F 107079968Sobrien { 107179968Sobrien $$ = STRU_F; 107279968Sobrien } 107379968Sobrien 107479968Sobrien | R 107579968Sobrien { 107679968Sobrien $$ = STRU_R; 107779968Sobrien } 107879968Sobrien 107979968Sobrien | P 108079968Sobrien { 108179968Sobrien $$ = STRU_P; 108279968Sobrien } 108379968Sobrien ; 108479968Sobrien 108579968Sobrienmode_code 108679968Sobrien : S 108779968Sobrien { 108879968Sobrien $$ = MODE_S; 108979968Sobrien } 109079968Sobrien 109179968Sobrien | B 109279968Sobrien { 109379968Sobrien $$ = MODE_B; 109479968Sobrien } 109579968Sobrien 109679968Sobrien | C 109779968Sobrien { 109879968Sobrien $$ = MODE_C; 109979968Sobrien } 110079968Sobrien ; 110179968Sobrien 110279968Sobrienpathname 110379968Sobrien : pathstring 110479968Sobrien { 110579968Sobrien /* 110679968Sobrien * Problem: this production is used for all pathname 110779968Sobrien * processing, but only gives a 550 error reply. 110879968Sobrien * This is a valid reply in some cases but not in 110979968Sobrien * others. 111079968Sobrien */ 111179968Sobrien if (logged_in && $1 && *$1 == '~') { 111279968Sobrien char *path, *home, *result; 111379968Sobrien size_t len; 111479968Sobrien 111579968Sobrien path = strchr($1 + 1, '/'); 111679968Sobrien if (path != NULL) 111779968Sobrien *path++ = '\0'; 111879968Sobrien if ($1[1] == '\0') 111979968Sobrien home = homedir; 112079968Sobrien else { 112192282Sobrien struct passwd *hpw; 112279968Sobrien 112392282Sobrien if ((hpw = getpwnam($1 + 1)) != NULL) 112492282Sobrien home = hpw->pw_dir; 112579968Sobrien else 112679968Sobrien home = $1; 112779968Sobrien } 112879968Sobrien len = strlen(home) + 1; 112979968Sobrien if (path != NULL) 113079968Sobrien len += strlen(path) + 1; 113179968Sobrien if ((result = malloc(len)) == NULL) 113279968Sobrien fatal("Local resource failure: malloc"); 113379968Sobrien strlcpy(result, home, len); 113479968Sobrien if (path != NULL) { 113579968Sobrien strlcat(result, "/", len); 113679968Sobrien strlcat(result, path, len); 113779968Sobrien } 113879968Sobrien $$ = result; 113979968Sobrien free($1); 114079968Sobrien } else 114179968Sobrien $$ = $1; 114279968Sobrien } 114379968Sobrien ; 114479968Sobrien 114579968Sobrienpathstring 114679968Sobrien : STRING 114779968Sobrien ; 114879968Sobrien 114979968Sobrienoctal_number 115079968Sobrien : NUMBER 115179968Sobrien { 115279968Sobrien int ret, dec, multby, digit; 115379968Sobrien 115479968Sobrien /* 115579968Sobrien * Convert a number that was read as decimal number 115679968Sobrien * to what it would be if it had been read as octal. 115779968Sobrien */ 1158108746Sobrien dec = $1.i; 115979968Sobrien multby = 1; 116079968Sobrien ret = 0; 116179968Sobrien while (dec) { 116279968Sobrien digit = dec%10; 116379968Sobrien if (digit > 7) { 116479968Sobrien ret = -1; 116579968Sobrien break; 116679968Sobrien } 116779968Sobrien ret += digit * multby; 116879968Sobrien multby *= 8; 116979968Sobrien dec /= 10; 117079968Sobrien } 117179968Sobrien $$ = ret; 117279968Sobrien } 117379968Sobrien ; 117479968Sobrien 117579968Sobrienmechanism_name 117679968Sobrien : STRING 117779968Sobrien ; 117879968Sobrien 117979968Sobrienbase64data 118079968Sobrien : STRING 118179968Sobrien ; 118279968Sobrien 118379968Sobrienprot_code 118479968Sobrien : STRING 118579968Sobrien ; 118679968Sobrien 118779968Sobriendecimal_integer 118879968Sobrien : NUMBER 1189108746Sobrien { 1190108746Sobrien $$ = $1.i; 1191108746Sobrien } 119279968Sobrien ; 119379968Sobrien 119479968Sobriencheck_login 119579968Sobrien : /* empty */ 119679968Sobrien { 119779968Sobrien if (logged_in) 119879968Sobrien $$ = 1; 119979968Sobrien else { 120079968Sobrien reply(530, "Please login with USER and PASS."); 120179968Sobrien $$ = 0; 120279968Sobrien hasyyerrored = 1; 120379968Sobrien } 120479968Sobrien } 120579968Sobrien ; 120679968Sobrien 120779968Sobrien%% 120879968Sobrien 120979968Sobrien#define CMD 0 /* beginning of command */ 121079968Sobrien#define ARGS 1 /* expect miscellaneous arguments */ 121179968Sobrien#define STR1 2 /* expect SP followed by STRING */ 121279968Sobrien#define STR2 3 /* expect STRING */ 121379968Sobrien#define OSTR 4 /* optional SP then STRING */ 121479968Sobrien#define ZSTR1 5 /* SP then optional STRING */ 121579968Sobrien#define ZSTR2 6 /* optional STRING after SP */ 121679968Sobrien#define SITECMD 7 /* SITE command */ 121779968Sobrien#define NSTR 8 /* Number followed by a string */ 121879968Sobrien#define NOARGS 9 /* No arguments allowed */ 121979968Sobrien#define EOLN 10 /* End of line */ 122079968Sobrien 122179968Sobrienstruct tab cmdtab[] = { 122279968Sobrien /* From RFC 959, in order defined (5.3.1) */ 122379968Sobrien { "USER", USER, STR1, 1, "<sp> username" }, 122479968Sobrien { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 122579968Sobrien { "ACCT", ACCT, STR1, 0, "(specify account)" }, 122679968Sobrien { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 122779968Sobrien { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)" }, 122879968Sobrien { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 122979968Sobrien { "QUIT", QUIT, NOARGS, 1, "(terminate service)" }, 123079968Sobrien { "REIN", REIN, NOARGS, 0, "(reinitialize server state)" }, 1231110242Sobrien { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" }, 123279968Sobrien { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 123379968Sobrien { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 123479968Sobrien { "PASV", PASV, NOARGS, 1, "(set server in passive mode)" }, 123579968Sobrien { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 123679968Sobrien { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 123779968Sobrien { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 123879968Sobrien { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 123979968Sobrien { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 124079968Sobrien { "RETR", RETR, STR1, 1, "<sp> file-name" }, 124179968Sobrien { "STOR", STOR, STR1, 1, "<sp> file-name" }, 124279968Sobrien { "STOU", STOU, STR1, 1, "<sp> file-name" }, 124379968Sobrien { "APPE", APPE, STR1, 1, "<sp> file-name" }, 124479968Sobrien { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 124579968Sobrien { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 124679968Sobrien { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 124779968Sobrien { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 124879968Sobrien { "ABOR", ABOR, NOARGS, 4, "(abort operation)" }, 124979968Sobrien { "DELE", DELE, STR1, 1, "<sp> file-name" }, 125079968Sobrien { "RMD", RMD, STR1, 1, "<sp> path-name" }, 125179968Sobrien { "MKD", MKD, STR1, 1, "<sp> path-name" }, 125279968Sobrien { "PWD", PWD, NOARGS, 1, "(return current directory)" }, 125379968Sobrien { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 125479968Sobrien { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 125579968Sobrien { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 125679968Sobrien { "SYST", SYST, NOARGS, 1, "(get type of operating system)" }, 125779968Sobrien { "STAT", STAT, OSTR, 4, "[ <sp> path-name ]" }, 125879968Sobrien { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 125979968Sobrien { "NOOP", NOOP, NOARGS, 2, "" }, 126079968Sobrien 126179968Sobrien /* From RFC 2228, in order defined */ 126279968Sobrien { "AUTH", AUTH, STR1, 1, "<sp> mechanism-name" }, 126379968Sobrien { "ADAT", ADAT, STR1, 1, "<sp> base-64-data" }, 126479968Sobrien { "PROT", PROT, STR1, 1, "<sp> prot-code" }, 126579968Sobrien { "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer" }, 126679968Sobrien { "CCC", CCC, NOARGS, 1, "(Disable data protection)" }, 126779968Sobrien { "MIC", MIC, STR1, 4, "<sp> base64data" }, 126879968Sobrien { "CONF", CONF, STR1, 4, "<sp> base64data" }, 126979968Sobrien { "ENC", ENC, STR1, 4, "<sp> base64data" }, 127079968Sobrien 127179968Sobrien /* From RFC 2389, in order defined */ 127279968Sobrien { "FEAT", FEAT, NOARGS, 1, "(display extended features)" }, 127379968Sobrien { "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]" }, 127479968Sobrien 127579968Sobrien /* from draft-ietf-ftpext-mlst-11 */ 127679968Sobrien { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 127779968Sobrien { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 127879968Sobrien { "MLST", MLST, OSTR, 2, "[ <sp> path-name ]" }, 127979968Sobrien { "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]" }, 128079968Sobrien 128179968Sobrien /* obsolete commands */ 128279968Sobrien { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 128379968Sobrien { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 128479968Sobrien { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 128579968Sobrien { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 128679968Sobrien { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 128779968Sobrien { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 128879968Sobrien { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 128979968Sobrien { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)" }, 129079968Sobrien { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 129179968Sobrien { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 129279968Sobrien { "XPWD", PWD, NOARGS, 1, "(return current directory)" }, 129379968Sobrien { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 129479968Sobrien 129579968Sobrien { NULL, 0, 0, 0, 0 } 129679968Sobrien}; 129779968Sobrien 129879968Sobrienstruct tab sitetab[] = { 1299108746Sobrien { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 1300108746Sobrien { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1301108746Sobrien { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 1302108746Sobrien { "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]" }, 1303108746Sobrien { "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]" }, 1304108746Sobrien { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 1305108746Sobrien { NULL, 0, 0, 0, NULL } 130679968Sobrien}; 130779968Sobrien 130879968Sobrien/* 130979968Sobrien * Check if a filename is allowed to be modified (isupload == 0) or 131079968Sobrien * uploaded (isupload == 1), and if necessary, check the filename is `sane'. 1311108746Sobrien * If the filename is NULL, fail. 1312108746Sobrien * If the filename is "", don't do the sane name check. 131379968Sobrien */ 131479968Sobrienstatic int 131579968Sobriencheck_write(const char *file, int isupload) 131679968Sobrien{ 131779968Sobrien if (file == NULL) 131879968Sobrien return (0); 131979968Sobrien if (! logged_in) { 132079968Sobrien reply(530, "Please login with USER and PASS."); 132179968Sobrien return (0); 132279968Sobrien } 132379968Sobrien /* checking modify */ 132479968Sobrien if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) { 132579968Sobrien reply(502, "No permission to use this command."); 132679968Sobrien return (0); 132779968Sobrien } 132879968Sobrien /* checking upload */ 132979968Sobrien if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) { 133079968Sobrien reply(502, "No permission to use this command."); 133179968Sobrien return (0); 133279968Sobrien } 1333108746Sobrien 133479968Sobrien /* checking sanenames */ 1335108746Sobrien if (file[0] != '\0' && CURCLASS_FLAGS_ISSET(sanenames)) { 133679968Sobrien const char *p; 133779968Sobrien 133879968Sobrien if (file[0] == '.') 133979968Sobrien goto insane_name; 134079968Sobrien for (p = file; *p; p++) { 1341161764Sobrien if (isalnum((unsigned char)*p) || *p == '-' || *p == '+' || 134279968Sobrien *p == ',' || *p == '.' || *p == '_') 134379968Sobrien continue; 134479968Sobrien insane_name: 134579968Sobrien reply(553, "File name `%s' not allowed.", file); 134679968Sobrien return (0); 134779968Sobrien } 134879968Sobrien } 134979968Sobrien return (1); 135079968Sobrien} 135179968Sobrien 135279968Sobrienstruct tab * 135379968Sobrienlookup(struct tab *p, const char *cmd) 135479968Sobrien{ 135579968Sobrien 135679968Sobrien for (; p->name != NULL; p++) 135779968Sobrien if (strcasecmp(cmd, p->name) == 0) 135879968Sobrien return (p); 135979968Sobrien return (0); 136079968Sobrien} 136179968Sobrien 136279968Sobrien#include <arpa/telnet.h> 136379968Sobrien 136479968Sobrien/* 136579968Sobrien * getline - a hacked up version of fgets to ignore TELNET escape codes. 1366186872Ssimon * `s' is the buffer to read into. 1367186872Ssimon * `n' is the 1 less than the size of the buffer, to allow trailing NUL 1368186872Ssimon * `iop' is the FILE to read from. 1369186872Ssimon * Returns 0 on success, -1 on EOF, -2 if the command was too long. 137079968Sobrien */ 1371186872Ssimonint 137279968Sobriengetline(char *s, int n, FILE *iop) 137379968Sobrien{ 137479968Sobrien int c; 137579968Sobrien char *cs; 137679968Sobrien 137779968Sobrien cs = s; 137879968Sobrien/* tmpline may contain saved command from urgent mode interruption */ 137979968Sobrien for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 138079968Sobrien *cs++ = tmpline[c]; 138179968Sobrien if (tmpline[c] == '\n') { 138279968Sobrien *cs++ = '\0'; 1383161764Sobrien if (ftpd_debug) 138479968Sobrien syslog(LOG_DEBUG, "command: %s", s); 138579968Sobrien tmpline[0] = '\0'; 1386186872Ssimon return(0); 138779968Sobrien } 138879968Sobrien if (c == 0) 138979968Sobrien tmpline[0] = '\0'; 139079968Sobrien } 139179968Sobrien while ((c = getc(iop)) != EOF) { 139279968Sobrien total_bytes++; 139379968Sobrien total_bytes_in++; 139479968Sobrien c &= 0377; 139579968Sobrien if (c == IAC) { 139679968Sobrien if ((c = getc(iop)) != EOF) { 139779968Sobrien total_bytes++; 139879968Sobrien total_bytes_in++; 139979968Sobrien c &= 0377; 140079968Sobrien switch (c) { 140179968Sobrien case WILL: 140279968Sobrien case WONT: 140379968Sobrien c = getc(iop); 140479968Sobrien total_bytes++; 140579968Sobrien total_bytes_in++; 140679968Sobrien cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c); 140779968Sobrien (void) fflush(stdout); 140879968Sobrien continue; 140979968Sobrien case DO: 141079968Sobrien case DONT: 141179968Sobrien c = getc(iop); 141279968Sobrien total_bytes++; 141379968Sobrien total_bytes_in++; 141479968Sobrien cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c); 141579968Sobrien (void) fflush(stdout); 141679968Sobrien continue; 141779968Sobrien case IAC: 141879968Sobrien break; 141979968Sobrien default: 142079968Sobrien continue; /* ignore command */ 142179968Sobrien } 142279968Sobrien } 142379968Sobrien } 142479968Sobrien *cs++ = c; 1425186872Ssimon if (--n <= 0) { 1426186872Ssimon /* 1427186872Ssimon * If command doesn't fit into buffer, discard the 1428186872Ssimon * rest of the command and indicate truncation. 1429186872Ssimon * This prevents the command to be split up into 1430186872Ssimon * multiple commands. 1431186872Ssimon */ 1432186872Ssimon if (ftpd_debug) 1433186872Ssimon syslog(LOG_DEBUG, 1434186872Ssimon "command too long, last char: %d", c); 1435186872Ssimon while (c != '\n' && (c = getc(iop)) != EOF) 1436186872Ssimon continue; 1437186872Ssimon return (-2); 1438186872Ssimon } 1439186872Ssimon if (c == '\n') 144079968Sobrien break; 144179968Sobrien } 144279968Sobrien if (c == EOF && cs == s) 1443186872Ssimon return (-1); 144479968Sobrien *cs++ = '\0'; 1445161764Sobrien if (ftpd_debug) { 144679968Sobrien if ((curclass.type != CLASS_GUEST && 144779968Sobrien strncasecmp(s, "PASS ", 5) == 0) || 144879968Sobrien strncasecmp(s, "ACCT ", 5) == 0) { 144979968Sobrien /* Don't syslog passwords */ 145079968Sobrien syslog(LOG_DEBUG, "command: %.4s ???", s); 145179968Sobrien } else { 145279968Sobrien char *cp; 145379968Sobrien int len; 145479968Sobrien 145579968Sobrien /* Don't syslog trailing CR-LF */ 145679968Sobrien len = strlen(s); 145779968Sobrien cp = s + len - 1; 145879968Sobrien while (cp >= s && (*cp == '\n' || *cp == '\r')) { 145979968Sobrien --cp; 146079968Sobrien --len; 146179968Sobrien } 146279968Sobrien syslog(LOG_DEBUG, "command: %.*s", len, s); 146379968Sobrien } 146479968Sobrien } 1465186872Ssimon return (0); 146679968Sobrien} 146779968Sobrien 146879968Sobrienvoid 146979968Sobrienftp_handle_line(char *cp) 147079968Sobrien{ 147179968Sobrien 147279968Sobrien cmdp = cp; 147379968Sobrien yyparse(); 147479968Sobrien} 147579968Sobrien 147679968Sobrienvoid 147779968Sobrienftp_loop(void) 147879968Sobrien{ 1479186872Ssimon int ret; 148079968Sobrien 148179968Sobrien while (1) { 148279968Sobrien (void) alarm(curclass.timeout); 1483186872Ssimon ret = getline(cbuf, sizeof(cbuf)-1, stdin); 1484186872Ssimon (void) alarm(0); 1485186872Ssimon if (ret == -1) { 148679968Sobrien reply(221, "You could at least say goodbye."); 148779968Sobrien dologout(0); 1488186872Ssimon } else if (ret == -2) { 1489186872Ssimon reply(500, "Command too long."); 1490186872Ssimon } else { 1491186872Ssimon ftp_handle_line(cbuf); 149279968Sobrien } 149379968Sobrien } 149479968Sobrien /*NOTREACHED*/ 149579968Sobrien} 149679968Sobrien 1497133936Sobrienint 149879968Sobrienyylex(void) 149979968Sobrien{ 150079968Sobrien static int cpos, state; 150179968Sobrien char *cp, *cp2; 150279968Sobrien struct tab *p; 150379968Sobrien int n; 150479968Sobrien char c; 150579968Sobrien 150679968Sobrien switch (state) { 150779968Sobrien 150879968Sobrien case CMD: 150979968Sobrien hasyyerrored = 0; 151079968Sobrien if ((cp = strchr(cmdp, '\r'))) { 151179968Sobrien *cp = '\0'; 151279968Sobrien#if HAVE_SETPROCTITLE 151379968Sobrien if (strncasecmp(cmdp, "PASS", 4) != 0 && 151479968Sobrien strncasecmp(cmdp, "ACCT", 4) != 0) 151579968Sobrien setproctitle("%s: %s", proctitle, cmdp); 151679968Sobrien#endif /* HAVE_SETPROCTITLE */ 151779968Sobrien *cp++ = '\n'; 151879968Sobrien *cp = '\0'; 151979968Sobrien } 152079968Sobrien if ((cp = strpbrk(cmdp, " \n"))) 152179968Sobrien cpos = cp - cmdp; 152279968Sobrien if (cpos == 0) 152379968Sobrien cpos = 4; 152479968Sobrien c = cmdp[cpos]; 152579968Sobrien cmdp[cpos] = '\0'; 152679968Sobrien p = lookup(cmdtab, cmdp); 152779968Sobrien cmdp[cpos] = c; 152879968Sobrien if (p != NULL) { 152979968Sobrien if (is_oob && ! CMD_OOB(p)) { 153079968Sobrien /* command will be handled in-band */ 153179968Sobrien return (0); 153279968Sobrien } else if (! CMD_IMPLEMENTED(p)) { 153379968Sobrien reply(502, "%s command not implemented.", 153479968Sobrien p->name); 153579968Sobrien hasyyerrored = 1; 153679968Sobrien break; 153779968Sobrien } 153879968Sobrien state = p->state; 153979968Sobrien yylval.s = p->name; 154079968Sobrien return (p->token); 154179968Sobrien } 154279968Sobrien break; 154379968Sobrien 154479968Sobrien case SITECMD: 154579968Sobrien if (cmdp[cpos] == ' ') { 154679968Sobrien cpos++; 154779968Sobrien return (SP); 154879968Sobrien } 154979968Sobrien cp = &cmdp[cpos]; 155079968Sobrien if ((cp2 = strpbrk(cp, " \n"))) 155179968Sobrien cpos = cp2 - cmdp; 155279968Sobrien c = cmdp[cpos]; 155379968Sobrien cmdp[cpos] = '\0'; 155479968Sobrien p = lookup(sitetab, cp); 155579968Sobrien cmdp[cpos] = c; 155679968Sobrien if (p != NULL) { 155779968Sobrien if (!CMD_IMPLEMENTED(p)) { 155879968Sobrien reply(502, "SITE %s command not implemented.", 155979968Sobrien p->name); 156079968Sobrien hasyyerrored = 1; 156179968Sobrien break; 156279968Sobrien } 156379968Sobrien state = p->state; 156479968Sobrien yylval.s = p->name; 156579968Sobrien return (p->token); 156679968Sobrien } 156779968Sobrien break; 156879968Sobrien 156979968Sobrien case OSTR: 157079968Sobrien if (cmdp[cpos] == '\n') { 157179968Sobrien state = EOLN; 157279968Sobrien return (CRLF); 157379968Sobrien } 157479968Sobrien /* FALLTHROUGH */ 157579968Sobrien 157679968Sobrien case STR1: 157779968Sobrien case ZSTR1: 157879968Sobrien dostr1: 157979968Sobrien if (cmdp[cpos] == ' ') { 158079968Sobrien cpos++; 158179968Sobrien state = state == OSTR ? STR2 : state+1; 158279968Sobrien return (SP); 158379968Sobrien } 158479968Sobrien break; 158579968Sobrien 158679968Sobrien case ZSTR2: 158779968Sobrien if (cmdp[cpos] == '\n') { 158879968Sobrien state = EOLN; 158979968Sobrien return (CRLF); 159079968Sobrien } 159179968Sobrien /* FALLTHROUGH */ 159279968Sobrien 159379968Sobrien case STR2: 159479968Sobrien cp = &cmdp[cpos]; 159579968Sobrien n = strlen(cp); 159679968Sobrien cpos += n - 1; 159779968Sobrien /* 159879968Sobrien * Make sure the string is nonempty and \n terminated. 159979968Sobrien */ 160079968Sobrien if (n > 1 && cmdp[cpos] == '\n') { 160179968Sobrien cmdp[cpos] = '\0'; 1602161764Sobrien yylval.s = ftpd_strdup(cp); 160379968Sobrien cmdp[cpos] = '\n'; 160479968Sobrien state = ARGS; 160579968Sobrien return (STRING); 160679968Sobrien } 160779968Sobrien break; 160879968Sobrien 160979968Sobrien case NSTR: 161079968Sobrien if (cmdp[cpos] == ' ') { 161179968Sobrien cpos++; 161279968Sobrien return (SP); 161379968Sobrien } 1614161764Sobrien if (isdigit((unsigned char)cmdp[cpos])) { 161579968Sobrien cp = &cmdp[cpos]; 1616161764Sobrien while (isdigit((unsigned char)cmdp[++cpos])) 161779968Sobrien ; 161879968Sobrien c = cmdp[cpos]; 161979968Sobrien cmdp[cpos] = '\0'; 1620108746Sobrien yylval.u.i = atoi(cp); 162179968Sobrien cmdp[cpos] = c; 162279968Sobrien state = STR1; 162379968Sobrien return (NUMBER); 162479968Sobrien } 162579968Sobrien state = STR1; 162679968Sobrien goto dostr1; 162779968Sobrien 162879968Sobrien case ARGS: 1629161764Sobrien if (isdigit((unsigned char)cmdp[cpos])) { 163079968Sobrien cp = &cmdp[cpos]; 1631161764Sobrien while (isdigit((unsigned char)cmdp[++cpos])) 163279968Sobrien ; 163379968Sobrien c = cmdp[cpos]; 163479968Sobrien cmdp[cpos] = '\0'; 1635108746Sobrien yylval.u.i = atoi(cp); 1636133936Sobrien yylval.u.ll = STRTOLL(cp, (char **)NULL, 10); 163779968Sobrien cmdp[cpos] = c; 163879968Sobrien return (NUMBER); 163979968Sobrien } 164079968Sobrien if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0 1641161764Sobrien && !isalnum((unsigned char)cmdp[cpos + 3])) { 164279968Sobrien cpos += 3; 1643133936Sobrien return (ALL); 164479968Sobrien } 164579968Sobrien switch (cmdp[cpos++]) { 164679968Sobrien 164779968Sobrien case '\n': 164879968Sobrien state = EOLN; 164979968Sobrien return (CRLF); 165079968Sobrien 165179968Sobrien case ' ': 165279968Sobrien return (SP); 165379968Sobrien 165479968Sobrien case ',': 165579968Sobrien return (COMMA); 165679968Sobrien 165779968Sobrien case 'A': 165879968Sobrien case 'a': 165979968Sobrien return (A); 166079968Sobrien 166179968Sobrien case 'B': 166279968Sobrien case 'b': 166379968Sobrien return (B); 166479968Sobrien 166579968Sobrien case 'C': 166679968Sobrien case 'c': 166779968Sobrien return (C); 166879968Sobrien 166979968Sobrien case 'E': 167079968Sobrien case 'e': 167179968Sobrien return (E); 167279968Sobrien 167379968Sobrien case 'F': 167479968Sobrien case 'f': 167579968Sobrien return (F); 167679968Sobrien 167779968Sobrien case 'I': 167879968Sobrien case 'i': 167979968Sobrien return (I); 168079968Sobrien 168179968Sobrien case 'L': 168279968Sobrien case 'l': 168379968Sobrien return (L); 168479968Sobrien 168579968Sobrien case 'N': 168679968Sobrien case 'n': 168779968Sobrien return (N); 168879968Sobrien 168979968Sobrien case 'P': 169079968Sobrien case 'p': 169179968Sobrien return (P); 169279968Sobrien 169379968Sobrien case 'R': 169479968Sobrien case 'r': 169579968Sobrien return (R); 169679968Sobrien 169779968Sobrien case 'S': 169879968Sobrien case 's': 169979968Sobrien return (S); 170079968Sobrien 170179968Sobrien case 'T': 170279968Sobrien case 't': 170379968Sobrien return (T); 170479968Sobrien 170579968Sobrien } 170679968Sobrien break; 170779968Sobrien 170879968Sobrien case NOARGS: 170979968Sobrien if (cmdp[cpos] == '\n') { 171079968Sobrien state = EOLN; 171179968Sobrien return (CRLF); 171279968Sobrien } 171379968Sobrien c = cmdp[cpos]; 171479968Sobrien cmdp[cpos] = '\0'; 171579968Sobrien reply(501, "'%s' command does not take any arguments.", cmdp); 171679968Sobrien hasyyerrored = 1; 171779968Sobrien cmdp[cpos] = c; 171879968Sobrien break; 171979968Sobrien 172079968Sobrien case EOLN: 172179968Sobrien state = CMD; 172279968Sobrien return (0); 172379968Sobrien 172479968Sobrien default: 172579968Sobrien fatal("Unknown state in scanner."); 172679968Sobrien } 172779968Sobrien yyerror(NULL); 172879968Sobrien state = CMD; 1729133936Sobrien return (0); 173079968Sobrien} 173179968Sobrien 173279968Sobrien/* ARGSUSED */ 173379968Sobrienvoid 173479968Sobrienyyerror(char *s) 173579968Sobrien{ 173679968Sobrien char *cp; 173779968Sobrien 173879968Sobrien if (hasyyerrored || is_oob) 173979968Sobrien return; 174079968Sobrien if ((cp = strchr(cmdp,'\n')) != NULL) 174179968Sobrien *cp = '\0'; 174279968Sobrien reply(500, "'%s': command not understood.", cmdp); 174379968Sobrien hasyyerrored = 1; 174479968Sobrien} 174579968Sobrien 174679968Sobrienstatic void 174779968Sobrienhelp(struct tab *ctab, const char *s) 174879968Sobrien{ 174979968Sobrien struct tab *c; 175079968Sobrien int width, NCMDS; 175192282Sobrien char *htype; 175279968Sobrien 175379968Sobrien if (ctab == sitetab) 175492282Sobrien htype = "SITE "; 175579968Sobrien else 175692282Sobrien htype = ""; 175779968Sobrien width = 0, NCMDS = 0; 175879968Sobrien for (c = ctab; c->name != NULL; c++) { 175979968Sobrien int len = strlen(c->name); 176079968Sobrien 176179968Sobrien if (len > width) 176279968Sobrien width = len; 176379968Sobrien NCMDS++; 176479968Sobrien } 176579968Sobrien width = (width + 8) &~ 7; 176679968Sobrien if (s == 0) { 176779968Sobrien int i, j, w; 176879968Sobrien int columns, lines; 176979968Sobrien 177079968Sobrien reply(-214, "%s", ""); 177192282Sobrien reply(0, "The following %scommands are recognized.", htype); 177279968Sobrien reply(0, "(`-' = not implemented, `+' = supports options)"); 177379968Sobrien columns = 76 / width; 177479968Sobrien if (columns == 0) 177579968Sobrien columns = 1; 177679968Sobrien lines = (NCMDS + columns - 1) / columns; 177779968Sobrien for (i = 0; i < lines; i++) { 177879968Sobrien cprintf(stdout, " "); 177979968Sobrien for (j = 0; j < columns; j++) { 178079968Sobrien c = ctab + j * lines + i; 178179968Sobrien cprintf(stdout, "%s", c->name); 178279968Sobrien w = strlen(c->name); 178379968Sobrien if (! CMD_IMPLEMENTED(c)) { 178479968Sobrien CPUTC('-', stdout); 178579968Sobrien w++; 178679968Sobrien } 178779968Sobrien if (CMD_HAS_OPTIONS(c)) { 178879968Sobrien CPUTC('+', stdout); 178979968Sobrien w++; 179079968Sobrien } 179179968Sobrien if (c + lines >= &ctab[NCMDS]) 179279968Sobrien break; 179379968Sobrien while (w < width) { 179479968Sobrien CPUTC(' ', stdout); 179579968Sobrien w++; 179679968Sobrien } 179779968Sobrien } 179879968Sobrien cprintf(stdout, "\r\n"); 179979968Sobrien } 180079968Sobrien (void) fflush(stdout); 180179968Sobrien reply(214, "Direct comments to ftp-bugs@%s.", hostname); 180279968Sobrien return; 180379968Sobrien } 180479968Sobrien c = lookup(ctab, s); 180579968Sobrien if (c == (struct tab *)0) { 1806108746Sobrien reply(502, "Unknown command '%s'.", s); 180779968Sobrien return; 180879968Sobrien } 180979968Sobrien if (CMD_IMPLEMENTED(c)) 181092282Sobrien reply(214, "Syntax: %s%s %s", htype, c->name, c->help); 181179968Sobrien else 1812108746Sobrien reply(504, "%s%-*s\t%s; not implemented.", htype, width, 181379968Sobrien c->name, c->help); 181479968Sobrien} 181579968Sobrien 181679968Sobrien/* 181779968Sobrien * Check that the structures used for a PORT, LPRT or EPRT command are 181879968Sobrien * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks. 181979968Sobrien * If family != -1 check that his_addr.su_family == family. 182079968Sobrien */ 182179968Sobrienstatic void 182279968Sobrienport_check(const char *cmd, int family) 182379968Sobrien{ 182479968Sobrien char h1[NI_MAXHOST], h2[NI_MAXHOST]; 182579968Sobrien char s1[NI_MAXHOST], s2[NI_MAXHOST]; 182679968Sobrien#ifdef NI_WITHSCOPEID 182779968Sobrien const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID; 182879968Sobrien#else 182979968Sobrien const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; 183079968Sobrien#endif 183179968Sobrien 183279968Sobrien if (epsvall) { 183379968Sobrien reply(501, "%s disallowed after EPSV ALL", cmd); 183479968Sobrien return; 183579968Sobrien } 183679968Sobrien 183779968Sobrien if (family != -1 && his_addr.su_family != family) { 183879968Sobrien port_check_fail: 183979968Sobrien reply(500, "Illegal %s command rejected", cmd); 184079968Sobrien return; 184179968Sobrien } 184279968Sobrien 184379968Sobrien if (data_dest.su_family != his_addr.su_family) 184479968Sobrien goto port_check_fail; 184579968Sobrien 184679968Sobrien /* be paranoid, if told so */ 184779968Sobrien if (CURCLASS_FLAGS_ISSET(checkportcmd)) { 184879968Sobrien#ifdef INET6 184979968Sobrien /* 185079968Sobrien * be paranoid, there are getnameinfo implementation that does 185179968Sobrien * not present scopeid portion 185279968Sobrien */ 185379968Sobrien if (data_dest.su_family == AF_INET6 && 185479968Sobrien data_dest.su_scope_id != his_addr.su_scope_id) 185579968Sobrien goto port_check_fail; 185679968Sobrien#endif 185779968Sobrien 185879968Sobrien if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len, 185979968Sobrien h1, sizeof(h1), s1, sizeof(s1), niflags)) 186079968Sobrien goto port_check_fail; 186179968Sobrien if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 186279968Sobrien h2, sizeof(h2), s2, sizeof(s2), niflags)) 186379968Sobrien goto port_check_fail; 186479968Sobrien 186579968Sobrien if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0) 186679968Sobrien goto port_check_fail; 186779968Sobrien } 186879968Sobrien 186979968Sobrien usedefault = 0; 187079968Sobrien if (pdata >= 0) { 187179968Sobrien (void) close(pdata); 187279968Sobrien pdata = -1; 187379968Sobrien } 187479968Sobrien reply(200, "%s command successful.", cmd); 187579968Sobrien} 1876