ftpcmd.y revision 82460
11592Srgrimes/* 21592Srgrimes * Copyright (c) 1985, 1988, 1993, 1994 31592Srgrimes * The Regents of the University of California. All rights reserved. 41592Srgrimes * 51592Srgrimes * Redistribution and use in source and binary forms, with or without 61592Srgrimes * modification, are permitted provided that the following conditions 71592Srgrimes * are met: 81592Srgrimes * 1. Redistributions of source code must retain the above copyright 91592Srgrimes * notice, this list of conditions and the following disclaimer. 101592Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111592Srgrimes * notice, this list of conditions and the following disclaimer in the 121592Srgrimes * documentation and/or other materials provided with the distribution. 131592Srgrimes * 3. All advertising materials mentioning features or use of this software 141592Srgrimes * must display the following acknowledgement: 151592Srgrimes * This product includes software developed by the University of 161592Srgrimes * California, Berkeley and its contributors. 171592Srgrimes * 4. Neither the name of the University nor the names of its contributors 181592Srgrimes * may be used to endorse or promote products derived from this software 191592Srgrimes * without specific prior written permission. 201592Srgrimes * 211592Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221592Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231592Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241592Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251592Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261592Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271592Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281592Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291592Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301592Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311592Srgrimes * SUCH DAMAGE. 321592Srgrimes * 331592Srgrimes * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 341592Srgrimes */ 351592Srgrimes 361592Srgrimes/* 371592Srgrimes * Grammar for FTP commands. 381592Srgrimes * See RFC 959. 391592Srgrimes */ 401592Srgrimes 411592Srgrimes%{ 421592Srgrimes 431592Srgrimes#ifndef lint 4431329Scharnier#if 0 451592Srgrimesstatic char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; 4631329Scharnier#endif 4731329Scharnierstatic const char rcsid[] = 4850476Speter "$FreeBSD: head/libexec/ftpd/ftpcmd.y 82460 2001-08-28 11:59:21Z nik $"; 491592Srgrimes#endif /* not lint */ 501592Srgrimes 511592Srgrimes#include <sys/param.h> 521592Srgrimes#include <sys/socket.h> 531592Srgrimes#include <sys/stat.h> 541592Srgrimes 551592Srgrimes#include <netinet/in.h> 561592Srgrimes#include <arpa/ftp.h> 571592Srgrimes 581592Srgrimes#include <ctype.h> 591592Srgrimes#include <errno.h> 601592Srgrimes#include <glob.h> 6156668Sshin#include <netdb.h> 621592Srgrimes#include <pwd.h> 631592Srgrimes#include <setjmp.h> 641592Srgrimes#include <signal.h> 651592Srgrimes#include <stdio.h> 661592Srgrimes#include <stdlib.h> 671592Srgrimes#include <string.h> 681592Srgrimes#include <syslog.h> 691592Srgrimes#include <time.h> 701592Srgrimes#include <unistd.h> 7113139Speter#include <libutil.h> 7275535Sphk#include <md5.h> 731592Srgrimes 741592Srgrimes#include "extern.h" 751592Srgrimes 7656668Sshinextern union sockunion data_dest, his_addr; 771592Srgrimesextern int logged_in; 781592Srgrimesextern struct passwd *pw; 791592Srgrimesextern int guest; 8017435Spstextern int paranoid; 811592Srgrimesextern int logging; 821592Srgrimesextern int type; 831592Srgrimesextern int form; 8476096Smarkmextern int ftpdebug; 851592Srgrimesextern int timeout; 861592Srgrimesextern int maxtimeout; 871592Srgrimesextern int pdata; 8827650Sdavidnextern char *hostname; 8927650Sdavidnextern char remotehost[]; 901592Srgrimesextern char proctitle[]; 911592Srgrimesextern int usedefault; 921592Srgrimesextern int transflag; 931592Srgrimesextern char tmpline[]; 9470102Sphkextern int readonly; 9570102Sphkextern int noepsv; 9682460Snikextern int noretr; 971592Srgrimes 981592Srgrimesoff_t restart_point; 991592Srgrimes 1001592Srgrimesstatic int cmd_type; 1011592Srgrimesstatic int cmd_form; 1021592Srgrimesstatic int cmd_bytesz; 1031592Srgrimeschar cbuf[512]; 1041592Srgrimeschar *fromname; 1051592Srgrimes 10656668Sshinextern int epsvall; 10756668Sshin 1081592Srgrimes%} 1091592Srgrimes 1101592Srgrimes%union { 1111592Srgrimes int i; 1121592Srgrimes char *s; 1131592Srgrimes} 1141592Srgrimes 1151592Srgrimes%token 1161592Srgrimes A B C E F I 1171592Srgrimes L N P R S T 11856668Sshin ALL 1191592Srgrimes 1201592Srgrimes SP CRLF COMMA 1211592Srgrimes 1221592Srgrimes USER PASS ACCT REIN QUIT PORT 1231592Srgrimes PASV TYPE STRU MODE RETR STOR 1241592Srgrimes APPE MLFL MAIL MSND MSOM MSAM 1251592Srgrimes MRSQ MRCP ALLO REST RNFR RNTO 1261592Srgrimes ABOR DELE CWD LIST NLST SITE 1271592Srgrimes STAT HELP NOOP MKD RMD PWD 1281592Srgrimes CDUP STOU SMNT SYST SIZE MDTM 12956668Sshin LPRT LPSV EPRT EPSV 1301592Srgrimes 13175535Sphk UMASK IDLE CHMOD MDFIVE 1321592Srgrimes 1331592Srgrimes LEXERR 1341592Srgrimes 1351592Srgrimes%token <s> STRING 1361592Srgrimes%token <i> NUMBER 1371592Srgrimes 1381592Srgrimes%type <i> check_login octal_number byte_size 13970102Sphk%type <i> check_login_ro octal_number byte_size 14070102Sphk%type <i> check_login_epsv octal_number byte_size 1411592Srgrimes%type <i> struct_code mode_code type_code form_code 14275567Speter%type <s> pathstring pathname password username 14356668Sshin%type <s> ALL 1441592Srgrimes 1451592Srgrimes%start cmd_list 1461592Srgrimes 1471592Srgrimes%% 1481592Srgrimes 1491592Srgrimescmd_list 1501592Srgrimes : /* empty */ 1511592Srgrimes | cmd_list cmd 1521592Srgrimes { 1531592Srgrimes fromname = (char *) 0; 1541592Srgrimes restart_point = (off_t) 0; 1551592Srgrimes } 1561592Srgrimes | cmd_list rcmd 1571592Srgrimes ; 1581592Srgrimes 1591592Srgrimescmd 1601592Srgrimes : USER SP username CRLF 1611592Srgrimes { 1621592Srgrimes user($3); 1631592Srgrimes free($3); 1641592Srgrimes } 1651592Srgrimes | PASS SP password CRLF 1661592Srgrimes { 1671592Srgrimes pass($3); 1681592Srgrimes free($3); 1691592Srgrimes } 17075556Sgreen | PASS CRLF 17175556Sgreen { 17275556Sgreen pass(""); 17375556Sgreen } 17417433Spst | PORT check_login SP host_port CRLF 1751592Srgrimes { 17656668Sshin if (epsvall) { 17756668Sshin reply(501, "no PORT allowed after EPSV ALL"); 17856668Sshin goto port_done; 17956668Sshin } 18056668Sshin if (!$2) 18156668Sshin goto port_done; 18256668Sshin if (port_check("PORT") == 1) 18356668Sshin goto port_done; 18456668Sshin#ifdef INET6 18556668Sshin if ((his_addr.su_family != AF_INET6 || 18656668Sshin !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { 18756668Sshin /* shoud never happen */ 18856668Sshin usedefault = 1; 18956668Sshin reply(500, "Invalid address rejected."); 19056668Sshin goto port_done; 19156668Sshin } 19256668Sshin port_check_v6("pcmd"); 19356668Sshin#endif 19456668Sshin port_done: 19556668Sshin } 19656668Sshin | LPRT check_login SP host_long_port CRLF 19756668Sshin { 19856668Sshin if (epsvall) { 19956668Sshin reply(501, "no LPRT allowed after EPSV ALL"); 20056668Sshin goto lprt_done; 20156668Sshin } 20256668Sshin if (!$2) 20356668Sshin goto lprt_done; 20456668Sshin if (port_check("LPRT") == 1) 20556668Sshin goto lprt_done; 20656668Sshin#ifdef INET6 20756668Sshin if (his_addr.su_family != AF_INET6) { 20856668Sshin usedefault = 1; 20956668Sshin reply(500, "Invalid address rejected."); 21056668Sshin goto lprt_done; 21156668Sshin } 21256668Sshin if (port_check_v6("LPRT") == 1) 21356668Sshin goto lprt_done; 21456668Sshin#endif 21556668Sshin lprt_done: 21656668Sshin } 21756668Sshin | EPRT check_login SP STRING CRLF 21856668Sshin { 21956668Sshin char delim; 22056668Sshin char *tmp = NULL; 22156668Sshin char *p, *q; 22256668Sshin char *result[3]; 22356668Sshin struct addrinfo hints; 22456668Sshin struct addrinfo *res; 22556668Sshin int i; 22656668Sshin 22756668Sshin if (epsvall) { 22856668Sshin reply(501, "no EPRT allowed after EPSV ALL"); 22956668Sshin goto eprt_done; 23056668Sshin } 23156668Sshin if (!$2) 23256668Sshin goto eprt_done; 23356668Sshin 23456668Sshin memset(&data_dest, 0, sizeof(data_dest)); 23556668Sshin tmp = strdup($4); 23676096Smarkm if (ftpdebug) 23756668Sshin syslog(LOG_DEBUG, "%s", tmp); 23856668Sshin if (!tmp) { 23976096Smarkm fatalerror("not enough core"); 24056668Sshin /*NOTREACHED*/ 24156668Sshin } 24256668Sshin p = tmp; 24356668Sshin delim = p[0]; 24456668Sshin p++; 24556668Sshin memset(result, 0, sizeof(result)); 24656668Sshin for (i = 0; i < 3; i++) { 24756668Sshin q = strchr(p, delim); 24856668Sshin if (!q || *q != delim) { 24956668Sshin parsefail: 25056668Sshin reply(500, 25156668Sshin "Invalid argument, rejected."); 25256668Sshin if (tmp) 25356668Sshin free(tmp); 25417433Spst usedefault = 1; 25556668Sshin goto eprt_done; 25617433Spst } 25756668Sshin *q++ = '\0'; 25856668Sshin result[i] = p; 25976096Smarkm if (ftpdebug) 26056668Sshin syslog(LOG_DEBUG, "%d: %s", i, p); 26156668Sshin p = q; 2621592Srgrimes } 26356668Sshin 26456668Sshin /* some more sanity check */ 26556668Sshin p = result[0]; 26656668Sshin while (*p) { 26756668Sshin if (!isdigit(*p)) 26856668Sshin goto parsefail; 26956668Sshin p++; 27056668Sshin } 27156668Sshin p = result[2]; 27256668Sshin while (*p) { 27356668Sshin if (!isdigit(*p)) 27456668Sshin goto parsefail; 27556668Sshin p++; 27656668Sshin } 27756668Sshin 27856668Sshin /* grab address */ 27956668Sshin memset(&hints, 0, sizeof(hints)); 28056668Sshin if (atoi(result[0]) == 1) 28156668Sshin hints.ai_family = PF_INET; 28256668Sshin#ifdef INET6 28356668Sshin else if (atoi(result[0]) == 2) 28456668Sshin hints.ai_family = PF_INET6; 28556668Sshin#endif 28656668Sshin else 28756668Sshin hints.ai_family = PF_UNSPEC; /*XXX*/ 28856668Sshin hints.ai_socktype = SOCK_STREAM; 28956668Sshin i = getaddrinfo(result[1], result[2], &hints, &res); 29056668Sshin if (i) 29156668Sshin goto parsefail; 29256668Sshin memcpy(&data_dest, res->ai_addr, res->ai_addrlen); 29356668Sshin#ifdef INET6 29456668Sshin if (his_addr.su_family == AF_INET6 29556668Sshin && data_dest.su_family == AF_INET6) { 29656668Sshin /* XXX more sanity checks! */ 29756668Sshin data_dest.su_sin6.sin6_scope_id = 29856668Sshin his_addr.su_sin6.sin6_scope_id; 29956668Sshin } 30056668Sshin#endif 30156668Sshin free(tmp); 30256668Sshin tmp = NULL; 30356668Sshin 30456668Sshin if (port_check("EPRT") == 1) 30556668Sshin goto eprt_done; 30656668Sshin#ifdef INET6 30756668Sshin if (his_addr.su_family != AF_INET6) { 30856668Sshin usedefault = 1; 30956668Sshin reply(500, "Invalid address rejected."); 31056668Sshin goto eprt_done; 31156668Sshin } 31256668Sshin if (port_check_v6("EPRT") == 1) 31356668Sshin goto eprt_done; 31456668Sshin#endif 31556668Sshin eprt_done:; 3161592Srgrimes } 31717433Spst | PASV check_login CRLF 3181592Srgrimes { 31956668Sshin if (epsvall) 32056668Sshin reply(501, "no PASV allowed after EPSV ALL"); 32156668Sshin else if ($2) 32217433Spst passive(); 3231592Srgrimes } 32456668Sshin | LPSV check_login CRLF 32556668Sshin { 32656668Sshin if (epsvall) 32756668Sshin reply(501, "no LPSV allowed after EPSV ALL"); 32856668Sshin else if ($2) 32956668Sshin long_passive("LPSV", PF_UNSPEC); 33056668Sshin } 33170102Sphk | EPSV check_login_epsv SP NUMBER CRLF 33256668Sshin { 33356668Sshin if ($2) { 33456668Sshin int pf; 33556668Sshin switch ($4) { 33656668Sshin case 1: 33756668Sshin pf = PF_INET; 33856668Sshin break; 33956668Sshin#ifdef INET6 34056668Sshin case 2: 34156668Sshin pf = PF_INET6; 34256668Sshin break; 34356668Sshin#endif 34456668Sshin default: 34556668Sshin pf = -1; /*junk value*/ 34656668Sshin break; 34756668Sshin } 34856668Sshin long_passive("EPSV", pf); 34956668Sshin } 35056668Sshin } 35170102Sphk | EPSV check_login_epsv SP ALL CRLF 35256668Sshin { 35356668Sshin if ($2) { 35456668Sshin reply(200, 35556668Sshin "EPSV ALL command successful."); 35656668Sshin epsvall++; 35756668Sshin } 35856668Sshin } 35970102Sphk | EPSV check_login_epsv CRLF 36056668Sshin { 36156668Sshin if ($2) 36256668Sshin long_passive("EPSV", PF_UNSPEC); 36356668Sshin } 36471278Sjedgar | TYPE check_login SP type_code CRLF 3651592Srgrimes { 36671278Sjedgar if ($2) { 36771278Sjedgar switch (cmd_type) { 3681592Srgrimes 36971278Sjedgar case TYPE_A: 37071278Sjedgar if (cmd_form == FORM_N) { 37171278Sjedgar reply(200, "Type set to A."); 37271278Sjedgar type = cmd_type; 37371278Sjedgar form = cmd_form; 37471278Sjedgar } else 37571278Sjedgar reply(504, "Form must be N."); 37671278Sjedgar break; 3771592Srgrimes 37871278Sjedgar case TYPE_E: 37971278Sjedgar reply(504, "Type E not implemented."); 38071278Sjedgar break; 3811592Srgrimes 38271278Sjedgar case TYPE_I: 38371278Sjedgar reply(200, "Type set to I."); 38471278Sjedgar type = cmd_type; 38571278Sjedgar break; 3861592Srgrimes 38771278Sjedgar case TYPE_L: 3881592Srgrimes#if NBBY == 8 38971278Sjedgar if (cmd_bytesz == 8) { 39071278Sjedgar reply(200, 39171278Sjedgar "Type set to L (byte size 8)."); 39271278Sjedgar type = cmd_type; 39371278Sjedgar } else 39471278Sjedgar reply(504, "Byte size must be 8."); 3951592Srgrimes#else /* NBBY == 8 */ 39671278Sjedgar UNIMPLEMENTED for NBBY != 8 3971592Srgrimes#endif /* NBBY == 8 */ 39871278Sjedgar } 3991592Srgrimes } 4001592Srgrimes } 40171278Sjedgar | STRU check_login SP struct_code CRLF 4021592Srgrimes { 40371278Sjedgar if ($2) { 40471278Sjedgar switch ($4) { 4051592Srgrimes 40671278Sjedgar case STRU_F: 40771278Sjedgar reply(200, "STRU F ok."); 40871278Sjedgar break; 4091592Srgrimes 41071278Sjedgar default: 41171278Sjedgar reply(504, "Unimplemented STRU type."); 41271278Sjedgar } 4131592Srgrimes } 4141592Srgrimes } 41571278Sjedgar | MODE check_login SP mode_code CRLF 4161592Srgrimes { 41771278Sjedgar if ($2) { 41871278Sjedgar switch ($4) { 4191592Srgrimes 42071278Sjedgar case MODE_S: 42171278Sjedgar reply(200, "MODE S ok."); 42271278Sjedgar break; 42371278Sjedgar 42471278Sjedgar default: 42571278Sjedgar reply(502, "Unimplemented MODE type."); 42671278Sjedgar } 4271592Srgrimes } 4281592Srgrimes } 42971278Sjedgar | ALLO check_login SP NUMBER CRLF 4301592Srgrimes { 43171278Sjedgar if ($2) { 43271278Sjedgar reply(202, "ALLO command ignored."); 43371278Sjedgar } 4341592Srgrimes } 43571278Sjedgar | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 4361592Srgrimes { 43771278Sjedgar if ($2) { 43871278Sjedgar reply(202, "ALLO command ignored."); 43971278Sjedgar } 4401592Srgrimes } 4411592Srgrimes | RETR check_login SP pathname CRLF 4421592Srgrimes { 44382460Snik if (noretr) 44482460Snik reply(500, "RETR command is disabled"); 44582460Snik else if ($2 && $4 != NULL) 4461592Srgrimes retrieve((char *) 0, $4); 44782460Snik 4481592Srgrimes if ($4 != NULL) 4491592Srgrimes free($4); 4501592Srgrimes } 45170102Sphk | STOR check_login_ro SP pathname CRLF 4521592Srgrimes { 4531592Srgrimes if ($2 && $4 != NULL) 4541592Srgrimes store($4, "w", 0); 4551592Srgrimes if ($4 != NULL) 4561592Srgrimes free($4); 4571592Srgrimes } 45870102Sphk | APPE check_login_ro SP pathname CRLF 4591592Srgrimes { 4601592Srgrimes if ($2 && $4 != NULL) 4611592Srgrimes store($4, "a", 0); 4621592Srgrimes if ($4 != NULL) 4631592Srgrimes free($4); 4641592Srgrimes } 4651592Srgrimes | NLST check_login CRLF 4661592Srgrimes { 4671592Srgrimes if ($2) 4681592Srgrimes send_file_list("."); 4691592Srgrimes } 4701592Srgrimes | NLST check_login SP STRING CRLF 4711592Srgrimes { 4721592Srgrimes if ($2 && $4 != NULL) 4731592Srgrimes send_file_list($4); 4741592Srgrimes if ($4 != NULL) 4751592Srgrimes free($4); 4761592Srgrimes } 4771592Srgrimes | LIST check_login CRLF 4781592Srgrimes { 4791592Srgrimes if ($2) 4801592Srgrimes retrieve("/bin/ls -lgA", ""); 4811592Srgrimes } 48275567Speter | LIST check_login SP pathstring CRLF 4831592Srgrimes { 4841592Srgrimes if ($2 && $4 != NULL) 4851592Srgrimes retrieve("/bin/ls -lgA %s", $4); 4861592Srgrimes if ($4 != NULL) 4871592Srgrimes free($4); 4881592Srgrimes } 4891592Srgrimes | STAT check_login SP pathname CRLF 4901592Srgrimes { 4911592Srgrimes if ($2 && $4 != NULL) 4921592Srgrimes statfilecmd($4); 4931592Srgrimes if ($4 != NULL) 4941592Srgrimes free($4); 4951592Srgrimes } 49671278Sjedgar | STAT check_login CRLF 4971592Srgrimes { 49871278Sjedgar if ($2) { 49971278Sjedgar statcmd(); 50071278Sjedgar } 5011592Srgrimes } 50270102Sphk | DELE check_login_ro SP pathname CRLF 5031592Srgrimes { 5041592Srgrimes if ($2 && $4 != NULL) 5051592Srgrimes delete($4); 5061592Srgrimes if ($4 != NULL) 5071592Srgrimes free($4); 5081592Srgrimes } 50970102Sphk | RNTO check_login_ro SP pathname CRLF 5101592Srgrimes { 51117433Spst if ($2) { 51217433Spst if (fromname) { 51317433Spst renamecmd(fromname, $4); 51417433Spst free(fromname); 51517433Spst fromname = (char *) 0; 51617433Spst } else { 51717433Spst reply(503, "Bad sequence of commands."); 51817433Spst } 5191592Srgrimes } 52017433Spst free($4); 5211592Srgrimes } 52271278Sjedgar | ABOR check_login CRLF 5231592Srgrimes { 52471278Sjedgar if ($2) 52571278Sjedgar reply(225, "ABOR command successful."); 5261592Srgrimes } 5271592Srgrimes | CWD check_login CRLF 5281592Srgrimes { 52969234Sdanny if ($2) { 53069234Sdanny if (guest) 53169234Sdanny cwd("/"); 53269234Sdanny else 53369234Sdanny cwd(pw->pw_dir); 53469234Sdanny } 5351592Srgrimes } 5361592Srgrimes | CWD check_login SP pathname CRLF 5371592Srgrimes { 5381592Srgrimes if ($2 && $4 != NULL) 5391592Srgrimes cwd($4); 5401592Srgrimes if ($4 != NULL) 5411592Srgrimes free($4); 5421592Srgrimes } 5431592Srgrimes | HELP CRLF 5441592Srgrimes { 5451592Srgrimes help(cmdtab, (char *) 0); 5461592Srgrimes } 5471592Srgrimes | HELP SP STRING CRLF 5481592Srgrimes { 5491592Srgrimes char *cp = $3; 5501592Srgrimes 5511592Srgrimes if (strncasecmp(cp, "SITE", 4) == 0) { 5521592Srgrimes cp = $3 + 4; 5531592Srgrimes if (*cp == ' ') 5541592Srgrimes cp++; 5551592Srgrimes if (*cp) 5561592Srgrimes help(sitetab, cp); 5571592Srgrimes else 5581592Srgrimes help(sitetab, (char *) 0); 5591592Srgrimes } else 5601592Srgrimes help(cmdtab, $3); 5611592Srgrimes } 5621592Srgrimes | NOOP CRLF 5631592Srgrimes { 5641592Srgrimes reply(200, "NOOP command successful."); 5651592Srgrimes } 56670102Sphk | MKD check_login_ro SP pathname CRLF 5671592Srgrimes { 5681592Srgrimes if ($2 && $4 != NULL) 5691592Srgrimes makedir($4); 5701592Srgrimes if ($4 != NULL) 5711592Srgrimes free($4); 5721592Srgrimes } 57370102Sphk | RMD check_login_ro SP pathname CRLF 5741592Srgrimes { 5751592Srgrimes if ($2 && $4 != NULL) 5761592Srgrimes removedir($4); 5771592Srgrimes if ($4 != NULL) 5781592Srgrimes free($4); 5791592Srgrimes } 5801592Srgrimes | PWD check_login CRLF 5811592Srgrimes { 5821592Srgrimes if ($2) 5831592Srgrimes pwd(); 5841592Srgrimes } 5851592Srgrimes | CDUP check_login CRLF 5861592Srgrimes { 5871592Srgrimes if ($2) 5881592Srgrimes cwd(".."); 5891592Srgrimes } 5901592Srgrimes | SITE SP HELP CRLF 5911592Srgrimes { 5921592Srgrimes help(sitetab, (char *) 0); 5931592Srgrimes } 5941592Srgrimes | SITE SP HELP SP STRING CRLF 5951592Srgrimes { 5961592Srgrimes help(sitetab, $5); 5971592Srgrimes } 59875535Sphk | SITE SP MDFIVE check_login SP pathname CRLF 59975535Sphk { 60075535Sphk char p[64], *q; 60175535Sphk 60275535Sphk if ($4) { 60375535Sphk q = MD5File($6, p); 60475535Sphk if (q != NULL) 60575535Sphk reply(200, "MD5(%s) = %s", $6, p); 60675535Sphk else 60775535Sphk perror_reply(550, $6); 60875535Sphk } 60975535Sphk } 6101592Srgrimes | SITE SP UMASK check_login CRLF 6111592Srgrimes { 6121592Srgrimes int oldmask; 6131592Srgrimes 6141592Srgrimes if ($4) { 6151592Srgrimes oldmask = umask(0); 6161592Srgrimes (void) umask(oldmask); 6171592Srgrimes reply(200, "Current UMASK is %03o", oldmask); 6181592Srgrimes } 6191592Srgrimes } 6201592Srgrimes | SITE SP UMASK check_login SP octal_number CRLF 6211592Srgrimes { 6221592Srgrimes int oldmask; 6231592Srgrimes 6241592Srgrimes if ($4) { 6251592Srgrimes if (($6 == -1) || ($6 > 0777)) { 6261592Srgrimes reply(501, "Bad UMASK value"); 6271592Srgrimes } else { 6281592Srgrimes oldmask = umask($6); 6291592Srgrimes reply(200, 6301592Srgrimes "UMASK set to %03o (was %03o)", 6311592Srgrimes $6, oldmask); 6321592Srgrimes } 6331592Srgrimes } 6341592Srgrimes } 63570102Sphk | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF 6361592Srgrimes { 6371592Srgrimes if ($4 && ($8 != NULL)) { 6381592Srgrimes if ($6 > 0777) 6391592Srgrimes reply(501, 6401592Srgrimes "CHMOD: Mode value must be between 0 and 0777"); 6411592Srgrimes else if (chmod($8, $6) < 0) 6421592Srgrimes perror_reply(550, $8); 6431592Srgrimes else 6441592Srgrimes reply(200, "CHMOD command successful."); 6451592Srgrimes } 6461592Srgrimes if ($8 != NULL) 6471592Srgrimes free($8); 6481592Srgrimes } 64971278Sjedgar | SITE SP check_login IDLE CRLF 6501592Srgrimes { 65171278Sjedgar if ($3) 65271278Sjedgar reply(200, 65371278Sjedgar "Current IDLE time limit is %d seconds; max %d", 65471278Sjedgar timeout, maxtimeout); 6551592Srgrimes } 65671278Sjedgar | SITE SP check_login IDLE SP NUMBER CRLF 6571592Srgrimes { 65871278Sjedgar if ($3) { 65971278Sjedgar if ($6 < 30 || $6 > maxtimeout) { 66071278Sjedgar reply(501, 66171278Sjedgar "Maximum IDLE time must be between 30 and %d seconds", 66271278Sjedgar maxtimeout); 66371278Sjedgar } else { 66471278Sjedgar timeout = $6; 66571278Sjedgar (void) alarm((unsigned) timeout); 66671278Sjedgar reply(200, 66771278Sjedgar "Maximum IDLE time set to %d seconds", 66871278Sjedgar timeout); 66971278Sjedgar } 6701592Srgrimes } 6711592Srgrimes } 67270102Sphk | STOU check_login_ro SP pathname CRLF 6731592Srgrimes { 6741592Srgrimes if ($2 && $4 != NULL) 6751592Srgrimes store($4, "w", 1); 6761592Srgrimes if ($4 != NULL) 6771592Srgrimes free($4); 6781592Srgrimes } 67971278Sjedgar | SYST check_login CRLF 6801592Srgrimes { 68171278Sjedgar if ($2) 6821592Srgrimes#ifdef unix 6831592Srgrimes#ifdef BSD 6841592Srgrimes reply(215, "UNIX Type: L%d Version: BSD-%d", 6851592Srgrimes NBBY, BSD); 6861592Srgrimes#else /* BSD */ 6871592Srgrimes reply(215, "UNIX Type: L%d", NBBY); 6881592Srgrimes#endif /* BSD */ 6891592Srgrimes#else /* unix */ 6901592Srgrimes reply(215, "UNKNOWN Type: L%d", NBBY); 6911592Srgrimes#endif /* unix */ 6921592Srgrimes } 6931592Srgrimes 6941592Srgrimes /* 6951592Srgrimes * SIZE is not in RFC959, but Postel has blessed it and 6961592Srgrimes * it will be in the updated RFC. 6971592Srgrimes * 6981592Srgrimes * Return size of file in a format suitable for 6991592Srgrimes * using with RESTART (we just count bytes). 7001592Srgrimes */ 7011592Srgrimes | SIZE check_login SP pathname CRLF 7021592Srgrimes { 7031592Srgrimes if ($2 && $4 != NULL) 7041592Srgrimes sizecmd($4); 7051592Srgrimes if ($4 != NULL) 7061592Srgrimes free($4); 7071592Srgrimes } 7081592Srgrimes 7091592Srgrimes /* 7101592Srgrimes * MDTM is not in RFC959, but Postel has blessed it and 7111592Srgrimes * it will be in the updated RFC. 7121592Srgrimes * 7131592Srgrimes * Return modification time of file as an ISO 3307 7141592Srgrimes * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 7151592Srgrimes * where xxx is the fractional second (of any precision, 7161592Srgrimes * not necessarily 3 digits) 7171592Srgrimes */ 7181592Srgrimes | MDTM check_login SP pathname CRLF 7191592Srgrimes { 7201592Srgrimes if ($2 && $4 != NULL) { 7211592Srgrimes struct stat stbuf; 7221592Srgrimes if (stat($4, &stbuf) < 0) 7231592Srgrimes reply(550, "%s: %s", 7241592Srgrimes $4, strerror(errno)); 7251592Srgrimes else if (!S_ISREG(stbuf.st_mode)) { 7261592Srgrimes reply(550, "%s: not a plain file.", $4); 7271592Srgrimes } else { 7281592Srgrimes struct tm *t; 7291592Srgrimes t = gmtime(&stbuf.st_mtime); 7301592Srgrimes reply(213, 73117435Spst "%04d%02d%02d%02d%02d%02d", 73217435Spst 1900 + t->tm_year, 73317435Spst t->tm_mon+1, t->tm_mday, 7341592Srgrimes t->tm_hour, t->tm_min, t->tm_sec); 7351592Srgrimes } 7361592Srgrimes } 7371592Srgrimes if ($4 != NULL) 7381592Srgrimes free($4); 7391592Srgrimes } 7401592Srgrimes | QUIT CRLF 7411592Srgrimes { 7421592Srgrimes reply(221, "Goodbye."); 7431592Srgrimes dologout(0); 7441592Srgrimes } 7451592Srgrimes | error CRLF 7461592Srgrimes { 7471592Srgrimes yyerrok; 7481592Srgrimes } 7491592Srgrimes ; 7501592Srgrimesrcmd 75170102Sphk : RNFR check_login_ro SP pathname CRLF 7521592Srgrimes { 7531592Srgrimes char *renamefrom(); 7541592Srgrimes 7551592Srgrimes restart_point = (off_t) 0; 7561592Srgrimes if ($2 && $4) { 7571592Srgrimes fromname = renamefrom($4); 7581592Srgrimes if (fromname == (char *) 0 && $4) { 7591592Srgrimes free($4); 7601592Srgrimes } 7611592Srgrimes } 7621592Srgrimes } 76371278Sjedgar | REST check_login SP byte_size CRLF 7641592Srgrimes { 76571278Sjedgar if ($2) { 76671278Sjedgar fromname = (char *) 0; 76771278Sjedgar restart_point = $4; /* XXX $4 is only "int" */ 76871278Sjedgar reply(350, "Restarting at %qd. %s", 76971278Sjedgar restart_point, 77071278Sjedgar "Send STORE or RETRIEVE to initiate transfer."); 77171278Sjedgar } 7721592Srgrimes } 7731592Srgrimes ; 7741592Srgrimes 7751592Srgrimesusername 7761592Srgrimes : STRING 7771592Srgrimes ; 7781592Srgrimes 7791592Srgrimespassword 7801592Srgrimes : /* empty */ 7811592Srgrimes { 7821592Srgrimes $$ = (char *)calloc(1, sizeof(char)); 7831592Srgrimes } 7841592Srgrimes | STRING 7851592Srgrimes ; 7861592Srgrimes 7871592Srgrimesbyte_size 7881592Srgrimes : NUMBER 7891592Srgrimes ; 7901592Srgrimes 7911592Srgrimeshost_port 7921592Srgrimes : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 7931592Srgrimes NUMBER COMMA NUMBER 7941592Srgrimes { 7951592Srgrimes char *a, *p; 7961592Srgrimes 79756668Sshin data_dest.su_len = sizeof(struct sockaddr_in); 79856668Sshin data_dest.su_family = AF_INET; 79956668Sshin p = (char *)&data_dest.su_sin.sin_port; 80017435Spst p[0] = $9; p[1] = $11; 80156668Sshin a = (char *)&data_dest.su_sin.sin_addr; 8021592Srgrimes a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 8031592Srgrimes } 8041592Srgrimes ; 8051592Srgrimes 80656668Sshinhost_long_port 80756668Sshin : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 80856668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 80956668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 81056668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 81156668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 81256668Sshin NUMBER 81356668Sshin { 81456668Sshin char *a, *p; 81556668Sshin 81656668Sshin memset(&data_dest, 0, sizeof(data_dest)); 81756668Sshin data_dest.su_len = sizeof(struct sockaddr_in6); 81856668Sshin data_dest.su_family = AF_INET6; 81956668Sshin p = (char *)&data_dest.su_port; 82056668Sshin p[0] = $39; p[1] = $41; 82156668Sshin a = (char *)&data_dest.su_sin6.sin6_addr; 82256668Sshin a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 82356668Sshin a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19; 82456668Sshin a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27; 82556668Sshin a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35; 82656668Sshin if (his_addr.su_family == AF_INET6) { 82756668Sshin /* XXX more sanity checks! */ 82856668Sshin data_dest.su_sin6.sin6_scope_id = 82956668Sshin his_addr.su_sin6.sin6_scope_id; 83056668Sshin } 83156668Sshin if ($1 != 6 || $3 != 16 || $37 != 2) 83256668Sshin memset(&data_dest, 0, sizeof(data_dest)); 83356668Sshin } 83456668Sshin | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83556668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83656668Sshin NUMBER 83756668Sshin { 83856668Sshin char *a, *p; 83956668Sshin 84056668Sshin memset(&data_dest, 0, sizeof(data_dest)); 84156668Sshin data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 84256668Sshin data_dest.su_family = AF_INET; 84356668Sshin p = (char *)&data_dest.su_port; 84456668Sshin p[0] = $15; p[1] = $17; 84556668Sshin a = (char *)&data_dest.su_sin.sin_addr; 84656668Sshin a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 84756668Sshin if ($1 != 4 || $3 != 4 || $13 != 2) 84856668Sshin memset(&data_dest, 0, sizeof(data_dest)); 84956668Sshin } 85056668Sshin ; 85156668Sshin 8521592Srgrimesform_code 8531592Srgrimes : N 8541592Srgrimes { 8551592Srgrimes $$ = FORM_N; 8561592Srgrimes } 8571592Srgrimes | T 8581592Srgrimes { 8591592Srgrimes $$ = FORM_T; 8601592Srgrimes } 8611592Srgrimes | C 8621592Srgrimes { 8631592Srgrimes $$ = FORM_C; 8641592Srgrimes } 8651592Srgrimes ; 8661592Srgrimes 8671592Srgrimestype_code 8681592Srgrimes : A 8691592Srgrimes { 8701592Srgrimes cmd_type = TYPE_A; 8711592Srgrimes cmd_form = FORM_N; 8721592Srgrimes } 8731592Srgrimes | A SP form_code 8741592Srgrimes { 8751592Srgrimes cmd_type = TYPE_A; 8761592Srgrimes cmd_form = $3; 8771592Srgrimes } 8781592Srgrimes | E 8791592Srgrimes { 8801592Srgrimes cmd_type = TYPE_E; 8811592Srgrimes cmd_form = FORM_N; 8821592Srgrimes } 8831592Srgrimes | E SP form_code 8841592Srgrimes { 8851592Srgrimes cmd_type = TYPE_E; 8861592Srgrimes cmd_form = $3; 8871592Srgrimes } 8881592Srgrimes | I 8891592Srgrimes { 8901592Srgrimes cmd_type = TYPE_I; 8911592Srgrimes } 8921592Srgrimes | L 8931592Srgrimes { 8941592Srgrimes cmd_type = TYPE_L; 8951592Srgrimes cmd_bytesz = NBBY; 8961592Srgrimes } 8971592Srgrimes | L SP byte_size 8981592Srgrimes { 8991592Srgrimes cmd_type = TYPE_L; 9001592Srgrimes cmd_bytesz = $3; 9011592Srgrimes } 9021592Srgrimes /* this is for a bug in the BBN ftp */ 9031592Srgrimes | L byte_size 9041592Srgrimes { 9051592Srgrimes cmd_type = TYPE_L; 9061592Srgrimes cmd_bytesz = $2; 9071592Srgrimes } 9081592Srgrimes ; 9091592Srgrimes 9101592Srgrimesstruct_code 9111592Srgrimes : F 9121592Srgrimes { 9131592Srgrimes $$ = STRU_F; 9141592Srgrimes } 9151592Srgrimes | R 9161592Srgrimes { 9171592Srgrimes $$ = STRU_R; 9181592Srgrimes } 9191592Srgrimes | P 9201592Srgrimes { 9211592Srgrimes $$ = STRU_P; 9221592Srgrimes } 9231592Srgrimes ; 9241592Srgrimes 9251592Srgrimesmode_code 9261592Srgrimes : S 9271592Srgrimes { 9281592Srgrimes $$ = MODE_S; 9291592Srgrimes } 9301592Srgrimes | B 9311592Srgrimes { 9321592Srgrimes $$ = MODE_B; 9331592Srgrimes } 9341592Srgrimes | C 9351592Srgrimes { 9361592Srgrimes $$ = MODE_C; 9371592Srgrimes } 9381592Srgrimes ; 9391592Srgrimes 9401592Srgrimespathname 9411592Srgrimes : pathstring 9421592Srgrimes { 9431592Srgrimes /* 9441592Srgrimes * Problem: this production is used for all pathname 9451592Srgrimes * processing, but only gives a 550 error reply. 9461592Srgrimes * This is a valid reply in some cases but not in others. 9471592Srgrimes */ 94875567Speter if (logged_in && $1) { 9491592Srgrimes glob_t gl; 9501592Srgrimes int flags = 9511592Srgrimes GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 9521592Srgrimes 9531592Srgrimes memset(&gl, 0, sizeof(gl)); 95475560Sjedgar flags |= GLOB_MAXPATH; 95575560Sjedgar gl.gl_matchc = MAXGLOBARGS; 9561592Srgrimes if (glob($1, flags, NULL, &gl) || 9571592Srgrimes gl.gl_pathc == 0) { 9581592Srgrimes reply(550, "not found"); 9591592Srgrimes $$ = NULL; 96075567Speter } else if (gl.gl_pathc > 1) { 96175567Speter reply(550, "ambiguous"); 96275567Speter $$ = NULL; 9631592Srgrimes } else { 9641592Srgrimes $$ = strdup(gl.gl_pathv[0]); 9651592Srgrimes } 9661592Srgrimes globfree(&gl); 9671592Srgrimes free($1); 9681592Srgrimes } else 9691592Srgrimes $$ = $1; 9701592Srgrimes } 9711592Srgrimes ; 9721592Srgrimes 9731592Srgrimespathstring 9741592Srgrimes : STRING 9751592Srgrimes ; 9761592Srgrimes 9771592Srgrimesoctal_number 9781592Srgrimes : NUMBER 9791592Srgrimes { 9801592Srgrimes int ret, dec, multby, digit; 9811592Srgrimes 9821592Srgrimes /* 9831592Srgrimes * Convert a number that was read as decimal number 9841592Srgrimes * to what it would be if it had been read as octal. 9851592Srgrimes */ 9861592Srgrimes dec = $1; 9871592Srgrimes multby = 1; 9881592Srgrimes ret = 0; 9891592Srgrimes while (dec) { 9901592Srgrimes digit = dec%10; 9911592Srgrimes if (digit > 7) { 9921592Srgrimes ret = -1; 9931592Srgrimes break; 9941592Srgrimes } 9951592Srgrimes ret += digit * multby; 9961592Srgrimes multby *= 8; 9971592Srgrimes dec /= 10; 9981592Srgrimes } 9991592Srgrimes $$ = ret; 10001592Srgrimes } 10011592Srgrimes ; 10021592Srgrimes 10031592Srgrimes 10041592Srgrimescheck_login 10051592Srgrimes : /* empty */ 10061592Srgrimes { 100770102Sphk $$ = check_login1(); 10081592Srgrimes } 10091592Srgrimes ; 10101592Srgrimes 101170102Sphkcheck_login_epsv 101270102Sphk : /* empty */ 101370102Sphk { 101470102Sphk if (noepsv) { 101570102Sphk reply(500, "EPSV command disabled"); 101670102Sphk $$ = 0; 101770102Sphk } 101870102Sphk else 101970102Sphk $$ = check_login1(); 102070102Sphk } 102170102Sphk ; 102270102Sphk 102370102Sphkcheck_login_ro 102470102Sphk : /* empty */ 102570102Sphk { 102670102Sphk if (readonly) { 102772710Sdes reply(550, "Permission denied."); 102870102Sphk $$ = 0; 102970102Sphk } 103070102Sphk else 103170102Sphk $$ = check_login1(); 103270102Sphk } 103370102Sphk ; 103470102Sphk 10351592Srgrimes%% 10361592Srgrimes 10371592Srgrimesextern jmp_buf errcatch; 10381592Srgrimes 10391592Srgrimes#define CMD 0 /* beginning of command */ 10401592Srgrimes#define ARGS 1 /* expect miscellaneous arguments */ 10411592Srgrimes#define STR1 2 /* expect SP followed by STRING */ 10421592Srgrimes#define STR2 3 /* expect STRING */ 10431592Srgrimes#define OSTR 4 /* optional SP then STRING */ 104475556Sgreen#define ZSTR1 5 /* optional SP then optional STRING */ 10451592Srgrimes#define ZSTR2 6 /* optional STRING after SP */ 10461592Srgrimes#define SITECMD 7 /* SITE command */ 10471592Srgrimes#define NSTR 8 /* Number followed by a string */ 10481592Srgrimes 104975560Sjedgar#define MAXGLOBARGS 1000 105075560Sjedgar 10511592Srgrimesstruct tab { 10521592Srgrimes char *name; 10531592Srgrimes short token; 10541592Srgrimes short state; 10551592Srgrimes short implemented; /* 1 if command is implemented */ 10561592Srgrimes char *help; 10571592Srgrimes}; 10581592Srgrimes 10591592Srgrimesstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 10601592Srgrimes { "USER", USER, STR1, 1, "<sp> username" }, 106175556Sgreen { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" }, 10621592Srgrimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 10631592Srgrimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 10641592Srgrimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 10651592Srgrimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 10661592Srgrimes { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 106756668Sshin { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 106856668Sshin { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 10691592Srgrimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 107056668Sshin { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 107156668Sshin { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 10721592Srgrimes { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 10731592Srgrimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 10741592Srgrimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 10751592Srgrimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 10761592Srgrimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 10771592Srgrimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 10781592Srgrimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 10791592Srgrimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 10801592Srgrimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 10811592Srgrimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 10821592Srgrimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 10831592Srgrimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 10841592Srgrimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 10851592Srgrimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 10861592Srgrimes { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 10871592Srgrimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 10881592Srgrimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 10891592Srgrimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 10901592Srgrimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 10911592Srgrimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 10921592Srgrimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 10931592Srgrimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 10941592Srgrimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 10951592Srgrimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 10961592Srgrimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 10971592Srgrimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 10981592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 10991592Srgrimes { "NOOP", NOOP, ARGS, 1, "" }, 11001592Srgrimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 11011592Srgrimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 11021592Srgrimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 11031592Srgrimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 11041592Srgrimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 11051592Srgrimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 11061592Srgrimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11071592Srgrimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11081592Srgrimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 11091592Srgrimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 11101592Srgrimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 11111592Srgrimes { NULL, 0, 0, 0, 0 } 11121592Srgrimes}; 11131592Srgrimes 11141592Srgrimesstruct tab sitetab[] = { 111575535Sphk { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" }, 11161592Srgrimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 11171592Srgrimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 11181592Srgrimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 11191592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11201592Srgrimes { NULL, 0, 0, 0, 0 } 11211592Srgrimes}; 11221592Srgrimes 11231592Srgrimesstatic char *copy __P((char *)); 11241592Srgrimesstatic void help __P((struct tab *, char *)); 11251592Srgrimesstatic struct tab * 11261592Srgrimes lookup __P((struct tab *, char *)); 112756668Sshinstatic int port_check __P((const char *)); 112856668Sshinstatic int port_check_v6 __P((const char *)); 11291592Srgrimesstatic void sizecmd __P((char *)); 11301592Srgrimesstatic void toolong __P((int)); 113156668Sshinstatic void v4map_data_dest __P((void)); 11321592Srgrimesstatic int yylex __P((void)); 11331592Srgrimes 11341592Srgrimesstatic struct tab * 11351592Srgrimeslookup(p, cmd) 11361592Srgrimes struct tab *p; 11371592Srgrimes char *cmd; 11381592Srgrimes{ 11391592Srgrimes 11401592Srgrimes for (; p->name != NULL; p++) 11411592Srgrimes if (strcmp(cmd, p->name) == 0) 11421592Srgrimes return (p); 11431592Srgrimes return (0); 11441592Srgrimes} 11451592Srgrimes 11461592Srgrimes#include <arpa/telnet.h> 11471592Srgrimes 11481592Srgrimes/* 11491592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes. 11501592Srgrimes */ 11511592Srgrimeschar * 11521592Srgrimesgetline(s, n, iop) 11531592Srgrimes char *s; 11541592Srgrimes int n; 11551592Srgrimes FILE *iop; 11561592Srgrimes{ 11571592Srgrimes int c; 11581592Srgrimes register char *cs; 11591592Srgrimes 11601592Srgrimes cs = s; 11611592Srgrimes/* tmpline may contain saved command from urgent mode interruption */ 11621592Srgrimes for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 11631592Srgrimes *cs++ = tmpline[c]; 11641592Srgrimes if (tmpline[c] == '\n') { 11651592Srgrimes *cs++ = '\0'; 116676096Smarkm if (ftpdebug) 11671592Srgrimes syslog(LOG_DEBUG, "command: %s", s); 11681592Srgrimes tmpline[0] = '\0'; 11691592Srgrimes return(s); 11701592Srgrimes } 11711592Srgrimes if (c == 0) 11721592Srgrimes tmpline[0] = '\0'; 11731592Srgrimes } 11741592Srgrimes while ((c = getc(iop)) != EOF) { 11751592Srgrimes c &= 0377; 11761592Srgrimes if (c == IAC) { 11771592Srgrimes if ((c = getc(iop)) != EOF) { 11781592Srgrimes c &= 0377; 11791592Srgrimes switch (c) { 11801592Srgrimes case WILL: 11811592Srgrimes case WONT: 11821592Srgrimes c = getc(iop); 11831592Srgrimes printf("%c%c%c", IAC, DONT, 0377&c); 11841592Srgrimes (void) fflush(stdout); 11851592Srgrimes continue; 11861592Srgrimes case DO: 11871592Srgrimes case DONT: 11881592Srgrimes c = getc(iop); 11891592Srgrimes printf("%c%c%c", IAC, WONT, 0377&c); 11901592Srgrimes (void) fflush(stdout); 11911592Srgrimes continue; 11921592Srgrimes case IAC: 11931592Srgrimes break; 11941592Srgrimes default: 11951592Srgrimes continue; /* ignore command */ 11961592Srgrimes } 11971592Srgrimes } 11981592Srgrimes } 11991592Srgrimes *cs++ = c; 12001592Srgrimes if (--n <= 0 || c == '\n') 12011592Srgrimes break; 12021592Srgrimes } 12031592Srgrimes if (c == EOF && cs == s) 12041592Srgrimes return (NULL); 12051592Srgrimes *cs++ = '\0'; 120676096Smarkm if (ftpdebug) { 12071592Srgrimes if (!guest && strncasecmp("pass ", s, 5) == 0) { 12081592Srgrimes /* Don't syslog passwords */ 12091592Srgrimes syslog(LOG_DEBUG, "command: %.5s ???", s); 12101592Srgrimes } else { 12111592Srgrimes register char *cp; 12121592Srgrimes register int len; 12131592Srgrimes 12141592Srgrimes /* Don't syslog trailing CR-LF */ 12151592Srgrimes len = strlen(s); 12161592Srgrimes cp = s + len - 1; 12171592Srgrimes while (cp >= s && (*cp == '\n' || *cp == '\r')) { 12181592Srgrimes --cp; 12191592Srgrimes --len; 12201592Srgrimes } 12211592Srgrimes syslog(LOG_DEBUG, "command: %.*s", len, s); 12221592Srgrimes } 12231592Srgrimes } 12241592Srgrimes return (s); 12251592Srgrimes} 12261592Srgrimes 12271592Srgrimesstatic void 12281592Srgrimestoolong(signo) 12291592Srgrimes int signo; 12301592Srgrimes{ 12311592Srgrimes 12321592Srgrimes reply(421, 12331592Srgrimes "Timeout (%d seconds): closing control connection.", timeout); 12341592Srgrimes if (logging) 12351592Srgrimes syslog(LOG_INFO, "User %s timed out after %d seconds", 12361592Srgrimes (pw ? pw -> pw_name : "unknown"), timeout); 12371592Srgrimes dologout(1); 12381592Srgrimes} 12391592Srgrimes 12401592Srgrimesstatic int 12411592Srgrimesyylex() 12421592Srgrimes{ 12431592Srgrimes static int cpos, state; 12441592Srgrimes char *cp, *cp2; 12451592Srgrimes struct tab *p; 12461592Srgrimes int n; 12471592Srgrimes char c; 12481592Srgrimes 12491592Srgrimes for (;;) { 12501592Srgrimes switch (state) { 12511592Srgrimes 12521592Srgrimes case CMD: 12531592Srgrimes (void) signal(SIGALRM, toolong); 12541592Srgrimes (void) alarm((unsigned) timeout); 12551592Srgrimes if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 12561592Srgrimes reply(221, "You could at least say goodbye."); 12571592Srgrimes dologout(0); 12581592Srgrimes } 12591592Srgrimes (void) alarm(0); 12601592Srgrimes#ifdef SETPROCTITLE 126129574Sphk if (strncasecmp(cbuf, "PASS", 4) != 0) 12621592Srgrimes setproctitle("%s: %s", proctitle, cbuf); 12631592Srgrimes#endif /* SETPROCTITLE */ 12641592Srgrimes if ((cp = strchr(cbuf, '\r'))) { 12651592Srgrimes *cp++ = '\n'; 12661592Srgrimes *cp = '\0'; 12671592Srgrimes } 12681592Srgrimes if ((cp = strpbrk(cbuf, " \n"))) 12691592Srgrimes cpos = cp - cbuf; 12701592Srgrimes if (cpos == 0) 12711592Srgrimes cpos = 4; 12721592Srgrimes c = cbuf[cpos]; 12731592Srgrimes cbuf[cpos] = '\0'; 12741592Srgrimes upper(cbuf); 12751592Srgrimes p = lookup(cmdtab, cbuf); 12761592Srgrimes cbuf[cpos] = c; 12773776Spst if (p != 0) { 12781592Srgrimes if (p->implemented == 0) { 12791592Srgrimes nack(p->name); 12801592Srgrimes longjmp(errcatch,0); 12811592Srgrimes /* NOTREACHED */ 12821592Srgrimes } 12831592Srgrimes state = p->state; 12841592Srgrimes yylval.s = p->name; 12851592Srgrimes return (p->token); 12861592Srgrimes } 12871592Srgrimes break; 12881592Srgrimes 12891592Srgrimes case SITECMD: 12901592Srgrimes if (cbuf[cpos] == ' ') { 12911592Srgrimes cpos++; 12921592Srgrimes return (SP); 12931592Srgrimes } 12941592Srgrimes cp = &cbuf[cpos]; 12951592Srgrimes if ((cp2 = strpbrk(cp, " \n"))) 12961592Srgrimes cpos = cp2 - cbuf; 12971592Srgrimes c = cbuf[cpos]; 12981592Srgrimes cbuf[cpos] = '\0'; 12991592Srgrimes upper(cp); 13001592Srgrimes p = lookup(sitetab, cp); 13011592Srgrimes cbuf[cpos] = c; 13023777Spst if (guest == 0 && p != 0) { 13031592Srgrimes if (p->implemented == 0) { 13041592Srgrimes state = CMD; 13051592Srgrimes nack(p->name); 13061592Srgrimes longjmp(errcatch,0); 13071592Srgrimes /* NOTREACHED */ 13081592Srgrimes } 13091592Srgrimes state = p->state; 13101592Srgrimes yylval.s = p->name; 13111592Srgrimes return (p->token); 13121592Srgrimes } 13131592Srgrimes state = CMD; 13141592Srgrimes break; 13151592Srgrimes 131675556Sgreen case ZSTR1: 13171592Srgrimes case OSTR: 13181592Srgrimes if (cbuf[cpos] == '\n') { 13191592Srgrimes state = CMD; 13201592Srgrimes return (CRLF); 13211592Srgrimes } 13221592Srgrimes /* FALLTHROUGH */ 13231592Srgrimes 13241592Srgrimes case STR1: 13251592Srgrimes dostr1: 13261592Srgrimes if (cbuf[cpos] == ' ') { 13271592Srgrimes cpos++; 132851979Salfred state = state == OSTR ? STR2 : state+1; 13291592Srgrimes return (SP); 13301592Srgrimes } 13311592Srgrimes break; 13321592Srgrimes 13331592Srgrimes case ZSTR2: 13341592Srgrimes if (cbuf[cpos] == '\n') { 13351592Srgrimes state = CMD; 13361592Srgrimes return (CRLF); 13371592Srgrimes } 13381592Srgrimes /* FALLTHROUGH */ 13391592Srgrimes 13401592Srgrimes case STR2: 13411592Srgrimes cp = &cbuf[cpos]; 13421592Srgrimes n = strlen(cp); 13431592Srgrimes cpos += n - 1; 13441592Srgrimes /* 13451592Srgrimes * Make sure the string is nonempty and \n terminated. 13461592Srgrimes */ 13471592Srgrimes if (n > 1 && cbuf[cpos] == '\n') { 13481592Srgrimes cbuf[cpos] = '\0'; 13491592Srgrimes yylval.s = copy(cp); 13501592Srgrimes cbuf[cpos] = '\n'; 13511592Srgrimes state = ARGS; 13521592Srgrimes return (STRING); 13531592Srgrimes } 13541592Srgrimes break; 13551592Srgrimes 13561592Srgrimes case NSTR: 13571592Srgrimes if (cbuf[cpos] == ' ') { 13581592Srgrimes cpos++; 13591592Srgrimes return (SP); 13601592Srgrimes } 13611592Srgrimes if (isdigit(cbuf[cpos])) { 13621592Srgrimes cp = &cbuf[cpos]; 13631592Srgrimes while (isdigit(cbuf[++cpos])) 13641592Srgrimes ; 13651592Srgrimes c = cbuf[cpos]; 13661592Srgrimes cbuf[cpos] = '\0'; 13671592Srgrimes yylval.i = atoi(cp); 13681592Srgrimes cbuf[cpos] = c; 13691592Srgrimes state = STR1; 13701592Srgrimes return (NUMBER); 13711592Srgrimes } 13721592Srgrimes state = STR1; 13731592Srgrimes goto dostr1; 13741592Srgrimes 13751592Srgrimes case ARGS: 13761592Srgrimes if (isdigit(cbuf[cpos])) { 13771592Srgrimes cp = &cbuf[cpos]; 13781592Srgrimes while (isdigit(cbuf[++cpos])) 13791592Srgrimes ; 13801592Srgrimes c = cbuf[cpos]; 13811592Srgrimes cbuf[cpos] = '\0'; 13821592Srgrimes yylval.i = atoi(cp); 13831592Srgrimes cbuf[cpos] = c; 13841592Srgrimes return (NUMBER); 13851592Srgrimes } 138656668Sshin if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 138756668Sshin && !isalnum(cbuf[cpos + 3])) { 138856668Sshin cpos += 3; 138956668Sshin return ALL; 139056668Sshin } 13911592Srgrimes switch (cbuf[cpos++]) { 13921592Srgrimes 13931592Srgrimes case '\n': 13941592Srgrimes state = CMD; 13951592Srgrimes return (CRLF); 13961592Srgrimes 13971592Srgrimes case ' ': 13981592Srgrimes return (SP); 13991592Srgrimes 14001592Srgrimes case ',': 14011592Srgrimes return (COMMA); 14021592Srgrimes 14031592Srgrimes case 'A': 14041592Srgrimes case 'a': 14051592Srgrimes return (A); 14061592Srgrimes 14071592Srgrimes case 'B': 14081592Srgrimes case 'b': 14091592Srgrimes return (B); 14101592Srgrimes 14111592Srgrimes case 'C': 14121592Srgrimes case 'c': 14131592Srgrimes return (C); 14141592Srgrimes 14151592Srgrimes case 'E': 14161592Srgrimes case 'e': 14171592Srgrimes return (E); 14181592Srgrimes 14191592Srgrimes case 'F': 14201592Srgrimes case 'f': 14211592Srgrimes return (F); 14221592Srgrimes 14231592Srgrimes case 'I': 14241592Srgrimes case 'i': 14251592Srgrimes return (I); 14261592Srgrimes 14271592Srgrimes case 'L': 14281592Srgrimes case 'l': 14291592Srgrimes return (L); 14301592Srgrimes 14311592Srgrimes case 'N': 14321592Srgrimes case 'n': 14331592Srgrimes return (N); 14341592Srgrimes 14351592Srgrimes case 'P': 14361592Srgrimes case 'p': 14371592Srgrimes return (P); 14381592Srgrimes 14391592Srgrimes case 'R': 14401592Srgrimes case 'r': 14411592Srgrimes return (R); 14421592Srgrimes 14431592Srgrimes case 'S': 14441592Srgrimes case 's': 14451592Srgrimes return (S); 14461592Srgrimes 14471592Srgrimes case 'T': 14481592Srgrimes case 't': 14491592Srgrimes return (T); 14501592Srgrimes 14511592Srgrimes } 14521592Srgrimes break; 14531592Srgrimes 14541592Srgrimes default: 145576096Smarkm fatalerror("Unknown state in scanner."); 14561592Srgrimes } 14571592Srgrimes yyerror((char *) 0); 14581592Srgrimes state = CMD; 14591592Srgrimes longjmp(errcatch,0); 14601592Srgrimes } 14611592Srgrimes} 14621592Srgrimes 14631592Srgrimesvoid 14641592Srgrimesupper(s) 14651592Srgrimes char *s; 14661592Srgrimes{ 14671592Srgrimes while (*s != '\0') { 14681592Srgrimes if (islower(*s)) 14691592Srgrimes *s = toupper(*s); 14701592Srgrimes s++; 14711592Srgrimes } 14721592Srgrimes} 14731592Srgrimes 14741592Srgrimesstatic char * 14751592Srgrimescopy(s) 14761592Srgrimes char *s; 14771592Srgrimes{ 14781592Srgrimes char *p; 14791592Srgrimes 14801592Srgrimes p = malloc((unsigned) strlen(s) + 1); 14811592Srgrimes if (p == NULL) 148276096Smarkm fatalerror("Ran out of memory."); 14831592Srgrimes (void) strcpy(p, s); 14841592Srgrimes return (p); 14851592Srgrimes} 14861592Srgrimes 14871592Srgrimesstatic void 14881592Srgrimeshelp(ctab, s) 14891592Srgrimes struct tab *ctab; 14901592Srgrimes char *s; 14911592Srgrimes{ 14921592Srgrimes struct tab *c; 14931592Srgrimes int width, NCMDS; 14941592Srgrimes char *type; 14951592Srgrimes 14961592Srgrimes if (ctab == sitetab) 14971592Srgrimes type = "SITE "; 14981592Srgrimes else 14991592Srgrimes type = ""; 15001592Srgrimes width = 0, NCMDS = 0; 15011592Srgrimes for (c = ctab; c->name != NULL; c++) { 15021592Srgrimes int len = strlen(c->name); 15031592Srgrimes 15041592Srgrimes if (len > width) 15051592Srgrimes width = len; 15061592Srgrimes NCMDS++; 15071592Srgrimes } 15081592Srgrimes width = (width + 8) &~ 7; 15091592Srgrimes if (s == 0) { 15101592Srgrimes int i, j, w; 15111592Srgrimes int columns, lines; 15121592Srgrimes 15131592Srgrimes lreply(214, "The following %scommands are recognized %s.", 15141592Srgrimes type, "(* =>'s unimplemented)"); 15151592Srgrimes columns = 76 / width; 15161592Srgrimes if (columns == 0) 15171592Srgrimes columns = 1; 15181592Srgrimes lines = (NCMDS + columns - 1) / columns; 15191592Srgrimes for (i = 0; i < lines; i++) { 15201592Srgrimes printf(" "); 15211592Srgrimes for (j = 0; j < columns; j++) { 15221592Srgrimes c = ctab + j * lines + i; 15231592Srgrimes printf("%s%c", c->name, 15241592Srgrimes c->implemented ? ' ' : '*'); 15251592Srgrimes if (c + lines >= &ctab[NCMDS]) 15261592Srgrimes break; 15271592Srgrimes w = strlen(c->name) + 1; 15281592Srgrimes while (w < width) { 15291592Srgrimes putchar(' '); 15301592Srgrimes w++; 15311592Srgrimes } 15321592Srgrimes } 15331592Srgrimes printf("\r\n"); 15341592Srgrimes } 15351592Srgrimes (void) fflush(stdout); 15361592Srgrimes reply(214, "Direct comments to ftp-bugs@%s.", hostname); 15371592Srgrimes return; 15381592Srgrimes } 15391592Srgrimes upper(s); 15401592Srgrimes c = lookup(ctab, s); 15411592Srgrimes if (c == (struct tab *)0) { 15421592Srgrimes reply(502, "Unknown command %s.", s); 15431592Srgrimes return; 15441592Srgrimes } 15451592Srgrimes if (c->implemented) 15461592Srgrimes reply(214, "Syntax: %s%s %s", type, c->name, c->help); 15471592Srgrimes else 15481592Srgrimes reply(214, "%s%-*s\t%s; unimplemented.", type, width, 15491592Srgrimes c->name, c->help); 15501592Srgrimes} 15511592Srgrimes 15521592Srgrimesstatic void 15531592Srgrimessizecmd(filename) 15541592Srgrimes char *filename; 15551592Srgrimes{ 15561592Srgrimes switch (type) { 15571592Srgrimes case TYPE_L: 15581592Srgrimes case TYPE_I: { 15591592Srgrimes struct stat stbuf; 156063350Sdes if (stat(filename, &stbuf) < 0) 156163350Sdes perror_reply(550, filename); 156263350Sdes else if (!S_ISREG(stbuf.st_mode)) 15631592Srgrimes reply(550, "%s: not a plain file.", filename); 15641592Srgrimes else 15651592Srgrimes reply(213, "%qu", stbuf.st_size); 15661592Srgrimes break; } 15671592Srgrimes case TYPE_A: { 15681592Srgrimes FILE *fin; 15691592Srgrimes int c; 15701592Srgrimes off_t count; 15711592Srgrimes struct stat stbuf; 15721592Srgrimes fin = fopen(filename, "r"); 15731592Srgrimes if (fin == NULL) { 15741592Srgrimes perror_reply(550, filename); 15751592Srgrimes return; 15761592Srgrimes } 157763350Sdes if (fstat(fileno(fin), &stbuf) < 0) { 157863350Sdes perror_reply(550, filename); 157963350Sdes (void) fclose(fin); 158063350Sdes return; 158163350Sdes } else if (!S_ISREG(stbuf.st_mode)) { 15821592Srgrimes reply(550, "%s: not a plain file.", filename); 15831592Srgrimes (void) fclose(fin); 15841592Srgrimes return; 15851592Srgrimes } 15861592Srgrimes 15871592Srgrimes count = 0; 15881592Srgrimes while((c=getc(fin)) != EOF) { 15891592Srgrimes if (c == '\n') /* will get expanded to \r\n */ 15901592Srgrimes count++; 15911592Srgrimes count++; 15921592Srgrimes } 15931592Srgrimes (void) fclose(fin); 15941592Srgrimes 15951592Srgrimes reply(213, "%qd", count); 15961592Srgrimes break; } 15971592Srgrimes default: 15981592Srgrimes reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 15991592Srgrimes } 16001592Srgrimes} 160156668Sshin 160256668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 160356668Sshinstatic int 160456668Sshinport_check(pcmd) 160556668Sshin const char *pcmd; 160656668Sshin{ 160756668Sshin if (his_addr.su_family == AF_INET) { 160856668Sshin if (data_dest.su_family != AF_INET) { 160956668Sshin usedefault = 1; 161056668Sshin reply(500, "Invalid address rejected."); 161156668Sshin return 1; 161256668Sshin } 161356668Sshin if (paranoid && 161456668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 161556668Sshin memcmp(&data_dest.su_sin.sin_addr, 161656668Sshin &his_addr.su_sin.sin_addr, 161756668Sshin sizeof(data_dest.su_sin.sin_addr)))) { 161856668Sshin usedefault = 1; 161956668Sshin reply(500, "Illegal PORT range rejected."); 162056668Sshin } else { 162156668Sshin usedefault = 0; 162256668Sshin if (pdata >= 0) { 162356668Sshin (void) close(pdata); 162456668Sshin pdata = -1; 162556668Sshin } 162656668Sshin reply(200, "%s command successful.", pcmd); 162756668Sshin } 162856668Sshin return 1; 162956668Sshin } 163056668Sshin return 0; 163156668Sshin} 163256668Sshin 163370102Sphkstatic int 163470102Sphkcheck_login1() 163570102Sphk{ 163670102Sphk if (logged_in) 163770102Sphk return 1; 163870102Sphk else { 163970102Sphk reply(530, "Please login with USER and PASS."); 164070102Sphk return 0; 164170102Sphk } 164270102Sphk} 164370102Sphk 164456668Sshin#ifdef INET6 164556668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 164656668Sshinstatic int 164756668Sshinport_check_v6(pcmd) 164856668Sshin const char *pcmd; 164956668Sshin{ 165056668Sshin if (his_addr.su_family == AF_INET6) { 165156668Sshin if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 165256668Sshin /* Convert data_dest into v4 mapped sockaddr.*/ 165356668Sshin v4map_data_dest(); 165456668Sshin if (data_dest.su_family != AF_INET6) { 165556668Sshin usedefault = 1; 165656668Sshin reply(500, "Invalid address rejected."); 165756668Sshin return 1; 165856668Sshin } 165956668Sshin if (paranoid && 166056668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 166156668Sshin memcmp(&data_dest.su_sin6.sin6_addr, 166256668Sshin &his_addr.su_sin6.sin6_addr, 166356668Sshin sizeof(data_dest.su_sin6.sin6_addr)))) { 166456668Sshin usedefault = 1; 166556668Sshin reply(500, "Illegal PORT range rejected."); 166656668Sshin } else { 166756668Sshin usedefault = 0; 166856668Sshin if (pdata >= 0) { 166956668Sshin (void) close(pdata); 167056668Sshin pdata = -1; 167156668Sshin } 167256668Sshin reply(200, "%s command successful.", pcmd); 167356668Sshin } 167456668Sshin return 1; 167556668Sshin } 167656668Sshin return 0; 167756668Sshin} 167856668Sshin 167956668Sshinstatic void 168056668Sshinv4map_data_dest() 168156668Sshin{ 168256668Sshin struct in_addr savedaddr; 168356668Sshin int savedport; 168456668Sshin 168556668Sshin if (data_dest.su_family != AF_INET) { 168656668Sshin usedefault = 1; 168756668Sshin reply(500, "Invalid address rejected."); 168856668Sshin return; 168956668Sshin } 169056668Sshin 169156668Sshin savedaddr = data_dest.su_sin.sin_addr; 169256668Sshin savedport = data_dest.su_port; 169356668Sshin 169456668Sshin memset(&data_dest, 0, sizeof(data_dest)); 169556668Sshin data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 169656668Sshin data_dest.su_sin6.sin6_family = AF_INET6; 169756668Sshin data_dest.su_sin6.sin6_port = savedport; 169856668Sshin memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 169956668Sshin memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 170056668Sshin (caddr_t)&savedaddr, sizeof(savedaddr)); 170156668Sshin} 170256668Sshin#endif 1703