ftpcmd.y revision 71278
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 71278 2001-01-20 01:34:22Z jedgar $"; 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> 721592Srgrimes 731592Srgrimes#include "extern.h" 741592Srgrimes 7556668Sshinextern union sockunion data_dest, his_addr; 761592Srgrimesextern int logged_in; 771592Srgrimesextern struct passwd *pw; 781592Srgrimesextern int guest; 7917435Spstextern int paranoid; 801592Srgrimesextern int logging; 811592Srgrimesextern int type; 821592Srgrimesextern int form; 831592Srgrimesextern int debug; 841592Srgrimesextern int timeout; 851592Srgrimesextern int maxtimeout; 861592Srgrimesextern int pdata; 8727650Sdavidnextern char *hostname; 8827650Sdavidnextern char remotehost[]; 891592Srgrimesextern char proctitle[]; 901592Srgrimesextern int usedefault; 911592Srgrimesextern int transflag; 921592Srgrimesextern char tmpline[]; 9370102Sphkextern int readonly; 9470102Sphkextern int noepsv; 951592Srgrimes 961592Srgrimesoff_t restart_point; 971592Srgrimes 981592Srgrimesstatic int cmd_type; 991592Srgrimesstatic int cmd_form; 1001592Srgrimesstatic int cmd_bytesz; 1011592Srgrimeschar cbuf[512]; 1021592Srgrimeschar *fromname; 1031592Srgrimes 10456668Sshinextern int epsvall; 10556668Sshin 1061592Srgrimes%} 1071592Srgrimes 1081592Srgrimes%union { 1091592Srgrimes int i; 1101592Srgrimes char *s; 1111592Srgrimes} 1121592Srgrimes 1131592Srgrimes%token 1141592Srgrimes A B C E F I 1151592Srgrimes L N P R S T 11656668Sshin ALL 1171592Srgrimes 1181592Srgrimes SP CRLF COMMA 1191592Srgrimes 1201592Srgrimes USER PASS ACCT REIN QUIT PORT 1211592Srgrimes PASV TYPE STRU MODE RETR STOR 1221592Srgrimes APPE MLFL MAIL MSND MSOM MSAM 1231592Srgrimes MRSQ MRCP ALLO REST RNFR RNTO 1241592Srgrimes ABOR DELE CWD LIST NLST SITE 1251592Srgrimes STAT HELP NOOP MKD RMD PWD 1261592Srgrimes CDUP STOU SMNT SYST SIZE MDTM 12756668Sshin LPRT LPSV EPRT EPSV 1281592Srgrimes 1291592Srgrimes UMASK IDLE CHMOD 1301592Srgrimes 1311592Srgrimes LEXERR 1321592Srgrimes 1331592Srgrimes%token <s> STRING 1341592Srgrimes%token <i> NUMBER 1351592Srgrimes 1361592Srgrimes%type <i> check_login octal_number byte_size 13770102Sphk%type <i> check_login_ro octal_number byte_size 13870102Sphk%type <i> check_login_epsv octal_number byte_size 1391592Srgrimes%type <i> struct_code mode_code type_code form_code 14056668Sshin%type <s> pathstring pathname password username ext_arg 14156668Sshin%type <s> ALL 1421592Srgrimes 1431592Srgrimes%start cmd_list 1441592Srgrimes 1451592Srgrimes%% 1461592Srgrimes 1471592Srgrimescmd_list 1481592Srgrimes : /* empty */ 1491592Srgrimes | cmd_list cmd 1501592Srgrimes { 1511592Srgrimes fromname = (char *) 0; 1521592Srgrimes restart_point = (off_t) 0; 1531592Srgrimes } 1541592Srgrimes | cmd_list rcmd 1551592Srgrimes ; 1561592Srgrimes 1571592Srgrimescmd 1581592Srgrimes : USER SP username CRLF 1591592Srgrimes { 1601592Srgrimes user($3); 1611592Srgrimes free($3); 1621592Srgrimes } 1631592Srgrimes | PASS SP password CRLF 1641592Srgrimes { 1651592Srgrimes pass($3); 1661592Srgrimes free($3); 1671592Srgrimes } 16817433Spst | PORT check_login SP host_port CRLF 1691592Srgrimes { 17056668Sshin if (epsvall) { 17156668Sshin reply(501, "no PORT allowed after EPSV ALL"); 17256668Sshin goto port_done; 17356668Sshin } 17456668Sshin if (!$2) 17556668Sshin goto port_done; 17656668Sshin if (port_check("PORT") == 1) 17756668Sshin goto port_done; 17856668Sshin#ifdef INET6 17956668Sshin if ((his_addr.su_family != AF_INET6 || 18056668Sshin !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { 18156668Sshin /* shoud never happen */ 18256668Sshin usedefault = 1; 18356668Sshin reply(500, "Invalid address rejected."); 18456668Sshin goto port_done; 18556668Sshin } 18656668Sshin port_check_v6("pcmd"); 18756668Sshin#endif 18856668Sshin port_done: 18956668Sshin } 19056668Sshin | LPRT check_login SP host_long_port CRLF 19156668Sshin { 19256668Sshin if (epsvall) { 19356668Sshin reply(501, "no LPRT allowed after EPSV ALL"); 19456668Sshin goto lprt_done; 19556668Sshin } 19656668Sshin if (!$2) 19756668Sshin goto lprt_done; 19856668Sshin if (port_check("LPRT") == 1) 19956668Sshin goto lprt_done; 20056668Sshin#ifdef INET6 20156668Sshin if (his_addr.su_family != AF_INET6) { 20256668Sshin usedefault = 1; 20356668Sshin reply(500, "Invalid address rejected."); 20456668Sshin goto lprt_done; 20556668Sshin } 20656668Sshin if (port_check_v6("LPRT") == 1) 20756668Sshin goto lprt_done; 20856668Sshin#endif 20956668Sshin lprt_done: 21056668Sshin } 21156668Sshin | EPRT check_login SP STRING CRLF 21256668Sshin { 21356668Sshin char delim; 21456668Sshin char *tmp = NULL; 21556668Sshin char *p, *q; 21656668Sshin char *result[3]; 21756668Sshin struct addrinfo hints; 21856668Sshin struct addrinfo *res; 21956668Sshin int i; 22056668Sshin 22156668Sshin if (epsvall) { 22256668Sshin reply(501, "no EPRT allowed after EPSV ALL"); 22356668Sshin goto eprt_done; 22456668Sshin } 22556668Sshin if (!$2) 22656668Sshin goto eprt_done; 22756668Sshin 22856668Sshin memset(&data_dest, 0, sizeof(data_dest)); 22956668Sshin tmp = strdup($4); 23056668Sshin if (debug) 23156668Sshin syslog(LOG_DEBUG, "%s", tmp); 23256668Sshin if (!tmp) { 23356668Sshin fatal("not enough core"); 23456668Sshin /*NOTREACHED*/ 23556668Sshin } 23656668Sshin p = tmp; 23756668Sshin delim = p[0]; 23856668Sshin p++; 23956668Sshin memset(result, 0, sizeof(result)); 24056668Sshin for (i = 0; i < 3; i++) { 24156668Sshin q = strchr(p, delim); 24256668Sshin if (!q || *q != delim) { 24356668Sshin parsefail: 24456668Sshin reply(500, 24556668Sshin "Invalid argument, rejected."); 24656668Sshin if (tmp) 24756668Sshin free(tmp); 24817433Spst usedefault = 1; 24956668Sshin goto eprt_done; 25017433Spst } 25156668Sshin *q++ = '\0'; 25256668Sshin result[i] = p; 25356668Sshin if (debug) 25456668Sshin syslog(LOG_DEBUG, "%d: %s", i, p); 25556668Sshin p = q; 2561592Srgrimes } 25756668Sshin 25856668Sshin /* some more sanity check */ 25956668Sshin p = result[0]; 26056668Sshin while (*p) { 26156668Sshin if (!isdigit(*p)) 26256668Sshin goto parsefail; 26356668Sshin p++; 26456668Sshin } 26556668Sshin p = result[2]; 26656668Sshin while (*p) { 26756668Sshin if (!isdigit(*p)) 26856668Sshin goto parsefail; 26956668Sshin p++; 27056668Sshin } 27156668Sshin 27256668Sshin /* grab address */ 27356668Sshin memset(&hints, 0, sizeof(hints)); 27456668Sshin if (atoi(result[0]) == 1) 27556668Sshin hints.ai_family = PF_INET; 27656668Sshin#ifdef INET6 27756668Sshin else if (atoi(result[0]) == 2) 27856668Sshin hints.ai_family = PF_INET6; 27956668Sshin#endif 28056668Sshin else 28156668Sshin hints.ai_family = PF_UNSPEC; /*XXX*/ 28256668Sshin hints.ai_socktype = SOCK_STREAM; 28356668Sshin i = getaddrinfo(result[1], result[2], &hints, &res); 28456668Sshin if (i) 28556668Sshin goto parsefail; 28656668Sshin memcpy(&data_dest, res->ai_addr, res->ai_addrlen); 28756668Sshin#ifdef INET6 28856668Sshin if (his_addr.su_family == AF_INET6 28956668Sshin && data_dest.su_family == AF_INET6) { 29056668Sshin /* XXX more sanity checks! */ 29156668Sshin data_dest.su_sin6.sin6_scope_id = 29256668Sshin his_addr.su_sin6.sin6_scope_id; 29356668Sshin } 29456668Sshin#endif 29556668Sshin free(tmp); 29656668Sshin tmp = NULL; 29756668Sshin 29856668Sshin if (port_check("EPRT") == 1) 29956668Sshin goto eprt_done; 30056668Sshin#ifdef INET6 30156668Sshin if (his_addr.su_family != AF_INET6) { 30256668Sshin usedefault = 1; 30356668Sshin reply(500, "Invalid address rejected."); 30456668Sshin goto eprt_done; 30556668Sshin } 30656668Sshin if (port_check_v6("EPRT") == 1) 30756668Sshin goto eprt_done; 30856668Sshin#endif 30956668Sshin eprt_done:; 3101592Srgrimes } 31117433Spst | PASV check_login CRLF 3121592Srgrimes { 31356668Sshin if (epsvall) 31456668Sshin reply(501, "no PASV allowed after EPSV ALL"); 31556668Sshin else if ($2) 31617433Spst passive(); 3171592Srgrimes } 31856668Sshin | LPSV check_login CRLF 31956668Sshin { 32056668Sshin if (epsvall) 32156668Sshin reply(501, "no LPSV allowed after EPSV ALL"); 32256668Sshin else if ($2) 32356668Sshin long_passive("LPSV", PF_UNSPEC); 32456668Sshin } 32570102Sphk | EPSV check_login_epsv SP NUMBER CRLF 32656668Sshin { 32756668Sshin if ($2) { 32856668Sshin int pf; 32956668Sshin switch ($4) { 33056668Sshin case 1: 33156668Sshin pf = PF_INET; 33256668Sshin break; 33356668Sshin#ifdef INET6 33456668Sshin case 2: 33556668Sshin pf = PF_INET6; 33656668Sshin break; 33756668Sshin#endif 33856668Sshin default: 33956668Sshin pf = -1; /*junk value*/ 34056668Sshin break; 34156668Sshin } 34256668Sshin long_passive("EPSV", pf); 34356668Sshin } 34456668Sshin } 34570102Sphk | EPSV check_login_epsv SP ALL CRLF 34656668Sshin { 34756668Sshin if ($2) { 34856668Sshin reply(200, 34956668Sshin "EPSV ALL command successful."); 35056668Sshin epsvall++; 35156668Sshin } 35256668Sshin } 35370102Sphk | EPSV check_login_epsv CRLF 35456668Sshin { 35556668Sshin if ($2) 35656668Sshin long_passive("EPSV", PF_UNSPEC); 35756668Sshin } 35871278Sjedgar | TYPE check_login SP type_code CRLF 3591592Srgrimes { 36071278Sjedgar if ($2) { 36171278Sjedgar switch (cmd_type) { 3621592Srgrimes 36371278Sjedgar case TYPE_A: 36471278Sjedgar if (cmd_form == FORM_N) { 36571278Sjedgar reply(200, "Type set to A."); 36671278Sjedgar type = cmd_type; 36771278Sjedgar form = cmd_form; 36871278Sjedgar } else 36971278Sjedgar reply(504, "Form must be N."); 37071278Sjedgar break; 3711592Srgrimes 37271278Sjedgar case TYPE_E: 37371278Sjedgar reply(504, "Type E not implemented."); 37471278Sjedgar break; 3751592Srgrimes 37671278Sjedgar case TYPE_I: 37771278Sjedgar reply(200, "Type set to I."); 37871278Sjedgar type = cmd_type; 37971278Sjedgar break; 3801592Srgrimes 38171278Sjedgar case TYPE_L: 3821592Srgrimes#if NBBY == 8 38371278Sjedgar if (cmd_bytesz == 8) { 38471278Sjedgar reply(200, 38571278Sjedgar "Type set to L (byte size 8)."); 38671278Sjedgar type = cmd_type; 38771278Sjedgar } else 38871278Sjedgar reply(504, "Byte size must be 8."); 3891592Srgrimes#else /* NBBY == 8 */ 39071278Sjedgar UNIMPLEMENTED for NBBY != 8 3911592Srgrimes#endif /* NBBY == 8 */ 39271278Sjedgar } 3931592Srgrimes } 3941592Srgrimes } 39571278Sjedgar | STRU check_login SP struct_code CRLF 3961592Srgrimes { 39771278Sjedgar if ($2) { 39871278Sjedgar switch ($4) { 3991592Srgrimes 40071278Sjedgar case STRU_F: 40171278Sjedgar reply(200, "STRU F ok."); 40271278Sjedgar break; 4031592Srgrimes 40471278Sjedgar default: 40571278Sjedgar reply(504, "Unimplemented STRU type."); 40671278Sjedgar } 4071592Srgrimes } 4081592Srgrimes } 40971278Sjedgar | MODE check_login SP mode_code CRLF 4101592Srgrimes { 41171278Sjedgar if ($2) { 41271278Sjedgar switch ($4) { 4131592Srgrimes 41471278Sjedgar case MODE_S: 41571278Sjedgar reply(200, "MODE S ok."); 41671278Sjedgar break; 41771278Sjedgar 41871278Sjedgar default: 41971278Sjedgar reply(502, "Unimplemented MODE type."); 42071278Sjedgar } 4211592Srgrimes } 4221592Srgrimes } 42371278Sjedgar | ALLO check_login SP NUMBER CRLF 4241592Srgrimes { 42571278Sjedgar if ($2) { 42671278Sjedgar reply(202, "ALLO command ignored."); 42771278Sjedgar } 4281592Srgrimes } 42971278Sjedgar | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 4301592Srgrimes { 43171278Sjedgar if ($2) { 43271278Sjedgar reply(202, "ALLO command ignored."); 43371278Sjedgar } 4341592Srgrimes } 4351592Srgrimes | RETR check_login SP pathname CRLF 4361592Srgrimes { 4371592Srgrimes if ($2 && $4 != NULL) 4381592Srgrimes retrieve((char *) 0, $4); 4391592Srgrimes if ($4 != NULL) 4401592Srgrimes free($4); 4411592Srgrimes } 44270102Sphk | STOR check_login_ro SP pathname CRLF 4431592Srgrimes { 4441592Srgrimes if ($2 && $4 != NULL) 4451592Srgrimes store($4, "w", 0); 4461592Srgrimes if ($4 != NULL) 4471592Srgrimes free($4); 4481592Srgrimes } 44970102Sphk | APPE check_login_ro SP pathname CRLF 4501592Srgrimes { 4511592Srgrimes if ($2 && $4 != NULL) 4521592Srgrimes store($4, "a", 0); 4531592Srgrimes if ($4 != NULL) 4541592Srgrimes free($4); 4551592Srgrimes } 4561592Srgrimes | NLST check_login CRLF 4571592Srgrimes { 4581592Srgrimes if ($2) 4591592Srgrimes send_file_list("."); 4601592Srgrimes } 4611592Srgrimes | NLST check_login SP STRING CRLF 4621592Srgrimes { 4631592Srgrimes if ($2 && $4 != NULL) 4641592Srgrimes send_file_list($4); 4651592Srgrimes if ($4 != NULL) 4661592Srgrimes free($4); 4671592Srgrimes } 4681592Srgrimes | LIST check_login CRLF 4691592Srgrimes { 4701592Srgrimes if ($2) 4711592Srgrimes retrieve("/bin/ls -lgA", ""); 4721592Srgrimes } 4731592Srgrimes | LIST check_login SP pathname CRLF 4741592Srgrimes { 4751592Srgrimes if ($2 && $4 != NULL) 4761592Srgrimes retrieve("/bin/ls -lgA %s", $4); 4771592Srgrimes if ($4 != NULL) 4781592Srgrimes free($4); 4791592Srgrimes } 4801592Srgrimes | STAT check_login SP pathname CRLF 4811592Srgrimes { 4821592Srgrimes if ($2 && $4 != NULL) 4831592Srgrimes statfilecmd($4); 4841592Srgrimes if ($4 != NULL) 4851592Srgrimes free($4); 4861592Srgrimes } 48771278Sjedgar | STAT check_login CRLF 4881592Srgrimes { 48971278Sjedgar if ($2) { 49071278Sjedgar statcmd(); 49171278Sjedgar } 4921592Srgrimes } 49370102Sphk | DELE check_login_ro SP pathname CRLF 4941592Srgrimes { 4951592Srgrimes if ($2 && $4 != NULL) 4961592Srgrimes delete($4); 4971592Srgrimes if ($4 != NULL) 4981592Srgrimes free($4); 4991592Srgrimes } 50070102Sphk | RNTO check_login_ro SP pathname CRLF 5011592Srgrimes { 50217433Spst if ($2) { 50317433Spst if (fromname) { 50417433Spst renamecmd(fromname, $4); 50517433Spst free(fromname); 50617433Spst fromname = (char *) 0; 50717433Spst } else { 50817433Spst reply(503, "Bad sequence of commands."); 50917433Spst } 5101592Srgrimes } 51117433Spst free($4); 5121592Srgrimes } 51371278Sjedgar | ABOR check_login CRLF 5141592Srgrimes { 51571278Sjedgar if ($2) 51671278Sjedgar reply(225, "ABOR command successful."); 5171592Srgrimes } 5181592Srgrimes | CWD check_login CRLF 5191592Srgrimes { 52069234Sdanny if ($2) { 52169234Sdanny if (guest) 52269234Sdanny cwd("/"); 52369234Sdanny else 52469234Sdanny cwd(pw->pw_dir); 52569234Sdanny } 5261592Srgrimes } 5271592Srgrimes | CWD check_login SP pathname CRLF 5281592Srgrimes { 5291592Srgrimes if ($2 && $4 != NULL) 5301592Srgrimes cwd($4); 5311592Srgrimes if ($4 != NULL) 5321592Srgrimes free($4); 5331592Srgrimes } 5341592Srgrimes | HELP CRLF 5351592Srgrimes { 5361592Srgrimes help(cmdtab, (char *) 0); 5371592Srgrimes } 5381592Srgrimes | HELP SP STRING CRLF 5391592Srgrimes { 5401592Srgrimes char *cp = $3; 5411592Srgrimes 5421592Srgrimes if (strncasecmp(cp, "SITE", 4) == 0) { 5431592Srgrimes cp = $3 + 4; 5441592Srgrimes if (*cp == ' ') 5451592Srgrimes cp++; 5461592Srgrimes if (*cp) 5471592Srgrimes help(sitetab, cp); 5481592Srgrimes else 5491592Srgrimes help(sitetab, (char *) 0); 5501592Srgrimes } else 5511592Srgrimes help(cmdtab, $3); 5521592Srgrimes } 5531592Srgrimes | NOOP CRLF 5541592Srgrimes { 5551592Srgrimes reply(200, "NOOP command successful."); 5561592Srgrimes } 55770102Sphk | MKD check_login_ro SP pathname CRLF 5581592Srgrimes { 5591592Srgrimes if ($2 && $4 != NULL) 5601592Srgrimes makedir($4); 5611592Srgrimes if ($4 != NULL) 5621592Srgrimes free($4); 5631592Srgrimes } 56470102Sphk | RMD check_login_ro SP pathname CRLF 5651592Srgrimes { 5661592Srgrimes if ($2 && $4 != NULL) 5671592Srgrimes removedir($4); 5681592Srgrimes if ($4 != NULL) 5691592Srgrimes free($4); 5701592Srgrimes } 5711592Srgrimes | PWD check_login CRLF 5721592Srgrimes { 5731592Srgrimes if ($2) 5741592Srgrimes pwd(); 5751592Srgrimes } 5761592Srgrimes | CDUP check_login CRLF 5771592Srgrimes { 5781592Srgrimes if ($2) 5791592Srgrimes cwd(".."); 5801592Srgrimes } 5811592Srgrimes | SITE SP HELP CRLF 5821592Srgrimes { 5831592Srgrimes help(sitetab, (char *) 0); 5841592Srgrimes } 5851592Srgrimes | SITE SP HELP SP STRING CRLF 5861592Srgrimes { 5871592Srgrimes help(sitetab, $5); 5881592Srgrimes } 5891592Srgrimes | SITE SP UMASK check_login CRLF 5901592Srgrimes { 5911592Srgrimes int oldmask; 5921592Srgrimes 5931592Srgrimes if ($4) { 5941592Srgrimes oldmask = umask(0); 5951592Srgrimes (void) umask(oldmask); 5961592Srgrimes reply(200, "Current UMASK is %03o", oldmask); 5971592Srgrimes } 5981592Srgrimes } 5991592Srgrimes | SITE SP UMASK check_login SP octal_number CRLF 6001592Srgrimes { 6011592Srgrimes int oldmask; 6021592Srgrimes 6031592Srgrimes if ($4) { 6041592Srgrimes if (($6 == -1) || ($6 > 0777)) { 6051592Srgrimes reply(501, "Bad UMASK value"); 6061592Srgrimes } else { 6071592Srgrimes oldmask = umask($6); 6081592Srgrimes reply(200, 6091592Srgrimes "UMASK set to %03o (was %03o)", 6101592Srgrimes $6, oldmask); 6111592Srgrimes } 6121592Srgrimes } 6131592Srgrimes } 61470102Sphk | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF 6151592Srgrimes { 6161592Srgrimes if ($4 && ($8 != NULL)) { 6171592Srgrimes if ($6 > 0777) 6181592Srgrimes reply(501, 6191592Srgrimes "CHMOD: Mode value must be between 0 and 0777"); 6201592Srgrimes else if (chmod($8, $6) < 0) 6211592Srgrimes perror_reply(550, $8); 6221592Srgrimes else 6231592Srgrimes reply(200, "CHMOD command successful."); 6241592Srgrimes } 6251592Srgrimes if ($8 != NULL) 6261592Srgrimes free($8); 6271592Srgrimes } 62871278Sjedgar | SITE SP check_login IDLE CRLF 6291592Srgrimes { 63071278Sjedgar if ($3) 63171278Sjedgar reply(200, 63271278Sjedgar "Current IDLE time limit is %d seconds; max %d", 63371278Sjedgar timeout, maxtimeout); 6341592Srgrimes } 63571278Sjedgar | SITE SP check_login IDLE SP NUMBER CRLF 6361592Srgrimes { 63771278Sjedgar if ($3) { 63871278Sjedgar if ($6 < 30 || $6 > maxtimeout) { 63971278Sjedgar reply(501, 64071278Sjedgar "Maximum IDLE time must be between 30 and %d seconds", 64171278Sjedgar maxtimeout); 64271278Sjedgar } else { 64371278Sjedgar timeout = $6; 64471278Sjedgar (void) alarm((unsigned) timeout); 64571278Sjedgar reply(200, 64671278Sjedgar "Maximum IDLE time set to %d seconds", 64771278Sjedgar timeout); 64871278Sjedgar } 6491592Srgrimes } 6501592Srgrimes } 65170102Sphk | STOU check_login_ro SP pathname CRLF 6521592Srgrimes { 6531592Srgrimes if ($2 && $4 != NULL) 6541592Srgrimes store($4, "w", 1); 6551592Srgrimes if ($4 != NULL) 6561592Srgrimes free($4); 6571592Srgrimes } 65871278Sjedgar | SYST check_login CRLF 6591592Srgrimes { 66071278Sjedgar if ($2) 6611592Srgrimes#ifdef unix 6621592Srgrimes#ifdef BSD 6631592Srgrimes reply(215, "UNIX Type: L%d Version: BSD-%d", 6641592Srgrimes NBBY, BSD); 6651592Srgrimes#else /* BSD */ 6661592Srgrimes reply(215, "UNIX Type: L%d", NBBY); 6671592Srgrimes#endif /* BSD */ 6681592Srgrimes#else /* unix */ 6691592Srgrimes reply(215, "UNKNOWN Type: L%d", NBBY); 6701592Srgrimes#endif /* unix */ 6711592Srgrimes } 6721592Srgrimes 6731592Srgrimes /* 6741592Srgrimes * SIZE is not in RFC959, but Postel has blessed it and 6751592Srgrimes * it will be in the updated RFC. 6761592Srgrimes * 6771592Srgrimes * Return size of file in a format suitable for 6781592Srgrimes * using with RESTART (we just count bytes). 6791592Srgrimes */ 6801592Srgrimes | SIZE check_login SP pathname CRLF 6811592Srgrimes { 6821592Srgrimes if ($2 && $4 != NULL) 6831592Srgrimes sizecmd($4); 6841592Srgrimes if ($4 != NULL) 6851592Srgrimes free($4); 6861592Srgrimes } 6871592Srgrimes 6881592Srgrimes /* 6891592Srgrimes * MDTM is not in RFC959, but Postel has blessed it and 6901592Srgrimes * it will be in the updated RFC. 6911592Srgrimes * 6921592Srgrimes * Return modification time of file as an ISO 3307 6931592Srgrimes * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 6941592Srgrimes * where xxx is the fractional second (of any precision, 6951592Srgrimes * not necessarily 3 digits) 6961592Srgrimes */ 6971592Srgrimes | MDTM check_login SP pathname CRLF 6981592Srgrimes { 6991592Srgrimes if ($2 && $4 != NULL) { 7001592Srgrimes struct stat stbuf; 7011592Srgrimes if (stat($4, &stbuf) < 0) 7021592Srgrimes reply(550, "%s: %s", 7031592Srgrimes $4, strerror(errno)); 7041592Srgrimes else if (!S_ISREG(stbuf.st_mode)) { 7051592Srgrimes reply(550, "%s: not a plain file.", $4); 7061592Srgrimes } else { 7071592Srgrimes struct tm *t; 7081592Srgrimes t = gmtime(&stbuf.st_mtime); 7091592Srgrimes reply(213, 71017435Spst "%04d%02d%02d%02d%02d%02d", 71117435Spst 1900 + t->tm_year, 71217435Spst t->tm_mon+1, t->tm_mday, 7131592Srgrimes t->tm_hour, t->tm_min, t->tm_sec); 7141592Srgrimes } 7151592Srgrimes } 7161592Srgrimes if ($4 != NULL) 7171592Srgrimes free($4); 7181592Srgrimes } 7191592Srgrimes | QUIT CRLF 7201592Srgrimes { 7211592Srgrimes reply(221, "Goodbye."); 7221592Srgrimes dologout(0); 7231592Srgrimes } 7241592Srgrimes | error CRLF 7251592Srgrimes { 7261592Srgrimes yyerrok; 7271592Srgrimes } 7281592Srgrimes ; 7291592Srgrimesrcmd 73070102Sphk : RNFR check_login_ro SP pathname CRLF 7311592Srgrimes { 7321592Srgrimes char *renamefrom(); 7331592Srgrimes 7341592Srgrimes restart_point = (off_t) 0; 7351592Srgrimes if ($2 && $4) { 7361592Srgrimes fromname = renamefrom($4); 7371592Srgrimes if (fromname == (char *) 0 && $4) { 7381592Srgrimes free($4); 7391592Srgrimes } 7401592Srgrimes } 7411592Srgrimes } 74271278Sjedgar | REST check_login SP byte_size CRLF 7431592Srgrimes { 74471278Sjedgar if ($2) { 74571278Sjedgar fromname = (char *) 0; 74671278Sjedgar restart_point = $4; /* XXX $4 is only "int" */ 74771278Sjedgar reply(350, "Restarting at %qd. %s", 74871278Sjedgar restart_point, 74971278Sjedgar "Send STORE or RETRIEVE to initiate transfer."); 75071278Sjedgar } 7511592Srgrimes } 7521592Srgrimes ; 7531592Srgrimes 7541592Srgrimesusername 7551592Srgrimes : STRING 7561592Srgrimes ; 7571592Srgrimes 7581592Srgrimespassword 7591592Srgrimes : /* empty */ 7601592Srgrimes { 7611592Srgrimes $$ = (char *)calloc(1, sizeof(char)); 7621592Srgrimes } 7631592Srgrimes | STRING 7641592Srgrimes ; 7651592Srgrimes 7661592Srgrimesbyte_size 7671592Srgrimes : NUMBER 7681592Srgrimes ; 7691592Srgrimes 7701592Srgrimeshost_port 7711592Srgrimes : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 7721592Srgrimes NUMBER COMMA NUMBER 7731592Srgrimes { 7741592Srgrimes char *a, *p; 7751592Srgrimes 77656668Sshin data_dest.su_len = sizeof(struct sockaddr_in); 77756668Sshin data_dest.su_family = AF_INET; 77856668Sshin p = (char *)&data_dest.su_sin.sin_port; 77917435Spst p[0] = $9; p[1] = $11; 78056668Sshin a = (char *)&data_dest.su_sin.sin_addr; 7811592Srgrimes a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 7821592Srgrimes } 7831592Srgrimes ; 7841592Srgrimes 78556668Sshinhost_long_port 78656668Sshin : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 78756668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 78856668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 78956668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 79056668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 79156668Sshin NUMBER 79256668Sshin { 79356668Sshin char *a, *p; 79456668Sshin 79556668Sshin memset(&data_dest, 0, sizeof(data_dest)); 79656668Sshin data_dest.su_len = sizeof(struct sockaddr_in6); 79756668Sshin data_dest.su_family = AF_INET6; 79856668Sshin p = (char *)&data_dest.su_port; 79956668Sshin p[0] = $39; p[1] = $41; 80056668Sshin a = (char *)&data_dest.su_sin6.sin6_addr; 80156668Sshin a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 80256668Sshin a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19; 80356668Sshin a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27; 80456668Sshin a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35; 80556668Sshin if (his_addr.su_family == AF_INET6) { 80656668Sshin /* XXX more sanity checks! */ 80756668Sshin data_dest.su_sin6.sin6_scope_id = 80856668Sshin his_addr.su_sin6.sin6_scope_id; 80956668Sshin } 81056668Sshin if ($1 != 6 || $3 != 16 || $37 != 2) 81156668Sshin memset(&data_dest, 0, sizeof(data_dest)); 81256668Sshin } 81356668Sshin | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 81456668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 81556668Sshin NUMBER 81656668Sshin { 81756668Sshin char *a, *p; 81856668Sshin 81956668Sshin memset(&data_dest, 0, sizeof(data_dest)); 82056668Sshin data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 82156668Sshin data_dest.su_family = AF_INET; 82256668Sshin p = (char *)&data_dest.su_port; 82356668Sshin p[0] = $15; p[1] = $17; 82456668Sshin a = (char *)&data_dest.su_sin.sin_addr; 82556668Sshin a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 82656668Sshin if ($1 != 4 || $3 != 4 || $13 != 2) 82756668Sshin memset(&data_dest, 0, sizeof(data_dest)); 82856668Sshin } 82956668Sshin ; 83056668Sshin 8311592Srgrimesform_code 8321592Srgrimes : N 8331592Srgrimes { 8341592Srgrimes $$ = FORM_N; 8351592Srgrimes } 8361592Srgrimes | T 8371592Srgrimes { 8381592Srgrimes $$ = FORM_T; 8391592Srgrimes } 8401592Srgrimes | C 8411592Srgrimes { 8421592Srgrimes $$ = FORM_C; 8431592Srgrimes } 8441592Srgrimes ; 8451592Srgrimes 8461592Srgrimestype_code 8471592Srgrimes : A 8481592Srgrimes { 8491592Srgrimes cmd_type = TYPE_A; 8501592Srgrimes cmd_form = FORM_N; 8511592Srgrimes } 8521592Srgrimes | A SP form_code 8531592Srgrimes { 8541592Srgrimes cmd_type = TYPE_A; 8551592Srgrimes cmd_form = $3; 8561592Srgrimes } 8571592Srgrimes | E 8581592Srgrimes { 8591592Srgrimes cmd_type = TYPE_E; 8601592Srgrimes cmd_form = FORM_N; 8611592Srgrimes } 8621592Srgrimes | E SP form_code 8631592Srgrimes { 8641592Srgrimes cmd_type = TYPE_E; 8651592Srgrimes cmd_form = $3; 8661592Srgrimes } 8671592Srgrimes | I 8681592Srgrimes { 8691592Srgrimes cmd_type = TYPE_I; 8701592Srgrimes } 8711592Srgrimes | L 8721592Srgrimes { 8731592Srgrimes cmd_type = TYPE_L; 8741592Srgrimes cmd_bytesz = NBBY; 8751592Srgrimes } 8761592Srgrimes | L SP byte_size 8771592Srgrimes { 8781592Srgrimes cmd_type = TYPE_L; 8791592Srgrimes cmd_bytesz = $3; 8801592Srgrimes } 8811592Srgrimes /* this is for a bug in the BBN ftp */ 8821592Srgrimes | L byte_size 8831592Srgrimes { 8841592Srgrimes cmd_type = TYPE_L; 8851592Srgrimes cmd_bytesz = $2; 8861592Srgrimes } 8871592Srgrimes ; 8881592Srgrimes 8891592Srgrimesstruct_code 8901592Srgrimes : F 8911592Srgrimes { 8921592Srgrimes $$ = STRU_F; 8931592Srgrimes } 8941592Srgrimes | R 8951592Srgrimes { 8961592Srgrimes $$ = STRU_R; 8971592Srgrimes } 8981592Srgrimes | P 8991592Srgrimes { 9001592Srgrimes $$ = STRU_P; 9011592Srgrimes } 9021592Srgrimes ; 9031592Srgrimes 9041592Srgrimesmode_code 9051592Srgrimes : S 9061592Srgrimes { 9071592Srgrimes $$ = MODE_S; 9081592Srgrimes } 9091592Srgrimes | B 9101592Srgrimes { 9111592Srgrimes $$ = MODE_B; 9121592Srgrimes } 9131592Srgrimes | C 9141592Srgrimes { 9151592Srgrimes $$ = MODE_C; 9161592Srgrimes } 9171592Srgrimes ; 9181592Srgrimes 9191592Srgrimespathname 9201592Srgrimes : pathstring 9211592Srgrimes { 9221592Srgrimes /* 9231592Srgrimes * Problem: this production is used for all pathname 9241592Srgrimes * processing, but only gives a 550 error reply. 9251592Srgrimes * This is a valid reply in some cases but not in others. 9261592Srgrimes */ 9271592Srgrimes if (logged_in && $1 && *$1 == '~') { 9281592Srgrimes glob_t gl; 9291592Srgrimes int flags = 9301592Srgrimes GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 9311592Srgrimes 9321592Srgrimes memset(&gl, 0, sizeof(gl)); 9331592Srgrimes if (glob($1, flags, NULL, &gl) || 9341592Srgrimes gl.gl_pathc == 0) { 9351592Srgrimes reply(550, "not found"); 9361592Srgrimes $$ = NULL; 9371592Srgrimes } else { 9381592Srgrimes $$ = strdup(gl.gl_pathv[0]); 9391592Srgrimes } 9401592Srgrimes globfree(&gl); 9411592Srgrimes free($1); 9421592Srgrimes } else 9431592Srgrimes $$ = $1; 9441592Srgrimes } 9451592Srgrimes ; 9461592Srgrimes 9471592Srgrimespathstring 9481592Srgrimes : STRING 9491592Srgrimes ; 9501592Srgrimes 9511592Srgrimesoctal_number 9521592Srgrimes : NUMBER 9531592Srgrimes { 9541592Srgrimes int ret, dec, multby, digit; 9551592Srgrimes 9561592Srgrimes /* 9571592Srgrimes * Convert a number that was read as decimal number 9581592Srgrimes * to what it would be if it had been read as octal. 9591592Srgrimes */ 9601592Srgrimes dec = $1; 9611592Srgrimes multby = 1; 9621592Srgrimes ret = 0; 9631592Srgrimes while (dec) { 9641592Srgrimes digit = dec%10; 9651592Srgrimes if (digit > 7) { 9661592Srgrimes ret = -1; 9671592Srgrimes break; 9681592Srgrimes } 9691592Srgrimes ret += digit * multby; 9701592Srgrimes multby *= 8; 9711592Srgrimes dec /= 10; 9721592Srgrimes } 9731592Srgrimes $$ = ret; 9741592Srgrimes } 9751592Srgrimes ; 9761592Srgrimes 9771592Srgrimes 9781592Srgrimescheck_login 9791592Srgrimes : /* empty */ 9801592Srgrimes { 98170102Sphk $$ = check_login1(); 9821592Srgrimes } 9831592Srgrimes ; 9841592Srgrimes 98570102Sphkcheck_login_epsv 98670102Sphk : /* empty */ 98770102Sphk { 98870102Sphk if (noepsv) { 98970102Sphk reply(500, "EPSV command disabled"); 99070102Sphk $$ = 0; 99170102Sphk } 99270102Sphk else 99370102Sphk $$ = check_login1(); 99470102Sphk } 99570102Sphk ; 99670102Sphk 99770102Sphkcheck_login_ro 99870102Sphk : /* empty */ 99970102Sphk { 100070102Sphk if (readonly) { 100170102Sphk reply(202, "Command ignored. Server is in readonly mode."); 100270102Sphk $$ = 0; 100370102Sphk } 100470102Sphk else 100570102Sphk $$ = check_login1(); 100670102Sphk } 100770102Sphk ; 100870102Sphk 10091592Srgrimes%% 10101592Srgrimes 10111592Srgrimesextern jmp_buf errcatch; 10121592Srgrimes 10131592Srgrimes#define CMD 0 /* beginning of command */ 10141592Srgrimes#define ARGS 1 /* expect miscellaneous arguments */ 10151592Srgrimes#define STR1 2 /* expect SP followed by STRING */ 10161592Srgrimes#define STR2 3 /* expect STRING */ 10171592Srgrimes#define OSTR 4 /* optional SP then STRING */ 10181592Srgrimes#define ZSTR1 5 /* SP then optional STRING */ 10191592Srgrimes#define ZSTR2 6 /* optional STRING after SP */ 10201592Srgrimes#define SITECMD 7 /* SITE command */ 10211592Srgrimes#define NSTR 8 /* Number followed by a string */ 10221592Srgrimes 10231592Srgrimesstruct tab { 10241592Srgrimes char *name; 10251592Srgrimes short token; 10261592Srgrimes short state; 10271592Srgrimes short implemented; /* 1 if command is implemented */ 10281592Srgrimes char *help; 10291592Srgrimes}; 10301592Srgrimes 10311592Srgrimesstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 10321592Srgrimes { "USER", USER, STR1, 1, "<sp> username" }, 10331592Srgrimes { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 10341592Srgrimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 10351592Srgrimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 10361592Srgrimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 10371592Srgrimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 10381592Srgrimes { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 103956668Sshin { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 104056668Sshin { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 10411592Srgrimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 104256668Sshin { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 104356668Sshin { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 10441592Srgrimes { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 10451592Srgrimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 10461592Srgrimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 10471592Srgrimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 10481592Srgrimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 10491592Srgrimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 10501592Srgrimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 10511592Srgrimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 10521592Srgrimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 10531592Srgrimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 10541592Srgrimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 10551592Srgrimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 10561592Srgrimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 10571592Srgrimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 10581592Srgrimes { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 10591592Srgrimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 10601592Srgrimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 10611592Srgrimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 10621592Srgrimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 10631592Srgrimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 10641592Srgrimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 10651592Srgrimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 10661592Srgrimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 10671592Srgrimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 10681592Srgrimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 10691592Srgrimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 10701592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 10711592Srgrimes { "NOOP", NOOP, ARGS, 1, "" }, 10721592Srgrimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 10731592Srgrimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 10741592Srgrimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 10751592Srgrimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 10761592Srgrimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 10771592Srgrimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 10781592Srgrimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 10791592Srgrimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 10801592Srgrimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 10811592Srgrimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 10821592Srgrimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 10831592Srgrimes { NULL, 0, 0, 0, 0 } 10841592Srgrimes}; 10851592Srgrimes 10861592Srgrimesstruct tab sitetab[] = { 10871592Srgrimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 10881592Srgrimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 10891592Srgrimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 10901592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 10911592Srgrimes { NULL, 0, 0, 0, 0 } 10921592Srgrimes}; 10931592Srgrimes 10941592Srgrimesstatic char *copy __P((char *)); 10951592Srgrimesstatic void help __P((struct tab *, char *)); 10961592Srgrimesstatic struct tab * 10971592Srgrimes lookup __P((struct tab *, char *)); 109856668Sshinstatic int port_check __P((const char *)); 109956668Sshinstatic int port_check_v6 __P((const char *)); 11001592Srgrimesstatic void sizecmd __P((char *)); 11011592Srgrimesstatic void toolong __P((int)); 110256668Sshinstatic void v4map_data_dest __P((void)); 11031592Srgrimesstatic int yylex __P((void)); 11041592Srgrimes 11051592Srgrimesstatic struct tab * 11061592Srgrimeslookup(p, cmd) 11071592Srgrimes struct tab *p; 11081592Srgrimes char *cmd; 11091592Srgrimes{ 11101592Srgrimes 11111592Srgrimes for (; p->name != NULL; p++) 11121592Srgrimes if (strcmp(cmd, p->name) == 0) 11131592Srgrimes return (p); 11141592Srgrimes return (0); 11151592Srgrimes} 11161592Srgrimes 11171592Srgrimes#include <arpa/telnet.h> 11181592Srgrimes 11191592Srgrimes/* 11201592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes. 11211592Srgrimes */ 11221592Srgrimeschar * 11231592Srgrimesgetline(s, n, iop) 11241592Srgrimes char *s; 11251592Srgrimes int n; 11261592Srgrimes FILE *iop; 11271592Srgrimes{ 11281592Srgrimes int c; 11291592Srgrimes register char *cs; 11301592Srgrimes 11311592Srgrimes cs = s; 11321592Srgrimes/* tmpline may contain saved command from urgent mode interruption */ 11331592Srgrimes for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 11341592Srgrimes *cs++ = tmpline[c]; 11351592Srgrimes if (tmpline[c] == '\n') { 11361592Srgrimes *cs++ = '\0'; 11371592Srgrimes if (debug) 11381592Srgrimes syslog(LOG_DEBUG, "command: %s", s); 11391592Srgrimes tmpline[0] = '\0'; 11401592Srgrimes return(s); 11411592Srgrimes } 11421592Srgrimes if (c == 0) 11431592Srgrimes tmpline[0] = '\0'; 11441592Srgrimes } 11451592Srgrimes while ((c = getc(iop)) != EOF) { 11461592Srgrimes c &= 0377; 11471592Srgrimes if (c == IAC) { 11481592Srgrimes if ((c = getc(iop)) != EOF) { 11491592Srgrimes c &= 0377; 11501592Srgrimes switch (c) { 11511592Srgrimes case WILL: 11521592Srgrimes case WONT: 11531592Srgrimes c = getc(iop); 11541592Srgrimes printf("%c%c%c", IAC, DONT, 0377&c); 11551592Srgrimes (void) fflush(stdout); 11561592Srgrimes continue; 11571592Srgrimes case DO: 11581592Srgrimes case DONT: 11591592Srgrimes c = getc(iop); 11601592Srgrimes printf("%c%c%c", IAC, WONT, 0377&c); 11611592Srgrimes (void) fflush(stdout); 11621592Srgrimes continue; 11631592Srgrimes case IAC: 11641592Srgrimes break; 11651592Srgrimes default: 11661592Srgrimes continue; /* ignore command */ 11671592Srgrimes } 11681592Srgrimes } 11691592Srgrimes } 11701592Srgrimes *cs++ = c; 11711592Srgrimes if (--n <= 0 || c == '\n') 11721592Srgrimes break; 11731592Srgrimes } 11741592Srgrimes if (c == EOF && cs == s) 11751592Srgrimes return (NULL); 11761592Srgrimes *cs++ = '\0'; 11771592Srgrimes if (debug) { 11781592Srgrimes if (!guest && strncasecmp("pass ", s, 5) == 0) { 11791592Srgrimes /* Don't syslog passwords */ 11801592Srgrimes syslog(LOG_DEBUG, "command: %.5s ???", s); 11811592Srgrimes } else { 11821592Srgrimes register char *cp; 11831592Srgrimes register int len; 11841592Srgrimes 11851592Srgrimes /* Don't syslog trailing CR-LF */ 11861592Srgrimes len = strlen(s); 11871592Srgrimes cp = s + len - 1; 11881592Srgrimes while (cp >= s && (*cp == '\n' || *cp == '\r')) { 11891592Srgrimes --cp; 11901592Srgrimes --len; 11911592Srgrimes } 11921592Srgrimes syslog(LOG_DEBUG, "command: %.*s", len, s); 11931592Srgrimes } 11941592Srgrimes } 11951592Srgrimes return (s); 11961592Srgrimes} 11971592Srgrimes 11981592Srgrimesstatic void 11991592Srgrimestoolong(signo) 12001592Srgrimes int signo; 12011592Srgrimes{ 12021592Srgrimes 12031592Srgrimes reply(421, 12041592Srgrimes "Timeout (%d seconds): closing control connection.", timeout); 12051592Srgrimes if (logging) 12061592Srgrimes syslog(LOG_INFO, "User %s timed out after %d seconds", 12071592Srgrimes (pw ? pw -> pw_name : "unknown"), timeout); 12081592Srgrimes dologout(1); 12091592Srgrimes} 12101592Srgrimes 12111592Srgrimesstatic int 12121592Srgrimesyylex() 12131592Srgrimes{ 12141592Srgrimes static int cpos, state; 12151592Srgrimes char *cp, *cp2; 12161592Srgrimes struct tab *p; 12171592Srgrimes int n; 12181592Srgrimes char c; 12191592Srgrimes 12201592Srgrimes for (;;) { 12211592Srgrimes switch (state) { 12221592Srgrimes 12231592Srgrimes case CMD: 12241592Srgrimes (void) signal(SIGALRM, toolong); 12251592Srgrimes (void) alarm((unsigned) timeout); 12261592Srgrimes if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 12271592Srgrimes reply(221, "You could at least say goodbye."); 12281592Srgrimes dologout(0); 12291592Srgrimes } 12301592Srgrimes (void) alarm(0); 12311592Srgrimes#ifdef SETPROCTITLE 123229574Sphk if (strncasecmp(cbuf, "PASS", 4) != 0) 12331592Srgrimes setproctitle("%s: %s", proctitle, cbuf); 12341592Srgrimes#endif /* SETPROCTITLE */ 12351592Srgrimes if ((cp = strchr(cbuf, '\r'))) { 12361592Srgrimes *cp++ = '\n'; 12371592Srgrimes *cp = '\0'; 12381592Srgrimes } 12391592Srgrimes if ((cp = strpbrk(cbuf, " \n"))) 12401592Srgrimes cpos = cp - cbuf; 12411592Srgrimes if (cpos == 0) 12421592Srgrimes cpos = 4; 12431592Srgrimes c = cbuf[cpos]; 12441592Srgrimes cbuf[cpos] = '\0'; 12451592Srgrimes upper(cbuf); 12461592Srgrimes p = lookup(cmdtab, cbuf); 12471592Srgrimes cbuf[cpos] = c; 12483776Spst if (p != 0) { 12491592Srgrimes if (p->implemented == 0) { 12501592Srgrimes nack(p->name); 12511592Srgrimes longjmp(errcatch,0); 12521592Srgrimes /* NOTREACHED */ 12531592Srgrimes } 12541592Srgrimes state = p->state; 12551592Srgrimes yylval.s = p->name; 12561592Srgrimes return (p->token); 12571592Srgrimes } 12581592Srgrimes break; 12591592Srgrimes 12601592Srgrimes case SITECMD: 12611592Srgrimes if (cbuf[cpos] == ' ') { 12621592Srgrimes cpos++; 12631592Srgrimes return (SP); 12641592Srgrimes } 12651592Srgrimes cp = &cbuf[cpos]; 12661592Srgrimes if ((cp2 = strpbrk(cp, " \n"))) 12671592Srgrimes cpos = cp2 - cbuf; 12681592Srgrimes c = cbuf[cpos]; 12691592Srgrimes cbuf[cpos] = '\0'; 12701592Srgrimes upper(cp); 12711592Srgrimes p = lookup(sitetab, cp); 12721592Srgrimes cbuf[cpos] = c; 12733777Spst if (guest == 0 && p != 0) { 12741592Srgrimes if (p->implemented == 0) { 12751592Srgrimes state = CMD; 12761592Srgrimes nack(p->name); 12771592Srgrimes longjmp(errcatch,0); 12781592Srgrimes /* NOTREACHED */ 12791592Srgrimes } 12801592Srgrimes state = p->state; 12811592Srgrimes yylval.s = p->name; 12821592Srgrimes return (p->token); 12831592Srgrimes } 12841592Srgrimes state = CMD; 12851592Srgrimes break; 12861592Srgrimes 12871592Srgrimes case OSTR: 12881592Srgrimes if (cbuf[cpos] == '\n') { 12891592Srgrimes state = CMD; 12901592Srgrimes return (CRLF); 12911592Srgrimes } 12921592Srgrimes /* FALLTHROUGH */ 12931592Srgrimes 12941592Srgrimes case STR1: 12951592Srgrimes case ZSTR1: 12961592Srgrimes dostr1: 12971592Srgrimes if (cbuf[cpos] == ' ') { 12981592Srgrimes cpos++; 129951979Salfred state = state == OSTR ? STR2 : state+1; 13001592Srgrimes return (SP); 13011592Srgrimes } 13021592Srgrimes break; 13031592Srgrimes 13041592Srgrimes case ZSTR2: 13051592Srgrimes if (cbuf[cpos] == '\n') { 13061592Srgrimes state = CMD; 13071592Srgrimes return (CRLF); 13081592Srgrimes } 13091592Srgrimes /* FALLTHROUGH */ 13101592Srgrimes 13111592Srgrimes case STR2: 13121592Srgrimes cp = &cbuf[cpos]; 13131592Srgrimes n = strlen(cp); 13141592Srgrimes cpos += n - 1; 13151592Srgrimes /* 13161592Srgrimes * Make sure the string is nonempty and \n terminated. 13171592Srgrimes */ 13181592Srgrimes if (n > 1 && cbuf[cpos] == '\n') { 13191592Srgrimes cbuf[cpos] = '\0'; 13201592Srgrimes yylval.s = copy(cp); 13211592Srgrimes cbuf[cpos] = '\n'; 13221592Srgrimes state = ARGS; 13231592Srgrimes return (STRING); 13241592Srgrimes } 13251592Srgrimes break; 13261592Srgrimes 13271592Srgrimes case NSTR: 13281592Srgrimes if (cbuf[cpos] == ' ') { 13291592Srgrimes cpos++; 13301592Srgrimes return (SP); 13311592Srgrimes } 13321592Srgrimes if (isdigit(cbuf[cpos])) { 13331592Srgrimes cp = &cbuf[cpos]; 13341592Srgrimes while (isdigit(cbuf[++cpos])) 13351592Srgrimes ; 13361592Srgrimes c = cbuf[cpos]; 13371592Srgrimes cbuf[cpos] = '\0'; 13381592Srgrimes yylval.i = atoi(cp); 13391592Srgrimes cbuf[cpos] = c; 13401592Srgrimes state = STR1; 13411592Srgrimes return (NUMBER); 13421592Srgrimes } 13431592Srgrimes state = STR1; 13441592Srgrimes goto dostr1; 13451592Srgrimes 13461592Srgrimes case ARGS: 13471592Srgrimes if (isdigit(cbuf[cpos])) { 13481592Srgrimes cp = &cbuf[cpos]; 13491592Srgrimes while (isdigit(cbuf[++cpos])) 13501592Srgrimes ; 13511592Srgrimes c = cbuf[cpos]; 13521592Srgrimes cbuf[cpos] = '\0'; 13531592Srgrimes yylval.i = atoi(cp); 13541592Srgrimes cbuf[cpos] = c; 13551592Srgrimes return (NUMBER); 13561592Srgrimes } 135756668Sshin if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 135856668Sshin && !isalnum(cbuf[cpos + 3])) { 135956668Sshin cpos += 3; 136056668Sshin return ALL; 136156668Sshin } 13621592Srgrimes switch (cbuf[cpos++]) { 13631592Srgrimes 13641592Srgrimes case '\n': 13651592Srgrimes state = CMD; 13661592Srgrimes return (CRLF); 13671592Srgrimes 13681592Srgrimes case ' ': 13691592Srgrimes return (SP); 13701592Srgrimes 13711592Srgrimes case ',': 13721592Srgrimes return (COMMA); 13731592Srgrimes 13741592Srgrimes case 'A': 13751592Srgrimes case 'a': 13761592Srgrimes return (A); 13771592Srgrimes 13781592Srgrimes case 'B': 13791592Srgrimes case 'b': 13801592Srgrimes return (B); 13811592Srgrimes 13821592Srgrimes case 'C': 13831592Srgrimes case 'c': 13841592Srgrimes return (C); 13851592Srgrimes 13861592Srgrimes case 'E': 13871592Srgrimes case 'e': 13881592Srgrimes return (E); 13891592Srgrimes 13901592Srgrimes case 'F': 13911592Srgrimes case 'f': 13921592Srgrimes return (F); 13931592Srgrimes 13941592Srgrimes case 'I': 13951592Srgrimes case 'i': 13961592Srgrimes return (I); 13971592Srgrimes 13981592Srgrimes case 'L': 13991592Srgrimes case 'l': 14001592Srgrimes return (L); 14011592Srgrimes 14021592Srgrimes case 'N': 14031592Srgrimes case 'n': 14041592Srgrimes return (N); 14051592Srgrimes 14061592Srgrimes case 'P': 14071592Srgrimes case 'p': 14081592Srgrimes return (P); 14091592Srgrimes 14101592Srgrimes case 'R': 14111592Srgrimes case 'r': 14121592Srgrimes return (R); 14131592Srgrimes 14141592Srgrimes case 'S': 14151592Srgrimes case 's': 14161592Srgrimes return (S); 14171592Srgrimes 14181592Srgrimes case 'T': 14191592Srgrimes case 't': 14201592Srgrimes return (T); 14211592Srgrimes 14221592Srgrimes } 14231592Srgrimes break; 14241592Srgrimes 14251592Srgrimes default: 14261592Srgrimes fatal("Unknown state in scanner."); 14271592Srgrimes } 14281592Srgrimes yyerror((char *) 0); 14291592Srgrimes state = CMD; 14301592Srgrimes longjmp(errcatch,0); 14311592Srgrimes } 14321592Srgrimes} 14331592Srgrimes 14341592Srgrimesvoid 14351592Srgrimesupper(s) 14361592Srgrimes char *s; 14371592Srgrimes{ 14381592Srgrimes while (*s != '\0') { 14391592Srgrimes if (islower(*s)) 14401592Srgrimes *s = toupper(*s); 14411592Srgrimes s++; 14421592Srgrimes } 14431592Srgrimes} 14441592Srgrimes 14451592Srgrimesstatic char * 14461592Srgrimescopy(s) 14471592Srgrimes char *s; 14481592Srgrimes{ 14491592Srgrimes char *p; 14501592Srgrimes 14511592Srgrimes p = malloc((unsigned) strlen(s) + 1); 14521592Srgrimes if (p == NULL) 14531592Srgrimes fatal("Ran out of memory."); 14541592Srgrimes (void) strcpy(p, s); 14551592Srgrimes return (p); 14561592Srgrimes} 14571592Srgrimes 14581592Srgrimesstatic void 14591592Srgrimeshelp(ctab, s) 14601592Srgrimes struct tab *ctab; 14611592Srgrimes char *s; 14621592Srgrimes{ 14631592Srgrimes struct tab *c; 14641592Srgrimes int width, NCMDS; 14651592Srgrimes char *type; 14661592Srgrimes 14671592Srgrimes if (ctab == sitetab) 14681592Srgrimes type = "SITE "; 14691592Srgrimes else 14701592Srgrimes type = ""; 14711592Srgrimes width = 0, NCMDS = 0; 14721592Srgrimes for (c = ctab; c->name != NULL; c++) { 14731592Srgrimes int len = strlen(c->name); 14741592Srgrimes 14751592Srgrimes if (len > width) 14761592Srgrimes width = len; 14771592Srgrimes NCMDS++; 14781592Srgrimes } 14791592Srgrimes width = (width + 8) &~ 7; 14801592Srgrimes if (s == 0) { 14811592Srgrimes int i, j, w; 14821592Srgrimes int columns, lines; 14831592Srgrimes 14841592Srgrimes lreply(214, "The following %scommands are recognized %s.", 14851592Srgrimes type, "(* =>'s unimplemented)"); 14861592Srgrimes columns = 76 / width; 14871592Srgrimes if (columns == 0) 14881592Srgrimes columns = 1; 14891592Srgrimes lines = (NCMDS + columns - 1) / columns; 14901592Srgrimes for (i = 0; i < lines; i++) { 14911592Srgrimes printf(" "); 14921592Srgrimes for (j = 0; j < columns; j++) { 14931592Srgrimes c = ctab + j * lines + i; 14941592Srgrimes printf("%s%c", c->name, 14951592Srgrimes c->implemented ? ' ' : '*'); 14961592Srgrimes if (c + lines >= &ctab[NCMDS]) 14971592Srgrimes break; 14981592Srgrimes w = strlen(c->name) + 1; 14991592Srgrimes while (w < width) { 15001592Srgrimes putchar(' '); 15011592Srgrimes w++; 15021592Srgrimes } 15031592Srgrimes } 15041592Srgrimes printf("\r\n"); 15051592Srgrimes } 15061592Srgrimes (void) fflush(stdout); 15071592Srgrimes reply(214, "Direct comments to ftp-bugs@%s.", hostname); 15081592Srgrimes return; 15091592Srgrimes } 15101592Srgrimes upper(s); 15111592Srgrimes c = lookup(ctab, s); 15121592Srgrimes if (c == (struct tab *)0) { 15131592Srgrimes reply(502, "Unknown command %s.", s); 15141592Srgrimes return; 15151592Srgrimes } 15161592Srgrimes if (c->implemented) 15171592Srgrimes reply(214, "Syntax: %s%s %s", type, c->name, c->help); 15181592Srgrimes else 15191592Srgrimes reply(214, "%s%-*s\t%s; unimplemented.", type, width, 15201592Srgrimes c->name, c->help); 15211592Srgrimes} 15221592Srgrimes 15231592Srgrimesstatic void 15241592Srgrimessizecmd(filename) 15251592Srgrimes char *filename; 15261592Srgrimes{ 15271592Srgrimes switch (type) { 15281592Srgrimes case TYPE_L: 15291592Srgrimes case TYPE_I: { 15301592Srgrimes struct stat stbuf; 153163350Sdes if (stat(filename, &stbuf) < 0) 153263350Sdes perror_reply(550, filename); 153363350Sdes else if (!S_ISREG(stbuf.st_mode)) 15341592Srgrimes reply(550, "%s: not a plain file.", filename); 15351592Srgrimes else 15361592Srgrimes reply(213, "%qu", stbuf.st_size); 15371592Srgrimes break; } 15381592Srgrimes case TYPE_A: { 15391592Srgrimes FILE *fin; 15401592Srgrimes int c; 15411592Srgrimes off_t count; 15421592Srgrimes struct stat stbuf; 15431592Srgrimes fin = fopen(filename, "r"); 15441592Srgrimes if (fin == NULL) { 15451592Srgrimes perror_reply(550, filename); 15461592Srgrimes return; 15471592Srgrimes } 154863350Sdes if (fstat(fileno(fin), &stbuf) < 0) { 154963350Sdes perror_reply(550, filename); 155063350Sdes (void) fclose(fin); 155163350Sdes return; 155263350Sdes } else if (!S_ISREG(stbuf.st_mode)) { 15531592Srgrimes reply(550, "%s: not a plain file.", filename); 15541592Srgrimes (void) fclose(fin); 15551592Srgrimes return; 15561592Srgrimes } 15571592Srgrimes 15581592Srgrimes count = 0; 15591592Srgrimes while((c=getc(fin)) != EOF) { 15601592Srgrimes if (c == '\n') /* will get expanded to \r\n */ 15611592Srgrimes count++; 15621592Srgrimes count++; 15631592Srgrimes } 15641592Srgrimes (void) fclose(fin); 15651592Srgrimes 15661592Srgrimes reply(213, "%qd", count); 15671592Srgrimes break; } 15681592Srgrimes default: 15691592Srgrimes reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 15701592Srgrimes } 15711592Srgrimes} 157256668Sshin 157356668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 157456668Sshinstatic int 157556668Sshinport_check(pcmd) 157656668Sshin const char *pcmd; 157756668Sshin{ 157856668Sshin if (his_addr.su_family == AF_INET) { 157956668Sshin if (data_dest.su_family != AF_INET) { 158056668Sshin usedefault = 1; 158156668Sshin reply(500, "Invalid address rejected."); 158256668Sshin return 1; 158356668Sshin } 158456668Sshin if (paranoid && 158556668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 158656668Sshin memcmp(&data_dest.su_sin.sin_addr, 158756668Sshin &his_addr.su_sin.sin_addr, 158856668Sshin sizeof(data_dest.su_sin.sin_addr)))) { 158956668Sshin usedefault = 1; 159056668Sshin reply(500, "Illegal PORT range rejected."); 159156668Sshin } else { 159256668Sshin usedefault = 0; 159356668Sshin if (pdata >= 0) { 159456668Sshin (void) close(pdata); 159556668Sshin pdata = -1; 159656668Sshin } 159756668Sshin reply(200, "%s command successful.", pcmd); 159856668Sshin } 159956668Sshin return 1; 160056668Sshin } 160156668Sshin return 0; 160256668Sshin} 160356668Sshin 160470102Sphkstatic int 160570102Sphkcheck_login1() 160670102Sphk{ 160770102Sphk if (logged_in) 160870102Sphk return 1; 160970102Sphk else { 161070102Sphk reply(530, "Please login with USER and PASS."); 161170102Sphk return 0; 161270102Sphk } 161370102Sphk} 161470102Sphk 161556668Sshin#ifdef INET6 161656668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 161756668Sshinstatic int 161856668Sshinport_check_v6(pcmd) 161956668Sshin const char *pcmd; 162056668Sshin{ 162156668Sshin if (his_addr.su_family == AF_INET6) { 162256668Sshin if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 162356668Sshin /* Convert data_dest into v4 mapped sockaddr.*/ 162456668Sshin v4map_data_dest(); 162556668Sshin if (data_dest.su_family != AF_INET6) { 162656668Sshin usedefault = 1; 162756668Sshin reply(500, "Invalid address rejected."); 162856668Sshin return 1; 162956668Sshin } 163056668Sshin if (paranoid && 163156668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 163256668Sshin memcmp(&data_dest.su_sin6.sin6_addr, 163356668Sshin &his_addr.su_sin6.sin6_addr, 163456668Sshin sizeof(data_dest.su_sin6.sin6_addr)))) { 163556668Sshin usedefault = 1; 163656668Sshin reply(500, "Illegal PORT range rejected."); 163756668Sshin } else { 163856668Sshin usedefault = 0; 163956668Sshin if (pdata >= 0) { 164056668Sshin (void) close(pdata); 164156668Sshin pdata = -1; 164256668Sshin } 164356668Sshin reply(200, "%s command successful.", pcmd); 164456668Sshin } 164556668Sshin return 1; 164656668Sshin } 164756668Sshin return 0; 164856668Sshin} 164956668Sshin 165056668Sshinstatic void 165156668Sshinv4map_data_dest() 165256668Sshin{ 165356668Sshin struct in_addr savedaddr; 165456668Sshin int savedport; 165556668Sshin 165656668Sshin if (data_dest.su_family != AF_INET) { 165756668Sshin usedefault = 1; 165856668Sshin reply(500, "Invalid address rejected."); 165956668Sshin return; 166056668Sshin } 166156668Sshin 166256668Sshin savedaddr = data_dest.su_sin.sin_addr; 166356668Sshin savedport = data_dest.su_port; 166456668Sshin 166556668Sshin memset(&data_dest, 0, sizeof(data_dest)); 166656668Sshin data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 166756668Sshin data_dest.su_sin6.sin6_family = AF_INET6; 166856668Sshin data_dest.su_sin6.sin6_port = savedport; 166956668Sshin memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 167056668Sshin memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 167156668Sshin (caddr_t)&savedaddr, sizeof(savedaddr)); 167256668Sshin} 167356668Sshin#endif 1674