ftpcmd.y revision 76096
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 76096 2001-04-28 07:55:19Z markm $"; 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; 961592Srgrimes 971592Srgrimesoff_t restart_point; 981592Srgrimes 991592Srgrimesstatic int cmd_type; 1001592Srgrimesstatic int cmd_form; 1011592Srgrimesstatic int cmd_bytesz; 1021592Srgrimeschar cbuf[512]; 1031592Srgrimeschar *fromname; 1041592Srgrimes 10556668Sshinextern int epsvall; 10656668Sshin 1071592Srgrimes%} 1081592Srgrimes 1091592Srgrimes%union { 1101592Srgrimes int i; 1111592Srgrimes char *s; 1121592Srgrimes} 1131592Srgrimes 1141592Srgrimes%token 1151592Srgrimes A B C E F I 1161592Srgrimes L N P R S T 11756668Sshin ALL 1181592Srgrimes 1191592Srgrimes SP CRLF COMMA 1201592Srgrimes 1211592Srgrimes USER PASS ACCT REIN QUIT PORT 1221592Srgrimes PASV TYPE STRU MODE RETR STOR 1231592Srgrimes APPE MLFL MAIL MSND MSOM MSAM 1241592Srgrimes MRSQ MRCP ALLO REST RNFR RNTO 1251592Srgrimes ABOR DELE CWD LIST NLST SITE 1261592Srgrimes STAT HELP NOOP MKD RMD PWD 1271592Srgrimes CDUP STOU SMNT SYST SIZE MDTM 12856668Sshin LPRT LPSV EPRT EPSV 1291592Srgrimes 13075535Sphk UMASK IDLE CHMOD MDFIVE 1311592Srgrimes 1321592Srgrimes LEXERR 1331592Srgrimes 1341592Srgrimes%token <s> STRING 1351592Srgrimes%token <i> NUMBER 1361592Srgrimes 1371592Srgrimes%type <i> check_login octal_number byte_size 13870102Sphk%type <i> check_login_ro octal_number byte_size 13970102Sphk%type <i> check_login_epsv octal_number byte_size 1401592Srgrimes%type <i> struct_code mode_code type_code form_code 14175567Speter%type <s> pathstring pathname password username 14256668Sshin%type <s> ALL 1431592Srgrimes 1441592Srgrimes%start cmd_list 1451592Srgrimes 1461592Srgrimes%% 1471592Srgrimes 1481592Srgrimescmd_list 1491592Srgrimes : /* empty */ 1501592Srgrimes | cmd_list cmd 1511592Srgrimes { 1521592Srgrimes fromname = (char *) 0; 1531592Srgrimes restart_point = (off_t) 0; 1541592Srgrimes } 1551592Srgrimes | cmd_list rcmd 1561592Srgrimes ; 1571592Srgrimes 1581592Srgrimescmd 1591592Srgrimes : USER SP username CRLF 1601592Srgrimes { 1611592Srgrimes user($3); 1621592Srgrimes free($3); 1631592Srgrimes } 1641592Srgrimes | PASS SP password CRLF 1651592Srgrimes { 1661592Srgrimes pass($3); 1671592Srgrimes free($3); 1681592Srgrimes } 16975556Sgreen | PASS CRLF 17075556Sgreen { 17175556Sgreen pass(""); 17275556Sgreen } 17317433Spst | PORT check_login SP host_port CRLF 1741592Srgrimes { 17556668Sshin if (epsvall) { 17656668Sshin reply(501, "no PORT allowed after EPSV ALL"); 17756668Sshin goto port_done; 17856668Sshin } 17956668Sshin if (!$2) 18056668Sshin goto port_done; 18156668Sshin if (port_check("PORT") == 1) 18256668Sshin goto port_done; 18356668Sshin#ifdef INET6 18456668Sshin if ((his_addr.su_family != AF_INET6 || 18556668Sshin !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { 18656668Sshin /* shoud never happen */ 18756668Sshin usedefault = 1; 18856668Sshin reply(500, "Invalid address rejected."); 18956668Sshin goto port_done; 19056668Sshin } 19156668Sshin port_check_v6("pcmd"); 19256668Sshin#endif 19356668Sshin port_done: 19456668Sshin } 19556668Sshin | LPRT check_login SP host_long_port CRLF 19656668Sshin { 19756668Sshin if (epsvall) { 19856668Sshin reply(501, "no LPRT allowed after EPSV ALL"); 19956668Sshin goto lprt_done; 20056668Sshin } 20156668Sshin if (!$2) 20256668Sshin goto lprt_done; 20356668Sshin if (port_check("LPRT") == 1) 20456668Sshin goto lprt_done; 20556668Sshin#ifdef INET6 20656668Sshin if (his_addr.su_family != AF_INET6) { 20756668Sshin usedefault = 1; 20856668Sshin reply(500, "Invalid address rejected."); 20956668Sshin goto lprt_done; 21056668Sshin } 21156668Sshin if (port_check_v6("LPRT") == 1) 21256668Sshin goto lprt_done; 21356668Sshin#endif 21456668Sshin lprt_done: 21556668Sshin } 21656668Sshin | EPRT check_login SP STRING CRLF 21756668Sshin { 21856668Sshin char delim; 21956668Sshin char *tmp = NULL; 22056668Sshin char *p, *q; 22156668Sshin char *result[3]; 22256668Sshin struct addrinfo hints; 22356668Sshin struct addrinfo *res; 22456668Sshin int i; 22556668Sshin 22656668Sshin if (epsvall) { 22756668Sshin reply(501, "no EPRT allowed after EPSV ALL"); 22856668Sshin goto eprt_done; 22956668Sshin } 23056668Sshin if (!$2) 23156668Sshin goto eprt_done; 23256668Sshin 23356668Sshin memset(&data_dest, 0, sizeof(data_dest)); 23456668Sshin tmp = strdup($4); 23576096Smarkm if (ftpdebug) 23656668Sshin syslog(LOG_DEBUG, "%s", tmp); 23756668Sshin if (!tmp) { 23876096Smarkm fatalerror("not enough core"); 23956668Sshin /*NOTREACHED*/ 24056668Sshin } 24156668Sshin p = tmp; 24256668Sshin delim = p[0]; 24356668Sshin p++; 24456668Sshin memset(result, 0, sizeof(result)); 24556668Sshin for (i = 0; i < 3; i++) { 24656668Sshin q = strchr(p, delim); 24756668Sshin if (!q || *q != delim) { 24856668Sshin parsefail: 24956668Sshin reply(500, 25056668Sshin "Invalid argument, rejected."); 25156668Sshin if (tmp) 25256668Sshin free(tmp); 25317433Spst usedefault = 1; 25456668Sshin goto eprt_done; 25517433Spst } 25656668Sshin *q++ = '\0'; 25756668Sshin result[i] = p; 25876096Smarkm if (ftpdebug) 25956668Sshin syslog(LOG_DEBUG, "%d: %s", i, p); 26056668Sshin p = q; 2611592Srgrimes } 26256668Sshin 26356668Sshin /* some more sanity check */ 26456668Sshin p = result[0]; 26556668Sshin while (*p) { 26656668Sshin if (!isdigit(*p)) 26756668Sshin goto parsefail; 26856668Sshin p++; 26956668Sshin } 27056668Sshin p = result[2]; 27156668Sshin while (*p) { 27256668Sshin if (!isdigit(*p)) 27356668Sshin goto parsefail; 27456668Sshin p++; 27556668Sshin } 27656668Sshin 27756668Sshin /* grab address */ 27856668Sshin memset(&hints, 0, sizeof(hints)); 27956668Sshin if (atoi(result[0]) == 1) 28056668Sshin hints.ai_family = PF_INET; 28156668Sshin#ifdef INET6 28256668Sshin else if (atoi(result[0]) == 2) 28356668Sshin hints.ai_family = PF_INET6; 28456668Sshin#endif 28556668Sshin else 28656668Sshin hints.ai_family = PF_UNSPEC; /*XXX*/ 28756668Sshin hints.ai_socktype = SOCK_STREAM; 28856668Sshin i = getaddrinfo(result[1], result[2], &hints, &res); 28956668Sshin if (i) 29056668Sshin goto parsefail; 29156668Sshin memcpy(&data_dest, res->ai_addr, res->ai_addrlen); 29256668Sshin#ifdef INET6 29356668Sshin if (his_addr.su_family == AF_INET6 29456668Sshin && data_dest.su_family == AF_INET6) { 29556668Sshin /* XXX more sanity checks! */ 29656668Sshin data_dest.su_sin6.sin6_scope_id = 29756668Sshin his_addr.su_sin6.sin6_scope_id; 29856668Sshin } 29956668Sshin#endif 30056668Sshin free(tmp); 30156668Sshin tmp = NULL; 30256668Sshin 30356668Sshin if (port_check("EPRT") == 1) 30456668Sshin goto eprt_done; 30556668Sshin#ifdef INET6 30656668Sshin if (his_addr.su_family != AF_INET6) { 30756668Sshin usedefault = 1; 30856668Sshin reply(500, "Invalid address rejected."); 30956668Sshin goto eprt_done; 31056668Sshin } 31156668Sshin if (port_check_v6("EPRT") == 1) 31256668Sshin goto eprt_done; 31356668Sshin#endif 31456668Sshin eprt_done:; 3151592Srgrimes } 31617433Spst | PASV check_login CRLF 3171592Srgrimes { 31856668Sshin if (epsvall) 31956668Sshin reply(501, "no PASV allowed after EPSV ALL"); 32056668Sshin else if ($2) 32117433Spst passive(); 3221592Srgrimes } 32356668Sshin | LPSV check_login CRLF 32456668Sshin { 32556668Sshin if (epsvall) 32656668Sshin reply(501, "no LPSV allowed after EPSV ALL"); 32756668Sshin else if ($2) 32856668Sshin long_passive("LPSV", PF_UNSPEC); 32956668Sshin } 33070102Sphk | EPSV check_login_epsv SP NUMBER CRLF 33156668Sshin { 33256668Sshin if ($2) { 33356668Sshin int pf; 33456668Sshin switch ($4) { 33556668Sshin case 1: 33656668Sshin pf = PF_INET; 33756668Sshin break; 33856668Sshin#ifdef INET6 33956668Sshin case 2: 34056668Sshin pf = PF_INET6; 34156668Sshin break; 34256668Sshin#endif 34356668Sshin default: 34456668Sshin pf = -1; /*junk value*/ 34556668Sshin break; 34656668Sshin } 34756668Sshin long_passive("EPSV", pf); 34856668Sshin } 34956668Sshin } 35070102Sphk | EPSV check_login_epsv SP ALL CRLF 35156668Sshin { 35256668Sshin if ($2) { 35356668Sshin reply(200, 35456668Sshin "EPSV ALL command successful."); 35556668Sshin epsvall++; 35656668Sshin } 35756668Sshin } 35870102Sphk | EPSV check_login_epsv CRLF 35956668Sshin { 36056668Sshin if ($2) 36156668Sshin long_passive("EPSV", PF_UNSPEC); 36256668Sshin } 36371278Sjedgar | TYPE check_login SP type_code CRLF 3641592Srgrimes { 36571278Sjedgar if ($2) { 36671278Sjedgar switch (cmd_type) { 3671592Srgrimes 36871278Sjedgar case TYPE_A: 36971278Sjedgar if (cmd_form == FORM_N) { 37071278Sjedgar reply(200, "Type set to A."); 37171278Sjedgar type = cmd_type; 37271278Sjedgar form = cmd_form; 37371278Sjedgar } else 37471278Sjedgar reply(504, "Form must be N."); 37571278Sjedgar break; 3761592Srgrimes 37771278Sjedgar case TYPE_E: 37871278Sjedgar reply(504, "Type E not implemented."); 37971278Sjedgar break; 3801592Srgrimes 38171278Sjedgar case TYPE_I: 38271278Sjedgar reply(200, "Type set to I."); 38371278Sjedgar type = cmd_type; 38471278Sjedgar break; 3851592Srgrimes 38671278Sjedgar case TYPE_L: 3871592Srgrimes#if NBBY == 8 38871278Sjedgar if (cmd_bytesz == 8) { 38971278Sjedgar reply(200, 39071278Sjedgar "Type set to L (byte size 8)."); 39171278Sjedgar type = cmd_type; 39271278Sjedgar } else 39371278Sjedgar reply(504, "Byte size must be 8."); 3941592Srgrimes#else /* NBBY == 8 */ 39571278Sjedgar UNIMPLEMENTED for NBBY != 8 3961592Srgrimes#endif /* NBBY == 8 */ 39771278Sjedgar } 3981592Srgrimes } 3991592Srgrimes } 40071278Sjedgar | STRU check_login SP struct_code CRLF 4011592Srgrimes { 40271278Sjedgar if ($2) { 40371278Sjedgar switch ($4) { 4041592Srgrimes 40571278Sjedgar case STRU_F: 40671278Sjedgar reply(200, "STRU F ok."); 40771278Sjedgar break; 4081592Srgrimes 40971278Sjedgar default: 41071278Sjedgar reply(504, "Unimplemented STRU type."); 41171278Sjedgar } 4121592Srgrimes } 4131592Srgrimes } 41471278Sjedgar | MODE check_login SP mode_code CRLF 4151592Srgrimes { 41671278Sjedgar if ($2) { 41771278Sjedgar switch ($4) { 4181592Srgrimes 41971278Sjedgar case MODE_S: 42071278Sjedgar reply(200, "MODE S ok."); 42171278Sjedgar break; 42271278Sjedgar 42371278Sjedgar default: 42471278Sjedgar reply(502, "Unimplemented MODE type."); 42571278Sjedgar } 4261592Srgrimes } 4271592Srgrimes } 42871278Sjedgar | ALLO check_login SP NUMBER CRLF 4291592Srgrimes { 43071278Sjedgar if ($2) { 43171278Sjedgar reply(202, "ALLO command ignored."); 43271278Sjedgar } 4331592Srgrimes } 43471278Sjedgar | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 4351592Srgrimes { 43671278Sjedgar if ($2) { 43771278Sjedgar reply(202, "ALLO command ignored."); 43871278Sjedgar } 4391592Srgrimes } 4401592Srgrimes | RETR check_login SP pathname CRLF 4411592Srgrimes { 4421592Srgrimes if ($2 && $4 != NULL) 4431592Srgrimes retrieve((char *) 0, $4); 4441592Srgrimes if ($4 != NULL) 4451592Srgrimes free($4); 4461592Srgrimes } 44770102Sphk | STOR check_login_ro SP pathname CRLF 4481592Srgrimes { 4491592Srgrimes if ($2 && $4 != NULL) 4501592Srgrimes store($4, "w", 0); 4511592Srgrimes if ($4 != NULL) 4521592Srgrimes free($4); 4531592Srgrimes } 45470102Sphk | APPE check_login_ro SP pathname CRLF 4551592Srgrimes { 4561592Srgrimes if ($2 && $4 != NULL) 4571592Srgrimes store($4, "a", 0); 4581592Srgrimes if ($4 != NULL) 4591592Srgrimes free($4); 4601592Srgrimes } 4611592Srgrimes | NLST check_login CRLF 4621592Srgrimes { 4631592Srgrimes if ($2) 4641592Srgrimes send_file_list("."); 4651592Srgrimes } 4661592Srgrimes | NLST check_login SP STRING CRLF 4671592Srgrimes { 4681592Srgrimes if ($2 && $4 != NULL) 4691592Srgrimes send_file_list($4); 4701592Srgrimes if ($4 != NULL) 4711592Srgrimes free($4); 4721592Srgrimes } 4731592Srgrimes | LIST check_login CRLF 4741592Srgrimes { 4751592Srgrimes if ($2) 4761592Srgrimes retrieve("/bin/ls -lgA", ""); 4771592Srgrimes } 47875567Speter | LIST check_login SP pathstring CRLF 4791592Srgrimes { 4801592Srgrimes if ($2 && $4 != NULL) 4811592Srgrimes retrieve("/bin/ls -lgA %s", $4); 4821592Srgrimes if ($4 != NULL) 4831592Srgrimes free($4); 4841592Srgrimes } 4851592Srgrimes | STAT check_login SP pathname CRLF 4861592Srgrimes { 4871592Srgrimes if ($2 && $4 != NULL) 4881592Srgrimes statfilecmd($4); 4891592Srgrimes if ($4 != NULL) 4901592Srgrimes free($4); 4911592Srgrimes } 49271278Sjedgar | STAT check_login CRLF 4931592Srgrimes { 49471278Sjedgar if ($2) { 49571278Sjedgar statcmd(); 49671278Sjedgar } 4971592Srgrimes } 49870102Sphk | DELE check_login_ro SP pathname CRLF 4991592Srgrimes { 5001592Srgrimes if ($2 && $4 != NULL) 5011592Srgrimes delete($4); 5021592Srgrimes if ($4 != NULL) 5031592Srgrimes free($4); 5041592Srgrimes } 50570102Sphk | RNTO check_login_ro SP pathname CRLF 5061592Srgrimes { 50717433Spst if ($2) { 50817433Spst if (fromname) { 50917433Spst renamecmd(fromname, $4); 51017433Spst free(fromname); 51117433Spst fromname = (char *) 0; 51217433Spst } else { 51317433Spst reply(503, "Bad sequence of commands."); 51417433Spst } 5151592Srgrimes } 51617433Spst free($4); 5171592Srgrimes } 51871278Sjedgar | ABOR check_login CRLF 5191592Srgrimes { 52071278Sjedgar if ($2) 52171278Sjedgar reply(225, "ABOR command successful."); 5221592Srgrimes } 5231592Srgrimes | CWD check_login CRLF 5241592Srgrimes { 52569234Sdanny if ($2) { 52669234Sdanny if (guest) 52769234Sdanny cwd("/"); 52869234Sdanny else 52969234Sdanny cwd(pw->pw_dir); 53069234Sdanny } 5311592Srgrimes } 5321592Srgrimes | CWD check_login SP pathname CRLF 5331592Srgrimes { 5341592Srgrimes if ($2 && $4 != NULL) 5351592Srgrimes cwd($4); 5361592Srgrimes if ($4 != NULL) 5371592Srgrimes free($4); 5381592Srgrimes } 5391592Srgrimes | HELP CRLF 5401592Srgrimes { 5411592Srgrimes help(cmdtab, (char *) 0); 5421592Srgrimes } 5431592Srgrimes | HELP SP STRING CRLF 5441592Srgrimes { 5451592Srgrimes char *cp = $3; 5461592Srgrimes 5471592Srgrimes if (strncasecmp(cp, "SITE", 4) == 0) { 5481592Srgrimes cp = $3 + 4; 5491592Srgrimes if (*cp == ' ') 5501592Srgrimes cp++; 5511592Srgrimes if (*cp) 5521592Srgrimes help(sitetab, cp); 5531592Srgrimes else 5541592Srgrimes help(sitetab, (char *) 0); 5551592Srgrimes } else 5561592Srgrimes help(cmdtab, $3); 5571592Srgrimes } 5581592Srgrimes | NOOP CRLF 5591592Srgrimes { 5601592Srgrimes reply(200, "NOOP command successful."); 5611592Srgrimes } 56270102Sphk | MKD check_login_ro SP pathname CRLF 5631592Srgrimes { 5641592Srgrimes if ($2 && $4 != NULL) 5651592Srgrimes makedir($4); 5661592Srgrimes if ($4 != NULL) 5671592Srgrimes free($4); 5681592Srgrimes } 56970102Sphk | RMD check_login_ro SP pathname CRLF 5701592Srgrimes { 5711592Srgrimes if ($2 && $4 != NULL) 5721592Srgrimes removedir($4); 5731592Srgrimes if ($4 != NULL) 5741592Srgrimes free($4); 5751592Srgrimes } 5761592Srgrimes | PWD check_login CRLF 5771592Srgrimes { 5781592Srgrimes if ($2) 5791592Srgrimes pwd(); 5801592Srgrimes } 5811592Srgrimes | CDUP check_login CRLF 5821592Srgrimes { 5831592Srgrimes if ($2) 5841592Srgrimes cwd(".."); 5851592Srgrimes } 5861592Srgrimes | SITE SP HELP CRLF 5871592Srgrimes { 5881592Srgrimes help(sitetab, (char *) 0); 5891592Srgrimes } 5901592Srgrimes | SITE SP HELP SP STRING CRLF 5911592Srgrimes { 5921592Srgrimes help(sitetab, $5); 5931592Srgrimes } 59475535Sphk | SITE SP MDFIVE check_login SP pathname CRLF 59575535Sphk { 59675535Sphk char p[64], *q; 59775535Sphk 59875535Sphk if ($4) { 59975535Sphk q = MD5File($6, p); 60075535Sphk if (q != NULL) 60175535Sphk reply(200, "MD5(%s) = %s", $6, p); 60275535Sphk else 60375535Sphk perror_reply(550, $6); 60475535Sphk } 60575535Sphk } 6061592Srgrimes | SITE SP UMASK check_login CRLF 6071592Srgrimes { 6081592Srgrimes int oldmask; 6091592Srgrimes 6101592Srgrimes if ($4) { 6111592Srgrimes oldmask = umask(0); 6121592Srgrimes (void) umask(oldmask); 6131592Srgrimes reply(200, "Current UMASK is %03o", oldmask); 6141592Srgrimes } 6151592Srgrimes } 6161592Srgrimes | SITE SP UMASK check_login SP octal_number CRLF 6171592Srgrimes { 6181592Srgrimes int oldmask; 6191592Srgrimes 6201592Srgrimes if ($4) { 6211592Srgrimes if (($6 == -1) || ($6 > 0777)) { 6221592Srgrimes reply(501, "Bad UMASK value"); 6231592Srgrimes } else { 6241592Srgrimes oldmask = umask($6); 6251592Srgrimes reply(200, 6261592Srgrimes "UMASK set to %03o (was %03o)", 6271592Srgrimes $6, oldmask); 6281592Srgrimes } 6291592Srgrimes } 6301592Srgrimes } 63170102Sphk | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF 6321592Srgrimes { 6331592Srgrimes if ($4 && ($8 != NULL)) { 6341592Srgrimes if ($6 > 0777) 6351592Srgrimes reply(501, 6361592Srgrimes "CHMOD: Mode value must be between 0 and 0777"); 6371592Srgrimes else if (chmod($8, $6) < 0) 6381592Srgrimes perror_reply(550, $8); 6391592Srgrimes else 6401592Srgrimes reply(200, "CHMOD command successful."); 6411592Srgrimes } 6421592Srgrimes if ($8 != NULL) 6431592Srgrimes free($8); 6441592Srgrimes } 64571278Sjedgar | SITE SP check_login IDLE CRLF 6461592Srgrimes { 64771278Sjedgar if ($3) 64871278Sjedgar reply(200, 64971278Sjedgar "Current IDLE time limit is %d seconds; max %d", 65071278Sjedgar timeout, maxtimeout); 6511592Srgrimes } 65271278Sjedgar | SITE SP check_login IDLE SP NUMBER CRLF 6531592Srgrimes { 65471278Sjedgar if ($3) { 65571278Sjedgar if ($6 < 30 || $6 > maxtimeout) { 65671278Sjedgar reply(501, 65771278Sjedgar "Maximum IDLE time must be between 30 and %d seconds", 65871278Sjedgar maxtimeout); 65971278Sjedgar } else { 66071278Sjedgar timeout = $6; 66171278Sjedgar (void) alarm((unsigned) timeout); 66271278Sjedgar reply(200, 66371278Sjedgar "Maximum IDLE time set to %d seconds", 66471278Sjedgar timeout); 66571278Sjedgar } 6661592Srgrimes } 6671592Srgrimes } 66870102Sphk | STOU check_login_ro SP pathname CRLF 6691592Srgrimes { 6701592Srgrimes if ($2 && $4 != NULL) 6711592Srgrimes store($4, "w", 1); 6721592Srgrimes if ($4 != NULL) 6731592Srgrimes free($4); 6741592Srgrimes } 67571278Sjedgar | SYST check_login CRLF 6761592Srgrimes { 67771278Sjedgar if ($2) 6781592Srgrimes#ifdef unix 6791592Srgrimes#ifdef BSD 6801592Srgrimes reply(215, "UNIX Type: L%d Version: BSD-%d", 6811592Srgrimes NBBY, BSD); 6821592Srgrimes#else /* BSD */ 6831592Srgrimes reply(215, "UNIX Type: L%d", NBBY); 6841592Srgrimes#endif /* BSD */ 6851592Srgrimes#else /* unix */ 6861592Srgrimes reply(215, "UNKNOWN Type: L%d", NBBY); 6871592Srgrimes#endif /* unix */ 6881592Srgrimes } 6891592Srgrimes 6901592Srgrimes /* 6911592Srgrimes * SIZE is not in RFC959, but Postel has blessed it and 6921592Srgrimes * it will be in the updated RFC. 6931592Srgrimes * 6941592Srgrimes * Return size of file in a format suitable for 6951592Srgrimes * using with RESTART (we just count bytes). 6961592Srgrimes */ 6971592Srgrimes | SIZE check_login SP pathname CRLF 6981592Srgrimes { 6991592Srgrimes if ($2 && $4 != NULL) 7001592Srgrimes sizecmd($4); 7011592Srgrimes if ($4 != NULL) 7021592Srgrimes free($4); 7031592Srgrimes } 7041592Srgrimes 7051592Srgrimes /* 7061592Srgrimes * MDTM is not in RFC959, but Postel has blessed it and 7071592Srgrimes * it will be in the updated RFC. 7081592Srgrimes * 7091592Srgrimes * Return modification time of file as an ISO 3307 7101592Srgrimes * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 7111592Srgrimes * where xxx is the fractional second (of any precision, 7121592Srgrimes * not necessarily 3 digits) 7131592Srgrimes */ 7141592Srgrimes | MDTM check_login SP pathname CRLF 7151592Srgrimes { 7161592Srgrimes if ($2 && $4 != NULL) { 7171592Srgrimes struct stat stbuf; 7181592Srgrimes if (stat($4, &stbuf) < 0) 7191592Srgrimes reply(550, "%s: %s", 7201592Srgrimes $4, strerror(errno)); 7211592Srgrimes else if (!S_ISREG(stbuf.st_mode)) { 7221592Srgrimes reply(550, "%s: not a plain file.", $4); 7231592Srgrimes } else { 7241592Srgrimes struct tm *t; 7251592Srgrimes t = gmtime(&stbuf.st_mtime); 7261592Srgrimes reply(213, 72717435Spst "%04d%02d%02d%02d%02d%02d", 72817435Spst 1900 + t->tm_year, 72917435Spst t->tm_mon+1, t->tm_mday, 7301592Srgrimes t->tm_hour, t->tm_min, t->tm_sec); 7311592Srgrimes } 7321592Srgrimes } 7331592Srgrimes if ($4 != NULL) 7341592Srgrimes free($4); 7351592Srgrimes } 7361592Srgrimes | QUIT CRLF 7371592Srgrimes { 7381592Srgrimes reply(221, "Goodbye."); 7391592Srgrimes dologout(0); 7401592Srgrimes } 7411592Srgrimes | error CRLF 7421592Srgrimes { 7431592Srgrimes yyerrok; 7441592Srgrimes } 7451592Srgrimes ; 7461592Srgrimesrcmd 74770102Sphk : RNFR check_login_ro SP pathname CRLF 7481592Srgrimes { 7491592Srgrimes char *renamefrom(); 7501592Srgrimes 7511592Srgrimes restart_point = (off_t) 0; 7521592Srgrimes if ($2 && $4) { 7531592Srgrimes fromname = renamefrom($4); 7541592Srgrimes if (fromname == (char *) 0 && $4) { 7551592Srgrimes free($4); 7561592Srgrimes } 7571592Srgrimes } 7581592Srgrimes } 75971278Sjedgar | REST check_login SP byte_size CRLF 7601592Srgrimes { 76171278Sjedgar if ($2) { 76271278Sjedgar fromname = (char *) 0; 76371278Sjedgar restart_point = $4; /* XXX $4 is only "int" */ 76471278Sjedgar reply(350, "Restarting at %qd. %s", 76571278Sjedgar restart_point, 76671278Sjedgar "Send STORE or RETRIEVE to initiate transfer."); 76771278Sjedgar } 7681592Srgrimes } 7691592Srgrimes ; 7701592Srgrimes 7711592Srgrimesusername 7721592Srgrimes : STRING 7731592Srgrimes ; 7741592Srgrimes 7751592Srgrimespassword 7761592Srgrimes : /* empty */ 7771592Srgrimes { 7781592Srgrimes $$ = (char *)calloc(1, sizeof(char)); 7791592Srgrimes } 7801592Srgrimes | STRING 7811592Srgrimes ; 7821592Srgrimes 7831592Srgrimesbyte_size 7841592Srgrimes : NUMBER 7851592Srgrimes ; 7861592Srgrimes 7871592Srgrimeshost_port 7881592Srgrimes : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 7891592Srgrimes NUMBER COMMA NUMBER 7901592Srgrimes { 7911592Srgrimes char *a, *p; 7921592Srgrimes 79356668Sshin data_dest.su_len = sizeof(struct sockaddr_in); 79456668Sshin data_dest.su_family = AF_INET; 79556668Sshin p = (char *)&data_dest.su_sin.sin_port; 79617435Spst p[0] = $9; p[1] = $11; 79756668Sshin a = (char *)&data_dest.su_sin.sin_addr; 7981592Srgrimes a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 7991592Srgrimes } 8001592Srgrimes ; 8011592Srgrimes 80256668Sshinhost_long_port 80356668Sshin : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 80456668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 80556668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 80656668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 80756668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 80856668Sshin NUMBER 80956668Sshin { 81056668Sshin char *a, *p; 81156668Sshin 81256668Sshin memset(&data_dest, 0, sizeof(data_dest)); 81356668Sshin data_dest.su_len = sizeof(struct sockaddr_in6); 81456668Sshin data_dest.su_family = AF_INET6; 81556668Sshin p = (char *)&data_dest.su_port; 81656668Sshin p[0] = $39; p[1] = $41; 81756668Sshin a = (char *)&data_dest.su_sin6.sin6_addr; 81856668Sshin a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 81956668Sshin a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19; 82056668Sshin a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27; 82156668Sshin a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35; 82256668Sshin if (his_addr.su_family == AF_INET6) { 82356668Sshin /* XXX more sanity checks! */ 82456668Sshin data_dest.su_sin6.sin6_scope_id = 82556668Sshin his_addr.su_sin6.sin6_scope_id; 82656668Sshin } 82756668Sshin if ($1 != 6 || $3 != 16 || $37 != 2) 82856668Sshin memset(&data_dest, 0, sizeof(data_dest)); 82956668Sshin } 83056668Sshin | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83156668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 83256668Sshin NUMBER 83356668Sshin { 83456668Sshin char *a, *p; 83556668Sshin 83656668Sshin memset(&data_dest, 0, sizeof(data_dest)); 83756668Sshin data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 83856668Sshin data_dest.su_family = AF_INET; 83956668Sshin p = (char *)&data_dest.su_port; 84056668Sshin p[0] = $15; p[1] = $17; 84156668Sshin a = (char *)&data_dest.su_sin.sin_addr; 84256668Sshin a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 84356668Sshin if ($1 != 4 || $3 != 4 || $13 != 2) 84456668Sshin memset(&data_dest, 0, sizeof(data_dest)); 84556668Sshin } 84656668Sshin ; 84756668Sshin 8481592Srgrimesform_code 8491592Srgrimes : N 8501592Srgrimes { 8511592Srgrimes $$ = FORM_N; 8521592Srgrimes } 8531592Srgrimes | T 8541592Srgrimes { 8551592Srgrimes $$ = FORM_T; 8561592Srgrimes } 8571592Srgrimes | C 8581592Srgrimes { 8591592Srgrimes $$ = FORM_C; 8601592Srgrimes } 8611592Srgrimes ; 8621592Srgrimes 8631592Srgrimestype_code 8641592Srgrimes : A 8651592Srgrimes { 8661592Srgrimes cmd_type = TYPE_A; 8671592Srgrimes cmd_form = FORM_N; 8681592Srgrimes } 8691592Srgrimes | A SP form_code 8701592Srgrimes { 8711592Srgrimes cmd_type = TYPE_A; 8721592Srgrimes cmd_form = $3; 8731592Srgrimes } 8741592Srgrimes | E 8751592Srgrimes { 8761592Srgrimes cmd_type = TYPE_E; 8771592Srgrimes cmd_form = FORM_N; 8781592Srgrimes } 8791592Srgrimes | E SP form_code 8801592Srgrimes { 8811592Srgrimes cmd_type = TYPE_E; 8821592Srgrimes cmd_form = $3; 8831592Srgrimes } 8841592Srgrimes | I 8851592Srgrimes { 8861592Srgrimes cmd_type = TYPE_I; 8871592Srgrimes } 8881592Srgrimes | L 8891592Srgrimes { 8901592Srgrimes cmd_type = TYPE_L; 8911592Srgrimes cmd_bytesz = NBBY; 8921592Srgrimes } 8931592Srgrimes | L SP byte_size 8941592Srgrimes { 8951592Srgrimes cmd_type = TYPE_L; 8961592Srgrimes cmd_bytesz = $3; 8971592Srgrimes } 8981592Srgrimes /* this is for a bug in the BBN ftp */ 8991592Srgrimes | L byte_size 9001592Srgrimes { 9011592Srgrimes cmd_type = TYPE_L; 9021592Srgrimes cmd_bytesz = $2; 9031592Srgrimes } 9041592Srgrimes ; 9051592Srgrimes 9061592Srgrimesstruct_code 9071592Srgrimes : F 9081592Srgrimes { 9091592Srgrimes $$ = STRU_F; 9101592Srgrimes } 9111592Srgrimes | R 9121592Srgrimes { 9131592Srgrimes $$ = STRU_R; 9141592Srgrimes } 9151592Srgrimes | P 9161592Srgrimes { 9171592Srgrimes $$ = STRU_P; 9181592Srgrimes } 9191592Srgrimes ; 9201592Srgrimes 9211592Srgrimesmode_code 9221592Srgrimes : S 9231592Srgrimes { 9241592Srgrimes $$ = MODE_S; 9251592Srgrimes } 9261592Srgrimes | B 9271592Srgrimes { 9281592Srgrimes $$ = MODE_B; 9291592Srgrimes } 9301592Srgrimes | C 9311592Srgrimes { 9321592Srgrimes $$ = MODE_C; 9331592Srgrimes } 9341592Srgrimes ; 9351592Srgrimes 9361592Srgrimespathname 9371592Srgrimes : pathstring 9381592Srgrimes { 9391592Srgrimes /* 9401592Srgrimes * Problem: this production is used for all pathname 9411592Srgrimes * processing, but only gives a 550 error reply. 9421592Srgrimes * This is a valid reply in some cases but not in others. 9431592Srgrimes */ 94475567Speter if (logged_in && $1) { 9451592Srgrimes glob_t gl; 9461592Srgrimes int flags = 9471592Srgrimes GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 9481592Srgrimes 9491592Srgrimes memset(&gl, 0, sizeof(gl)); 95075560Sjedgar flags |= GLOB_MAXPATH; 95175560Sjedgar gl.gl_matchc = MAXGLOBARGS; 9521592Srgrimes if (glob($1, flags, NULL, &gl) || 9531592Srgrimes gl.gl_pathc == 0) { 9541592Srgrimes reply(550, "not found"); 9551592Srgrimes $$ = NULL; 95675567Speter } else if (gl.gl_pathc > 1) { 95775567Speter reply(550, "ambiguous"); 95875567Speter $$ = NULL; 9591592Srgrimes } else { 9601592Srgrimes $$ = strdup(gl.gl_pathv[0]); 9611592Srgrimes } 9621592Srgrimes globfree(&gl); 9631592Srgrimes free($1); 9641592Srgrimes } else 9651592Srgrimes $$ = $1; 9661592Srgrimes } 9671592Srgrimes ; 9681592Srgrimes 9691592Srgrimespathstring 9701592Srgrimes : STRING 9711592Srgrimes ; 9721592Srgrimes 9731592Srgrimesoctal_number 9741592Srgrimes : NUMBER 9751592Srgrimes { 9761592Srgrimes int ret, dec, multby, digit; 9771592Srgrimes 9781592Srgrimes /* 9791592Srgrimes * Convert a number that was read as decimal number 9801592Srgrimes * to what it would be if it had been read as octal. 9811592Srgrimes */ 9821592Srgrimes dec = $1; 9831592Srgrimes multby = 1; 9841592Srgrimes ret = 0; 9851592Srgrimes while (dec) { 9861592Srgrimes digit = dec%10; 9871592Srgrimes if (digit > 7) { 9881592Srgrimes ret = -1; 9891592Srgrimes break; 9901592Srgrimes } 9911592Srgrimes ret += digit * multby; 9921592Srgrimes multby *= 8; 9931592Srgrimes dec /= 10; 9941592Srgrimes } 9951592Srgrimes $$ = ret; 9961592Srgrimes } 9971592Srgrimes ; 9981592Srgrimes 9991592Srgrimes 10001592Srgrimescheck_login 10011592Srgrimes : /* empty */ 10021592Srgrimes { 100370102Sphk $$ = check_login1(); 10041592Srgrimes } 10051592Srgrimes ; 10061592Srgrimes 100770102Sphkcheck_login_epsv 100870102Sphk : /* empty */ 100970102Sphk { 101070102Sphk if (noepsv) { 101170102Sphk reply(500, "EPSV command disabled"); 101270102Sphk $$ = 0; 101370102Sphk } 101470102Sphk else 101570102Sphk $$ = check_login1(); 101670102Sphk } 101770102Sphk ; 101870102Sphk 101970102Sphkcheck_login_ro 102070102Sphk : /* empty */ 102170102Sphk { 102270102Sphk if (readonly) { 102372710Sdes reply(550, "Permission denied."); 102470102Sphk $$ = 0; 102570102Sphk } 102670102Sphk else 102770102Sphk $$ = check_login1(); 102870102Sphk } 102970102Sphk ; 103070102Sphk 10311592Srgrimes%% 10321592Srgrimes 10331592Srgrimesextern jmp_buf errcatch; 10341592Srgrimes 10351592Srgrimes#define CMD 0 /* beginning of command */ 10361592Srgrimes#define ARGS 1 /* expect miscellaneous arguments */ 10371592Srgrimes#define STR1 2 /* expect SP followed by STRING */ 10381592Srgrimes#define STR2 3 /* expect STRING */ 10391592Srgrimes#define OSTR 4 /* optional SP then STRING */ 104075556Sgreen#define ZSTR1 5 /* optional SP then optional STRING */ 10411592Srgrimes#define ZSTR2 6 /* optional STRING after SP */ 10421592Srgrimes#define SITECMD 7 /* SITE command */ 10431592Srgrimes#define NSTR 8 /* Number followed by a string */ 10441592Srgrimes 104575560Sjedgar#define MAXGLOBARGS 1000 104675560Sjedgar 10471592Srgrimesstruct tab { 10481592Srgrimes char *name; 10491592Srgrimes short token; 10501592Srgrimes short state; 10511592Srgrimes short implemented; /* 1 if command is implemented */ 10521592Srgrimes char *help; 10531592Srgrimes}; 10541592Srgrimes 10551592Srgrimesstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 10561592Srgrimes { "USER", USER, STR1, 1, "<sp> username" }, 105775556Sgreen { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" }, 10581592Srgrimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 10591592Srgrimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 10601592Srgrimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 10611592Srgrimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 10621592Srgrimes { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 106356668Sshin { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 106456668Sshin { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 10651592Srgrimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 106656668Sshin { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 106756668Sshin { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 10681592Srgrimes { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 10691592Srgrimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 10701592Srgrimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 10711592Srgrimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 10721592Srgrimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 10731592Srgrimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 10741592Srgrimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 10751592Srgrimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 10761592Srgrimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 10771592Srgrimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 10781592Srgrimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 10791592Srgrimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 10801592Srgrimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 10811592Srgrimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 10821592Srgrimes { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 10831592Srgrimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 10841592Srgrimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 10851592Srgrimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 10861592Srgrimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 10871592Srgrimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 10881592Srgrimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 10891592Srgrimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 10901592Srgrimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 10911592Srgrimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 10921592Srgrimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 10931592Srgrimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 10941592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 10951592Srgrimes { "NOOP", NOOP, ARGS, 1, "" }, 10961592Srgrimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 10971592Srgrimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 10981592Srgrimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 10991592Srgrimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 11001592Srgrimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 11011592Srgrimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 11021592Srgrimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11031592Srgrimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 11041592Srgrimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 11051592Srgrimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 11061592Srgrimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 11071592Srgrimes { NULL, 0, 0, 0, 0 } 11081592Srgrimes}; 11091592Srgrimes 11101592Srgrimesstruct tab sitetab[] = { 111175535Sphk { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" }, 11121592Srgrimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 11131592Srgrimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 11141592Srgrimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 11151592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11161592Srgrimes { NULL, 0, 0, 0, 0 } 11171592Srgrimes}; 11181592Srgrimes 11191592Srgrimesstatic char *copy __P((char *)); 11201592Srgrimesstatic void help __P((struct tab *, char *)); 11211592Srgrimesstatic struct tab * 11221592Srgrimes lookup __P((struct tab *, char *)); 112356668Sshinstatic int port_check __P((const char *)); 112456668Sshinstatic int port_check_v6 __P((const char *)); 11251592Srgrimesstatic void sizecmd __P((char *)); 11261592Srgrimesstatic void toolong __P((int)); 112756668Sshinstatic void v4map_data_dest __P((void)); 11281592Srgrimesstatic int yylex __P((void)); 11291592Srgrimes 11301592Srgrimesstatic struct tab * 11311592Srgrimeslookup(p, cmd) 11321592Srgrimes struct tab *p; 11331592Srgrimes char *cmd; 11341592Srgrimes{ 11351592Srgrimes 11361592Srgrimes for (; p->name != NULL; p++) 11371592Srgrimes if (strcmp(cmd, p->name) == 0) 11381592Srgrimes return (p); 11391592Srgrimes return (0); 11401592Srgrimes} 11411592Srgrimes 11421592Srgrimes#include <arpa/telnet.h> 11431592Srgrimes 11441592Srgrimes/* 11451592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes. 11461592Srgrimes */ 11471592Srgrimeschar * 11481592Srgrimesgetline(s, n, iop) 11491592Srgrimes char *s; 11501592Srgrimes int n; 11511592Srgrimes FILE *iop; 11521592Srgrimes{ 11531592Srgrimes int c; 11541592Srgrimes register char *cs; 11551592Srgrimes 11561592Srgrimes cs = s; 11571592Srgrimes/* tmpline may contain saved command from urgent mode interruption */ 11581592Srgrimes for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 11591592Srgrimes *cs++ = tmpline[c]; 11601592Srgrimes if (tmpline[c] == '\n') { 11611592Srgrimes *cs++ = '\0'; 116276096Smarkm if (ftpdebug) 11631592Srgrimes syslog(LOG_DEBUG, "command: %s", s); 11641592Srgrimes tmpline[0] = '\0'; 11651592Srgrimes return(s); 11661592Srgrimes } 11671592Srgrimes if (c == 0) 11681592Srgrimes tmpline[0] = '\0'; 11691592Srgrimes } 11701592Srgrimes while ((c = getc(iop)) != EOF) { 11711592Srgrimes c &= 0377; 11721592Srgrimes if (c == IAC) { 11731592Srgrimes if ((c = getc(iop)) != EOF) { 11741592Srgrimes c &= 0377; 11751592Srgrimes switch (c) { 11761592Srgrimes case WILL: 11771592Srgrimes case WONT: 11781592Srgrimes c = getc(iop); 11791592Srgrimes printf("%c%c%c", IAC, DONT, 0377&c); 11801592Srgrimes (void) fflush(stdout); 11811592Srgrimes continue; 11821592Srgrimes case DO: 11831592Srgrimes case DONT: 11841592Srgrimes c = getc(iop); 11851592Srgrimes printf("%c%c%c", IAC, WONT, 0377&c); 11861592Srgrimes (void) fflush(stdout); 11871592Srgrimes continue; 11881592Srgrimes case IAC: 11891592Srgrimes break; 11901592Srgrimes default: 11911592Srgrimes continue; /* ignore command */ 11921592Srgrimes } 11931592Srgrimes } 11941592Srgrimes } 11951592Srgrimes *cs++ = c; 11961592Srgrimes if (--n <= 0 || c == '\n') 11971592Srgrimes break; 11981592Srgrimes } 11991592Srgrimes if (c == EOF && cs == s) 12001592Srgrimes return (NULL); 12011592Srgrimes *cs++ = '\0'; 120276096Smarkm if (ftpdebug) { 12031592Srgrimes if (!guest && strncasecmp("pass ", s, 5) == 0) { 12041592Srgrimes /* Don't syslog passwords */ 12051592Srgrimes syslog(LOG_DEBUG, "command: %.5s ???", s); 12061592Srgrimes } else { 12071592Srgrimes register char *cp; 12081592Srgrimes register int len; 12091592Srgrimes 12101592Srgrimes /* Don't syslog trailing CR-LF */ 12111592Srgrimes len = strlen(s); 12121592Srgrimes cp = s + len - 1; 12131592Srgrimes while (cp >= s && (*cp == '\n' || *cp == '\r')) { 12141592Srgrimes --cp; 12151592Srgrimes --len; 12161592Srgrimes } 12171592Srgrimes syslog(LOG_DEBUG, "command: %.*s", len, s); 12181592Srgrimes } 12191592Srgrimes } 12201592Srgrimes return (s); 12211592Srgrimes} 12221592Srgrimes 12231592Srgrimesstatic void 12241592Srgrimestoolong(signo) 12251592Srgrimes int signo; 12261592Srgrimes{ 12271592Srgrimes 12281592Srgrimes reply(421, 12291592Srgrimes "Timeout (%d seconds): closing control connection.", timeout); 12301592Srgrimes if (logging) 12311592Srgrimes syslog(LOG_INFO, "User %s timed out after %d seconds", 12321592Srgrimes (pw ? pw -> pw_name : "unknown"), timeout); 12331592Srgrimes dologout(1); 12341592Srgrimes} 12351592Srgrimes 12361592Srgrimesstatic int 12371592Srgrimesyylex() 12381592Srgrimes{ 12391592Srgrimes static int cpos, state; 12401592Srgrimes char *cp, *cp2; 12411592Srgrimes struct tab *p; 12421592Srgrimes int n; 12431592Srgrimes char c; 12441592Srgrimes 12451592Srgrimes for (;;) { 12461592Srgrimes switch (state) { 12471592Srgrimes 12481592Srgrimes case CMD: 12491592Srgrimes (void) signal(SIGALRM, toolong); 12501592Srgrimes (void) alarm((unsigned) timeout); 12511592Srgrimes if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 12521592Srgrimes reply(221, "You could at least say goodbye."); 12531592Srgrimes dologout(0); 12541592Srgrimes } 12551592Srgrimes (void) alarm(0); 12561592Srgrimes#ifdef SETPROCTITLE 125729574Sphk if (strncasecmp(cbuf, "PASS", 4) != 0) 12581592Srgrimes setproctitle("%s: %s", proctitle, cbuf); 12591592Srgrimes#endif /* SETPROCTITLE */ 12601592Srgrimes if ((cp = strchr(cbuf, '\r'))) { 12611592Srgrimes *cp++ = '\n'; 12621592Srgrimes *cp = '\0'; 12631592Srgrimes } 12641592Srgrimes if ((cp = strpbrk(cbuf, " \n"))) 12651592Srgrimes cpos = cp - cbuf; 12661592Srgrimes if (cpos == 0) 12671592Srgrimes cpos = 4; 12681592Srgrimes c = cbuf[cpos]; 12691592Srgrimes cbuf[cpos] = '\0'; 12701592Srgrimes upper(cbuf); 12711592Srgrimes p = lookup(cmdtab, cbuf); 12721592Srgrimes cbuf[cpos] = c; 12733776Spst if (p != 0) { 12741592Srgrimes if (p->implemented == 0) { 12751592Srgrimes nack(p->name); 12761592Srgrimes longjmp(errcatch,0); 12771592Srgrimes /* NOTREACHED */ 12781592Srgrimes } 12791592Srgrimes state = p->state; 12801592Srgrimes yylval.s = p->name; 12811592Srgrimes return (p->token); 12821592Srgrimes } 12831592Srgrimes break; 12841592Srgrimes 12851592Srgrimes case SITECMD: 12861592Srgrimes if (cbuf[cpos] == ' ') { 12871592Srgrimes cpos++; 12881592Srgrimes return (SP); 12891592Srgrimes } 12901592Srgrimes cp = &cbuf[cpos]; 12911592Srgrimes if ((cp2 = strpbrk(cp, " \n"))) 12921592Srgrimes cpos = cp2 - cbuf; 12931592Srgrimes c = cbuf[cpos]; 12941592Srgrimes cbuf[cpos] = '\0'; 12951592Srgrimes upper(cp); 12961592Srgrimes p = lookup(sitetab, cp); 12971592Srgrimes cbuf[cpos] = c; 12983777Spst if (guest == 0 && p != 0) { 12991592Srgrimes if (p->implemented == 0) { 13001592Srgrimes state = CMD; 13011592Srgrimes nack(p->name); 13021592Srgrimes longjmp(errcatch,0); 13031592Srgrimes /* NOTREACHED */ 13041592Srgrimes } 13051592Srgrimes state = p->state; 13061592Srgrimes yylval.s = p->name; 13071592Srgrimes return (p->token); 13081592Srgrimes } 13091592Srgrimes state = CMD; 13101592Srgrimes break; 13111592Srgrimes 131275556Sgreen case ZSTR1: 13131592Srgrimes case OSTR: 13141592Srgrimes if (cbuf[cpos] == '\n') { 13151592Srgrimes state = CMD; 13161592Srgrimes return (CRLF); 13171592Srgrimes } 13181592Srgrimes /* FALLTHROUGH */ 13191592Srgrimes 13201592Srgrimes case STR1: 13211592Srgrimes dostr1: 13221592Srgrimes if (cbuf[cpos] == ' ') { 13231592Srgrimes cpos++; 132451979Salfred state = state == OSTR ? STR2 : state+1; 13251592Srgrimes return (SP); 13261592Srgrimes } 13271592Srgrimes break; 13281592Srgrimes 13291592Srgrimes case ZSTR2: 13301592Srgrimes if (cbuf[cpos] == '\n') { 13311592Srgrimes state = CMD; 13321592Srgrimes return (CRLF); 13331592Srgrimes } 13341592Srgrimes /* FALLTHROUGH */ 13351592Srgrimes 13361592Srgrimes case STR2: 13371592Srgrimes cp = &cbuf[cpos]; 13381592Srgrimes n = strlen(cp); 13391592Srgrimes cpos += n - 1; 13401592Srgrimes /* 13411592Srgrimes * Make sure the string is nonempty and \n terminated. 13421592Srgrimes */ 13431592Srgrimes if (n > 1 && cbuf[cpos] == '\n') { 13441592Srgrimes cbuf[cpos] = '\0'; 13451592Srgrimes yylval.s = copy(cp); 13461592Srgrimes cbuf[cpos] = '\n'; 13471592Srgrimes state = ARGS; 13481592Srgrimes return (STRING); 13491592Srgrimes } 13501592Srgrimes break; 13511592Srgrimes 13521592Srgrimes case NSTR: 13531592Srgrimes if (cbuf[cpos] == ' ') { 13541592Srgrimes cpos++; 13551592Srgrimes return (SP); 13561592Srgrimes } 13571592Srgrimes if (isdigit(cbuf[cpos])) { 13581592Srgrimes cp = &cbuf[cpos]; 13591592Srgrimes while (isdigit(cbuf[++cpos])) 13601592Srgrimes ; 13611592Srgrimes c = cbuf[cpos]; 13621592Srgrimes cbuf[cpos] = '\0'; 13631592Srgrimes yylval.i = atoi(cp); 13641592Srgrimes cbuf[cpos] = c; 13651592Srgrimes state = STR1; 13661592Srgrimes return (NUMBER); 13671592Srgrimes } 13681592Srgrimes state = STR1; 13691592Srgrimes goto dostr1; 13701592Srgrimes 13711592Srgrimes case ARGS: 13721592Srgrimes if (isdigit(cbuf[cpos])) { 13731592Srgrimes cp = &cbuf[cpos]; 13741592Srgrimes while (isdigit(cbuf[++cpos])) 13751592Srgrimes ; 13761592Srgrimes c = cbuf[cpos]; 13771592Srgrimes cbuf[cpos] = '\0'; 13781592Srgrimes yylval.i = atoi(cp); 13791592Srgrimes cbuf[cpos] = c; 13801592Srgrimes return (NUMBER); 13811592Srgrimes } 138256668Sshin if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 138356668Sshin && !isalnum(cbuf[cpos + 3])) { 138456668Sshin cpos += 3; 138556668Sshin return ALL; 138656668Sshin } 13871592Srgrimes switch (cbuf[cpos++]) { 13881592Srgrimes 13891592Srgrimes case '\n': 13901592Srgrimes state = CMD; 13911592Srgrimes return (CRLF); 13921592Srgrimes 13931592Srgrimes case ' ': 13941592Srgrimes return (SP); 13951592Srgrimes 13961592Srgrimes case ',': 13971592Srgrimes return (COMMA); 13981592Srgrimes 13991592Srgrimes case 'A': 14001592Srgrimes case 'a': 14011592Srgrimes return (A); 14021592Srgrimes 14031592Srgrimes case 'B': 14041592Srgrimes case 'b': 14051592Srgrimes return (B); 14061592Srgrimes 14071592Srgrimes case 'C': 14081592Srgrimes case 'c': 14091592Srgrimes return (C); 14101592Srgrimes 14111592Srgrimes case 'E': 14121592Srgrimes case 'e': 14131592Srgrimes return (E); 14141592Srgrimes 14151592Srgrimes case 'F': 14161592Srgrimes case 'f': 14171592Srgrimes return (F); 14181592Srgrimes 14191592Srgrimes case 'I': 14201592Srgrimes case 'i': 14211592Srgrimes return (I); 14221592Srgrimes 14231592Srgrimes case 'L': 14241592Srgrimes case 'l': 14251592Srgrimes return (L); 14261592Srgrimes 14271592Srgrimes case 'N': 14281592Srgrimes case 'n': 14291592Srgrimes return (N); 14301592Srgrimes 14311592Srgrimes case 'P': 14321592Srgrimes case 'p': 14331592Srgrimes return (P); 14341592Srgrimes 14351592Srgrimes case 'R': 14361592Srgrimes case 'r': 14371592Srgrimes return (R); 14381592Srgrimes 14391592Srgrimes case 'S': 14401592Srgrimes case 's': 14411592Srgrimes return (S); 14421592Srgrimes 14431592Srgrimes case 'T': 14441592Srgrimes case 't': 14451592Srgrimes return (T); 14461592Srgrimes 14471592Srgrimes } 14481592Srgrimes break; 14491592Srgrimes 14501592Srgrimes default: 145176096Smarkm fatalerror("Unknown state in scanner."); 14521592Srgrimes } 14531592Srgrimes yyerror((char *) 0); 14541592Srgrimes state = CMD; 14551592Srgrimes longjmp(errcatch,0); 14561592Srgrimes } 14571592Srgrimes} 14581592Srgrimes 14591592Srgrimesvoid 14601592Srgrimesupper(s) 14611592Srgrimes char *s; 14621592Srgrimes{ 14631592Srgrimes while (*s != '\0') { 14641592Srgrimes if (islower(*s)) 14651592Srgrimes *s = toupper(*s); 14661592Srgrimes s++; 14671592Srgrimes } 14681592Srgrimes} 14691592Srgrimes 14701592Srgrimesstatic char * 14711592Srgrimescopy(s) 14721592Srgrimes char *s; 14731592Srgrimes{ 14741592Srgrimes char *p; 14751592Srgrimes 14761592Srgrimes p = malloc((unsigned) strlen(s) + 1); 14771592Srgrimes if (p == NULL) 147876096Smarkm fatalerror("Ran out of memory."); 14791592Srgrimes (void) strcpy(p, s); 14801592Srgrimes return (p); 14811592Srgrimes} 14821592Srgrimes 14831592Srgrimesstatic void 14841592Srgrimeshelp(ctab, s) 14851592Srgrimes struct tab *ctab; 14861592Srgrimes char *s; 14871592Srgrimes{ 14881592Srgrimes struct tab *c; 14891592Srgrimes int width, NCMDS; 14901592Srgrimes char *type; 14911592Srgrimes 14921592Srgrimes if (ctab == sitetab) 14931592Srgrimes type = "SITE "; 14941592Srgrimes else 14951592Srgrimes type = ""; 14961592Srgrimes width = 0, NCMDS = 0; 14971592Srgrimes for (c = ctab; c->name != NULL; c++) { 14981592Srgrimes int len = strlen(c->name); 14991592Srgrimes 15001592Srgrimes if (len > width) 15011592Srgrimes width = len; 15021592Srgrimes NCMDS++; 15031592Srgrimes } 15041592Srgrimes width = (width + 8) &~ 7; 15051592Srgrimes if (s == 0) { 15061592Srgrimes int i, j, w; 15071592Srgrimes int columns, lines; 15081592Srgrimes 15091592Srgrimes lreply(214, "The following %scommands are recognized %s.", 15101592Srgrimes type, "(* =>'s unimplemented)"); 15111592Srgrimes columns = 76 / width; 15121592Srgrimes if (columns == 0) 15131592Srgrimes columns = 1; 15141592Srgrimes lines = (NCMDS + columns - 1) / columns; 15151592Srgrimes for (i = 0; i < lines; i++) { 15161592Srgrimes printf(" "); 15171592Srgrimes for (j = 0; j < columns; j++) { 15181592Srgrimes c = ctab + j * lines + i; 15191592Srgrimes printf("%s%c", c->name, 15201592Srgrimes c->implemented ? ' ' : '*'); 15211592Srgrimes if (c + lines >= &ctab[NCMDS]) 15221592Srgrimes break; 15231592Srgrimes w = strlen(c->name) + 1; 15241592Srgrimes while (w < width) { 15251592Srgrimes putchar(' '); 15261592Srgrimes w++; 15271592Srgrimes } 15281592Srgrimes } 15291592Srgrimes printf("\r\n"); 15301592Srgrimes } 15311592Srgrimes (void) fflush(stdout); 15321592Srgrimes reply(214, "Direct comments to ftp-bugs@%s.", hostname); 15331592Srgrimes return; 15341592Srgrimes } 15351592Srgrimes upper(s); 15361592Srgrimes c = lookup(ctab, s); 15371592Srgrimes if (c == (struct tab *)0) { 15381592Srgrimes reply(502, "Unknown command %s.", s); 15391592Srgrimes return; 15401592Srgrimes } 15411592Srgrimes if (c->implemented) 15421592Srgrimes reply(214, "Syntax: %s%s %s", type, c->name, c->help); 15431592Srgrimes else 15441592Srgrimes reply(214, "%s%-*s\t%s; unimplemented.", type, width, 15451592Srgrimes c->name, c->help); 15461592Srgrimes} 15471592Srgrimes 15481592Srgrimesstatic void 15491592Srgrimessizecmd(filename) 15501592Srgrimes char *filename; 15511592Srgrimes{ 15521592Srgrimes switch (type) { 15531592Srgrimes case TYPE_L: 15541592Srgrimes case TYPE_I: { 15551592Srgrimes struct stat stbuf; 155663350Sdes if (stat(filename, &stbuf) < 0) 155763350Sdes perror_reply(550, filename); 155863350Sdes else if (!S_ISREG(stbuf.st_mode)) 15591592Srgrimes reply(550, "%s: not a plain file.", filename); 15601592Srgrimes else 15611592Srgrimes reply(213, "%qu", stbuf.st_size); 15621592Srgrimes break; } 15631592Srgrimes case TYPE_A: { 15641592Srgrimes FILE *fin; 15651592Srgrimes int c; 15661592Srgrimes off_t count; 15671592Srgrimes struct stat stbuf; 15681592Srgrimes fin = fopen(filename, "r"); 15691592Srgrimes if (fin == NULL) { 15701592Srgrimes perror_reply(550, filename); 15711592Srgrimes return; 15721592Srgrimes } 157363350Sdes if (fstat(fileno(fin), &stbuf) < 0) { 157463350Sdes perror_reply(550, filename); 157563350Sdes (void) fclose(fin); 157663350Sdes return; 157763350Sdes } else if (!S_ISREG(stbuf.st_mode)) { 15781592Srgrimes reply(550, "%s: not a plain file.", filename); 15791592Srgrimes (void) fclose(fin); 15801592Srgrimes return; 15811592Srgrimes } 15821592Srgrimes 15831592Srgrimes count = 0; 15841592Srgrimes while((c=getc(fin)) != EOF) { 15851592Srgrimes if (c == '\n') /* will get expanded to \r\n */ 15861592Srgrimes count++; 15871592Srgrimes count++; 15881592Srgrimes } 15891592Srgrimes (void) fclose(fin); 15901592Srgrimes 15911592Srgrimes reply(213, "%qd", count); 15921592Srgrimes break; } 15931592Srgrimes default: 15941592Srgrimes reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 15951592Srgrimes } 15961592Srgrimes} 159756668Sshin 159856668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 159956668Sshinstatic int 160056668Sshinport_check(pcmd) 160156668Sshin const char *pcmd; 160256668Sshin{ 160356668Sshin if (his_addr.su_family == AF_INET) { 160456668Sshin if (data_dest.su_family != AF_INET) { 160556668Sshin usedefault = 1; 160656668Sshin reply(500, "Invalid address rejected."); 160756668Sshin return 1; 160856668Sshin } 160956668Sshin if (paranoid && 161056668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 161156668Sshin memcmp(&data_dest.su_sin.sin_addr, 161256668Sshin &his_addr.su_sin.sin_addr, 161356668Sshin sizeof(data_dest.su_sin.sin_addr)))) { 161456668Sshin usedefault = 1; 161556668Sshin reply(500, "Illegal PORT range rejected."); 161656668Sshin } else { 161756668Sshin usedefault = 0; 161856668Sshin if (pdata >= 0) { 161956668Sshin (void) close(pdata); 162056668Sshin pdata = -1; 162156668Sshin } 162256668Sshin reply(200, "%s command successful.", pcmd); 162356668Sshin } 162456668Sshin return 1; 162556668Sshin } 162656668Sshin return 0; 162756668Sshin} 162856668Sshin 162970102Sphkstatic int 163070102Sphkcheck_login1() 163170102Sphk{ 163270102Sphk if (logged_in) 163370102Sphk return 1; 163470102Sphk else { 163570102Sphk reply(530, "Please login with USER and PASS."); 163670102Sphk return 0; 163770102Sphk } 163870102Sphk} 163970102Sphk 164056668Sshin#ifdef INET6 164156668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 164256668Sshinstatic int 164356668Sshinport_check_v6(pcmd) 164456668Sshin const char *pcmd; 164556668Sshin{ 164656668Sshin if (his_addr.su_family == AF_INET6) { 164756668Sshin if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 164856668Sshin /* Convert data_dest into v4 mapped sockaddr.*/ 164956668Sshin v4map_data_dest(); 165056668Sshin if (data_dest.su_family != AF_INET6) { 165156668Sshin usedefault = 1; 165256668Sshin reply(500, "Invalid address rejected."); 165356668Sshin return 1; 165456668Sshin } 165556668Sshin if (paranoid && 165656668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 165756668Sshin memcmp(&data_dest.su_sin6.sin6_addr, 165856668Sshin &his_addr.su_sin6.sin6_addr, 165956668Sshin sizeof(data_dest.su_sin6.sin6_addr)))) { 166056668Sshin usedefault = 1; 166156668Sshin reply(500, "Illegal PORT range rejected."); 166256668Sshin } else { 166356668Sshin usedefault = 0; 166456668Sshin if (pdata >= 0) { 166556668Sshin (void) close(pdata); 166656668Sshin pdata = -1; 166756668Sshin } 166856668Sshin reply(200, "%s command successful.", pcmd); 166956668Sshin } 167056668Sshin return 1; 167156668Sshin } 167256668Sshin return 0; 167356668Sshin} 167456668Sshin 167556668Sshinstatic void 167656668Sshinv4map_data_dest() 167756668Sshin{ 167856668Sshin struct in_addr savedaddr; 167956668Sshin int savedport; 168056668Sshin 168156668Sshin if (data_dest.su_family != AF_INET) { 168256668Sshin usedefault = 1; 168356668Sshin reply(500, "Invalid address rejected."); 168456668Sshin return; 168556668Sshin } 168656668Sshin 168756668Sshin savedaddr = data_dest.su_sin.sin_addr; 168856668Sshin savedport = data_dest.su_port; 168956668Sshin 169056668Sshin memset(&data_dest, 0, sizeof(data_dest)); 169156668Sshin data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 169256668Sshin data_dest.su_sin6.sin6_family = AF_INET6; 169356668Sshin data_dest.su_sin6.sin6_port = savedport; 169456668Sshin memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 169556668Sshin memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 169656668Sshin (caddr_t)&savedaddr, sizeof(savedaddr)); 169756668Sshin} 169856668Sshin#endif 1699