ftpcmd.y revision 75535
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 75535 2001-04-15 20:59:29Z phk $"; 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; 841592Srgrimesextern int debug; 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 14156668Sshin%type <s> pathstring pathname password username ext_arg 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 } 16917433Spst | PORT check_login SP host_port CRLF 1701592Srgrimes { 17156668Sshin if (epsvall) { 17256668Sshin reply(501, "no PORT allowed after EPSV ALL"); 17356668Sshin goto port_done; 17456668Sshin } 17556668Sshin if (!$2) 17656668Sshin goto port_done; 17756668Sshin if (port_check("PORT") == 1) 17856668Sshin goto port_done; 17956668Sshin#ifdef INET6 18056668Sshin if ((his_addr.su_family != AF_INET6 || 18156668Sshin !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { 18256668Sshin /* shoud never happen */ 18356668Sshin usedefault = 1; 18456668Sshin reply(500, "Invalid address rejected."); 18556668Sshin goto port_done; 18656668Sshin } 18756668Sshin port_check_v6("pcmd"); 18856668Sshin#endif 18956668Sshin port_done: 19056668Sshin } 19156668Sshin | LPRT check_login SP host_long_port CRLF 19256668Sshin { 19356668Sshin if (epsvall) { 19456668Sshin reply(501, "no LPRT allowed after EPSV ALL"); 19556668Sshin goto lprt_done; 19656668Sshin } 19756668Sshin if (!$2) 19856668Sshin goto lprt_done; 19956668Sshin if (port_check("LPRT") == 1) 20056668Sshin goto lprt_done; 20156668Sshin#ifdef INET6 20256668Sshin if (his_addr.su_family != AF_INET6) { 20356668Sshin usedefault = 1; 20456668Sshin reply(500, "Invalid address rejected."); 20556668Sshin goto lprt_done; 20656668Sshin } 20756668Sshin if (port_check_v6("LPRT") == 1) 20856668Sshin goto lprt_done; 20956668Sshin#endif 21056668Sshin lprt_done: 21156668Sshin } 21256668Sshin | EPRT check_login SP STRING CRLF 21356668Sshin { 21456668Sshin char delim; 21556668Sshin char *tmp = NULL; 21656668Sshin char *p, *q; 21756668Sshin char *result[3]; 21856668Sshin struct addrinfo hints; 21956668Sshin struct addrinfo *res; 22056668Sshin int i; 22156668Sshin 22256668Sshin if (epsvall) { 22356668Sshin reply(501, "no EPRT allowed after EPSV ALL"); 22456668Sshin goto eprt_done; 22556668Sshin } 22656668Sshin if (!$2) 22756668Sshin goto eprt_done; 22856668Sshin 22956668Sshin memset(&data_dest, 0, sizeof(data_dest)); 23056668Sshin tmp = strdup($4); 23156668Sshin if (debug) 23256668Sshin syslog(LOG_DEBUG, "%s", tmp); 23356668Sshin if (!tmp) { 23456668Sshin fatal("not enough core"); 23556668Sshin /*NOTREACHED*/ 23656668Sshin } 23756668Sshin p = tmp; 23856668Sshin delim = p[0]; 23956668Sshin p++; 24056668Sshin memset(result, 0, sizeof(result)); 24156668Sshin for (i = 0; i < 3; i++) { 24256668Sshin q = strchr(p, delim); 24356668Sshin if (!q || *q != delim) { 24456668Sshin parsefail: 24556668Sshin reply(500, 24656668Sshin "Invalid argument, rejected."); 24756668Sshin if (tmp) 24856668Sshin free(tmp); 24917433Spst usedefault = 1; 25056668Sshin goto eprt_done; 25117433Spst } 25256668Sshin *q++ = '\0'; 25356668Sshin result[i] = p; 25456668Sshin if (debug) 25556668Sshin syslog(LOG_DEBUG, "%d: %s", i, p); 25656668Sshin p = q; 2571592Srgrimes } 25856668Sshin 25956668Sshin /* some more sanity check */ 26056668Sshin p = result[0]; 26156668Sshin while (*p) { 26256668Sshin if (!isdigit(*p)) 26356668Sshin goto parsefail; 26456668Sshin p++; 26556668Sshin } 26656668Sshin p = result[2]; 26756668Sshin while (*p) { 26856668Sshin if (!isdigit(*p)) 26956668Sshin goto parsefail; 27056668Sshin p++; 27156668Sshin } 27256668Sshin 27356668Sshin /* grab address */ 27456668Sshin memset(&hints, 0, sizeof(hints)); 27556668Sshin if (atoi(result[0]) == 1) 27656668Sshin hints.ai_family = PF_INET; 27756668Sshin#ifdef INET6 27856668Sshin else if (atoi(result[0]) == 2) 27956668Sshin hints.ai_family = PF_INET6; 28056668Sshin#endif 28156668Sshin else 28256668Sshin hints.ai_family = PF_UNSPEC; /*XXX*/ 28356668Sshin hints.ai_socktype = SOCK_STREAM; 28456668Sshin i = getaddrinfo(result[1], result[2], &hints, &res); 28556668Sshin if (i) 28656668Sshin goto parsefail; 28756668Sshin memcpy(&data_dest, res->ai_addr, res->ai_addrlen); 28856668Sshin#ifdef INET6 28956668Sshin if (his_addr.su_family == AF_INET6 29056668Sshin && data_dest.su_family == AF_INET6) { 29156668Sshin /* XXX more sanity checks! */ 29256668Sshin data_dest.su_sin6.sin6_scope_id = 29356668Sshin his_addr.su_sin6.sin6_scope_id; 29456668Sshin } 29556668Sshin#endif 29656668Sshin free(tmp); 29756668Sshin tmp = NULL; 29856668Sshin 29956668Sshin if (port_check("EPRT") == 1) 30056668Sshin goto eprt_done; 30156668Sshin#ifdef INET6 30256668Sshin if (his_addr.su_family != AF_INET6) { 30356668Sshin usedefault = 1; 30456668Sshin reply(500, "Invalid address rejected."); 30556668Sshin goto eprt_done; 30656668Sshin } 30756668Sshin if (port_check_v6("EPRT") == 1) 30856668Sshin goto eprt_done; 30956668Sshin#endif 31056668Sshin eprt_done:; 3111592Srgrimes } 31217433Spst | PASV check_login CRLF 3131592Srgrimes { 31456668Sshin if (epsvall) 31556668Sshin reply(501, "no PASV allowed after EPSV ALL"); 31656668Sshin else if ($2) 31717433Spst passive(); 3181592Srgrimes } 31956668Sshin | LPSV check_login CRLF 32056668Sshin { 32156668Sshin if (epsvall) 32256668Sshin reply(501, "no LPSV allowed after EPSV ALL"); 32356668Sshin else if ($2) 32456668Sshin long_passive("LPSV", PF_UNSPEC); 32556668Sshin } 32670102Sphk | EPSV check_login_epsv SP NUMBER CRLF 32756668Sshin { 32856668Sshin if ($2) { 32956668Sshin int pf; 33056668Sshin switch ($4) { 33156668Sshin case 1: 33256668Sshin pf = PF_INET; 33356668Sshin break; 33456668Sshin#ifdef INET6 33556668Sshin case 2: 33656668Sshin pf = PF_INET6; 33756668Sshin break; 33856668Sshin#endif 33956668Sshin default: 34056668Sshin pf = -1; /*junk value*/ 34156668Sshin break; 34256668Sshin } 34356668Sshin long_passive("EPSV", pf); 34456668Sshin } 34556668Sshin } 34670102Sphk | EPSV check_login_epsv SP ALL CRLF 34756668Sshin { 34856668Sshin if ($2) { 34956668Sshin reply(200, 35056668Sshin "EPSV ALL command successful."); 35156668Sshin epsvall++; 35256668Sshin } 35356668Sshin } 35470102Sphk | EPSV check_login_epsv CRLF 35556668Sshin { 35656668Sshin if ($2) 35756668Sshin long_passive("EPSV", PF_UNSPEC); 35856668Sshin } 35971278Sjedgar | TYPE check_login SP type_code CRLF 3601592Srgrimes { 36171278Sjedgar if ($2) { 36271278Sjedgar switch (cmd_type) { 3631592Srgrimes 36471278Sjedgar case TYPE_A: 36571278Sjedgar if (cmd_form == FORM_N) { 36671278Sjedgar reply(200, "Type set to A."); 36771278Sjedgar type = cmd_type; 36871278Sjedgar form = cmd_form; 36971278Sjedgar } else 37071278Sjedgar reply(504, "Form must be N."); 37171278Sjedgar break; 3721592Srgrimes 37371278Sjedgar case TYPE_E: 37471278Sjedgar reply(504, "Type E not implemented."); 37571278Sjedgar break; 3761592Srgrimes 37771278Sjedgar case TYPE_I: 37871278Sjedgar reply(200, "Type set to I."); 37971278Sjedgar type = cmd_type; 38071278Sjedgar break; 3811592Srgrimes 38271278Sjedgar case TYPE_L: 3831592Srgrimes#if NBBY == 8 38471278Sjedgar if (cmd_bytesz == 8) { 38571278Sjedgar reply(200, 38671278Sjedgar "Type set to L (byte size 8)."); 38771278Sjedgar type = cmd_type; 38871278Sjedgar } else 38971278Sjedgar reply(504, "Byte size must be 8."); 3901592Srgrimes#else /* NBBY == 8 */ 39171278Sjedgar UNIMPLEMENTED for NBBY != 8 3921592Srgrimes#endif /* NBBY == 8 */ 39371278Sjedgar } 3941592Srgrimes } 3951592Srgrimes } 39671278Sjedgar | STRU check_login SP struct_code CRLF 3971592Srgrimes { 39871278Sjedgar if ($2) { 39971278Sjedgar switch ($4) { 4001592Srgrimes 40171278Sjedgar case STRU_F: 40271278Sjedgar reply(200, "STRU F ok."); 40371278Sjedgar break; 4041592Srgrimes 40571278Sjedgar default: 40671278Sjedgar reply(504, "Unimplemented STRU type."); 40771278Sjedgar } 4081592Srgrimes } 4091592Srgrimes } 41071278Sjedgar | MODE check_login SP mode_code CRLF 4111592Srgrimes { 41271278Sjedgar if ($2) { 41371278Sjedgar switch ($4) { 4141592Srgrimes 41571278Sjedgar case MODE_S: 41671278Sjedgar reply(200, "MODE S ok."); 41771278Sjedgar break; 41871278Sjedgar 41971278Sjedgar default: 42071278Sjedgar reply(502, "Unimplemented MODE type."); 42171278Sjedgar } 4221592Srgrimes } 4231592Srgrimes } 42471278Sjedgar | ALLO check_login SP NUMBER CRLF 4251592Srgrimes { 42671278Sjedgar if ($2) { 42771278Sjedgar reply(202, "ALLO command ignored."); 42871278Sjedgar } 4291592Srgrimes } 43071278Sjedgar | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 4311592Srgrimes { 43271278Sjedgar if ($2) { 43371278Sjedgar reply(202, "ALLO command ignored."); 43471278Sjedgar } 4351592Srgrimes } 4361592Srgrimes | RETR check_login SP pathname CRLF 4371592Srgrimes { 4381592Srgrimes if ($2 && $4 != NULL) 4391592Srgrimes retrieve((char *) 0, $4); 4401592Srgrimes if ($4 != NULL) 4411592Srgrimes free($4); 4421592Srgrimes } 44370102Sphk | STOR check_login_ro SP pathname CRLF 4441592Srgrimes { 4451592Srgrimes if ($2 && $4 != NULL) 4461592Srgrimes store($4, "w", 0); 4471592Srgrimes if ($4 != NULL) 4481592Srgrimes free($4); 4491592Srgrimes } 45070102Sphk | APPE check_login_ro SP pathname CRLF 4511592Srgrimes { 4521592Srgrimes if ($2 && $4 != NULL) 4531592Srgrimes store($4, "a", 0); 4541592Srgrimes if ($4 != NULL) 4551592Srgrimes free($4); 4561592Srgrimes } 4571592Srgrimes | NLST check_login CRLF 4581592Srgrimes { 4591592Srgrimes if ($2) 4601592Srgrimes send_file_list("."); 4611592Srgrimes } 4621592Srgrimes | NLST check_login SP STRING CRLF 4631592Srgrimes { 4641592Srgrimes if ($2 && $4 != NULL) 4651592Srgrimes send_file_list($4); 4661592Srgrimes if ($4 != NULL) 4671592Srgrimes free($4); 4681592Srgrimes } 4691592Srgrimes | LIST check_login CRLF 4701592Srgrimes { 4711592Srgrimes if ($2) 4721592Srgrimes retrieve("/bin/ls -lgA", ""); 4731592Srgrimes } 4741592Srgrimes | LIST check_login SP pathname CRLF 4751592Srgrimes { 4761592Srgrimes if ($2 && $4 != NULL) 4771592Srgrimes retrieve("/bin/ls -lgA %s", $4); 4781592Srgrimes if ($4 != NULL) 4791592Srgrimes free($4); 4801592Srgrimes } 4811592Srgrimes | STAT check_login SP pathname CRLF 4821592Srgrimes { 4831592Srgrimes if ($2 && $4 != NULL) 4841592Srgrimes statfilecmd($4); 4851592Srgrimes if ($4 != NULL) 4861592Srgrimes free($4); 4871592Srgrimes } 48871278Sjedgar | STAT check_login CRLF 4891592Srgrimes { 49071278Sjedgar if ($2) { 49171278Sjedgar statcmd(); 49271278Sjedgar } 4931592Srgrimes } 49470102Sphk | DELE check_login_ro SP pathname CRLF 4951592Srgrimes { 4961592Srgrimes if ($2 && $4 != NULL) 4971592Srgrimes delete($4); 4981592Srgrimes if ($4 != NULL) 4991592Srgrimes free($4); 5001592Srgrimes } 50170102Sphk | RNTO check_login_ro SP pathname CRLF 5021592Srgrimes { 50317433Spst if ($2) { 50417433Spst if (fromname) { 50517433Spst renamecmd(fromname, $4); 50617433Spst free(fromname); 50717433Spst fromname = (char *) 0; 50817433Spst } else { 50917433Spst reply(503, "Bad sequence of commands."); 51017433Spst } 5111592Srgrimes } 51217433Spst free($4); 5131592Srgrimes } 51471278Sjedgar | ABOR check_login CRLF 5151592Srgrimes { 51671278Sjedgar if ($2) 51771278Sjedgar reply(225, "ABOR command successful."); 5181592Srgrimes } 5191592Srgrimes | CWD check_login CRLF 5201592Srgrimes { 52169234Sdanny if ($2) { 52269234Sdanny if (guest) 52369234Sdanny cwd("/"); 52469234Sdanny else 52569234Sdanny cwd(pw->pw_dir); 52669234Sdanny } 5271592Srgrimes } 5281592Srgrimes | CWD check_login SP pathname CRLF 5291592Srgrimes { 5301592Srgrimes if ($2 && $4 != NULL) 5311592Srgrimes cwd($4); 5321592Srgrimes if ($4 != NULL) 5331592Srgrimes free($4); 5341592Srgrimes } 5351592Srgrimes | HELP CRLF 5361592Srgrimes { 5371592Srgrimes help(cmdtab, (char *) 0); 5381592Srgrimes } 5391592Srgrimes | HELP SP STRING CRLF 5401592Srgrimes { 5411592Srgrimes char *cp = $3; 5421592Srgrimes 5431592Srgrimes if (strncasecmp(cp, "SITE", 4) == 0) { 5441592Srgrimes cp = $3 + 4; 5451592Srgrimes if (*cp == ' ') 5461592Srgrimes cp++; 5471592Srgrimes if (*cp) 5481592Srgrimes help(sitetab, cp); 5491592Srgrimes else 5501592Srgrimes help(sitetab, (char *) 0); 5511592Srgrimes } else 5521592Srgrimes help(cmdtab, $3); 5531592Srgrimes } 5541592Srgrimes | NOOP CRLF 5551592Srgrimes { 5561592Srgrimes reply(200, "NOOP command successful."); 5571592Srgrimes } 55870102Sphk | MKD check_login_ro SP pathname CRLF 5591592Srgrimes { 5601592Srgrimes if ($2 && $4 != NULL) 5611592Srgrimes makedir($4); 5621592Srgrimes if ($4 != NULL) 5631592Srgrimes free($4); 5641592Srgrimes } 56570102Sphk | RMD check_login_ro SP pathname CRLF 5661592Srgrimes { 5671592Srgrimes if ($2 && $4 != NULL) 5681592Srgrimes removedir($4); 5691592Srgrimes if ($4 != NULL) 5701592Srgrimes free($4); 5711592Srgrimes } 5721592Srgrimes | PWD check_login CRLF 5731592Srgrimes { 5741592Srgrimes if ($2) 5751592Srgrimes pwd(); 5761592Srgrimes } 5771592Srgrimes | CDUP check_login CRLF 5781592Srgrimes { 5791592Srgrimes if ($2) 5801592Srgrimes cwd(".."); 5811592Srgrimes } 5821592Srgrimes | SITE SP HELP CRLF 5831592Srgrimes { 5841592Srgrimes help(sitetab, (char *) 0); 5851592Srgrimes } 5861592Srgrimes | SITE SP HELP SP STRING CRLF 5871592Srgrimes { 5881592Srgrimes help(sitetab, $5); 5891592Srgrimes } 59075535Sphk | SITE SP MDFIVE check_login SP pathname CRLF 59175535Sphk { 59275535Sphk char p[64], *q; 59375535Sphk 59475535Sphk if ($4) { 59575535Sphk q = MD5File($6, p); 59675535Sphk if (q != NULL) 59775535Sphk reply(200, "MD5(%s) = %s", $6, p); 59875535Sphk else 59975535Sphk perror_reply(550, $6); 60075535Sphk } 60175535Sphk } 6021592Srgrimes | SITE SP UMASK check_login CRLF 6031592Srgrimes { 6041592Srgrimes int oldmask; 6051592Srgrimes 6061592Srgrimes if ($4) { 6071592Srgrimes oldmask = umask(0); 6081592Srgrimes (void) umask(oldmask); 6091592Srgrimes reply(200, "Current UMASK is %03o", oldmask); 6101592Srgrimes } 6111592Srgrimes } 6121592Srgrimes | SITE SP UMASK check_login SP octal_number CRLF 6131592Srgrimes { 6141592Srgrimes int oldmask; 6151592Srgrimes 6161592Srgrimes if ($4) { 6171592Srgrimes if (($6 == -1) || ($6 > 0777)) { 6181592Srgrimes reply(501, "Bad UMASK value"); 6191592Srgrimes } else { 6201592Srgrimes oldmask = umask($6); 6211592Srgrimes reply(200, 6221592Srgrimes "UMASK set to %03o (was %03o)", 6231592Srgrimes $6, oldmask); 6241592Srgrimes } 6251592Srgrimes } 6261592Srgrimes } 62770102Sphk | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF 6281592Srgrimes { 6291592Srgrimes if ($4 && ($8 != NULL)) { 6301592Srgrimes if ($6 > 0777) 6311592Srgrimes reply(501, 6321592Srgrimes "CHMOD: Mode value must be between 0 and 0777"); 6331592Srgrimes else if (chmod($8, $6) < 0) 6341592Srgrimes perror_reply(550, $8); 6351592Srgrimes else 6361592Srgrimes reply(200, "CHMOD command successful."); 6371592Srgrimes } 6381592Srgrimes if ($8 != NULL) 6391592Srgrimes free($8); 6401592Srgrimes } 64171278Sjedgar | SITE SP check_login IDLE CRLF 6421592Srgrimes { 64371278Sjedgar if ($3) 64471278Sjedgar reply(200, 64571278Sjedgar "Current IDLE time limit is %d seconds; max %d", 64671278Sjedgar timeout, maxtimeout); 6471592Srgrimes } 64871278Sjedgar | SITE SP check_login IDLE SP NUMBER CRLF 6491592Srgrimes { 65071278Sjedgar if ($3) { 65171278Sjedgar if ($6 < 30 || $6 > maxtimeout) { 65271278Sjedgar reply(501, 65371278Sjedgar "Maximum IDLE time must be between 30 and %d seconds", 65471278Sjedgar maxtimeout); 65571278Sjedgar } else { 65671278Sjedgar timeout = $6; 65771278Sjedgar (void) alarm((unsigned) timeout); 65871278Sjedgar reply(200, 65971278Sjedgar "Maximum IDLE time set to %d seconds", 66071278Sjedgar timeout); 66171278Sjedgar } 6621592Srgrimes } 6631592Srgrimes } 66470102Sphk | STOU check_login_ro SP pathname CRLF 6651592Srgrimes { 6661592Srgrimes if ($2 && $4 != NULL) 6671592Srgrimes store($4, "w", 1); 6681592Srgrimes if ($4 != NULL) 6691592Srgrimes free($4); 6701592Srgrimes } 67171278Sjedgar | SYST check_login CRLF 6721592Srgrimes { 67371278Sjedgar if ($2) 6741592Srgrimes#ifdef unix 6751592Srgrimes#ifdef BSD 6761592Srgrimes reply(215, "UNIX Type: L%d Version: BSD-%d", 6771592Srgrimes NBBY, BSD); 6781592Srgrimes#else /* BSD */ 6791592Srgrimes reply(215, "UNIX Type: L%d", NBBY); 6801592Srgrimes#endif /* BSD */ 6811592Srgrimes#else /* unix */ 6821592Srgrimes reply(215, "UNKNOWN Type: L%d", NBBY); 6831592Srgrimes#endif /* unix */ 6841592Srgrimes } 6851592Srgrimes 6861592Srgrimes /* 6871592Srgrimes * SIZE is not in RFC959, but Postel has blessed it and 6881592Srgrimes * it will be in the updated RFC. 6891592Srgrimes * 6901592Srgrimes * Return size of file in a format suitable for 6911592Srgrimes * using with RESTART (we just count bytes). 6921592Srgrimes */ 6931592Srgrimes | SIZE check_login SP pathname CRLF 6941592Srgrimes { 6951592Srgrimes if ($2 && $4 != NULL) 6961592Srgrimes sizecmd($4); 6971592Srgrimes if ($4 != NULL) 6981592Srgrimes free($4); 6991592Srgrimes } 7001592Srgrimes 7011592Srgrimes /* 7021592Srgrimes * MDTM is not in RFC959, but Postel has blessed it and 7031592Srgrimes * it will be in the updated RFC. 7041592Srgrimes * 7051592Srgrimes * Return modification time of file as an ISO 3307 7061592Srgrimes * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 7071592Srgrimes * where xxx is the fractional second (of any precision, 7081592Srgrimes * not necessarily 3 digits) 7091592Srgrimes */ 7101592Srgrimes | MDTM check_login SP pathname CRLF 7111592Srgrimes { 7121592Srgrimes if ($2 && $4 != NULL) { 7131592Srgrimes struct stat stbuf; 7141592Srgrimes if (stat($4, &stbuf) < 0) 7151592Srgrimes reply(550, "%s: %s", 7161592Srgrimes $4, strerror(errno)); 7171592Srgrimes else if (!S_ISREG(stbuf.st_mode)) { 7181592Srgrimes reply(550, "%s: not a plain file.", $4); 7191592Srgrimes } else { 7201592Srgrimes struct tm *t; 7211592Srgrimes t = gmtime(&stbuf.st_mtime); 7221592Srgrimes reply(213, 72317435Spst "%04d%02d%02d%02d%02d%02d", 72417435Spst 1900 + t->tm_year, 72517435Spst t->tm_mon+1, t->tm_mday, 7261592Srgrimes t->tm_hour, t->tm_min, t->tm_sec); 7271592Srgrimes } 7281592Srgrimes } 7291592Srgrimes if ($4 != NULL) 7301592Srgrimes free($4); 7311592Srgrimes } 7321592Srgrimes | QUIT CRLF 7331592Srgrimes { 7341592Srgrimes reply(221, "Goodbye."); 7351592Srgrimes dologout(0); 7361592Srgrimes } 7371592Srgrimes | error CRLF 7381592Srgrimes { 7391592Srgrimes yyerrok; 7401592Srgrimes } 7411592Srgrimes ; 7421592Srgrimesrcmd 74370102Sphk : RNFR check_login_ro SP pathname CRLF 7441592Srgrimes { 7451592Srgrimes char *renamefrom(); 7461592Srgrimes 7471592Srgrimes restart_point = (off_t) 0; 7481592Srgrimes if ($2 && $4) { 7491592Srgrimes fromname = renamefrom($4); 7501592Srgrimes if (fromname == (char *) 0 && $4) { 7511592Srgrimes free($4); 7521592Srgrimes } 7531592Srgrimes } 7541592Srgrimes } 75571278Sjedgar | REST check_login SP byte_size CRLF 7561592Srgrimes { 75771278Sjedgar if ($2) { 75871278Sjedgar fromname = (char *) 0; 75971278Sjedgar restart_point = $4; /* XXX $4 is only "int" */ 76071278Sjedgar reply(350, "Restarting at %qd. %s", 76171278Sjedgar restart_point, 76271278Sjedgar "Send STORE or RETRIEVE to initiate transfer."); 76371278Sjedgar } 7641592Srgrimes } 7651592Srgrimes ; 7661592Srgrimes 7671592Srgrimesusername 7681592Srgrimes : STRING 7691592Srgrimes ; 7701592Srgrimes 7711592Srgrimespassword 7721592Srgrimes : /* empty */ 7731592Srgrimes { 7741592Srgrimes $$ = (char *)calloc(1, sizeof(char)); 7751592Srgrimes } 7761592Srgrimes | STRING 7771592Srgrimes ; 7781592Srgrimes 7791592Srgrimesbyte_size 7801592Srgrimes : NUMBER 7811592Srgrimes ; 7821592Srgrimes 7831592Srgrimeshost_port 7841592Srgrimes : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 7851592Srgrimes NUMBER COMMA NUMBER 7861592Srgrimes { 7871592Srgrimes char *a, *p; 7881592Srgrimes 78956668Sshin data_dest.su_len = sizeof(struct sockaddr_in); 79056668Sshin data_dest.su_family = AF_INET; 79156668Sshin p = (char *)&data_dest.su_sin.sin_port; 79217435Spst p[0] = $9; p[1] = $11; 79356668Sshin a = (char *)&data_dest.su_sin.sin_addr; 7941592Srgrimes a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 7951592Srgrimes } 7961592Srgrimes ; 7971592Srgrimes 79856668Sshinhost_long_port 79956668Sshin : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 80056668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 80156668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 80256668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 80356668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 80456668Sshin NUMBER 80556668Sshin { 80656668Sshin char *a, *p; 80756668Sshin 80856668Sshin memset(&data_dest, 0, sizeof(data_dest)); 80956668Sshin data_dest.su_len = sizeof(struct sockaddr_in6); 81056668Sshin data_dest.su_family = AF_INET6; 81156668Sshin p = (char *)&data_dest.su_port; 81256668Sshin p[0] = $39; p[1] = $41; 81356668Sshin a = (char *)&data_dest.su_sin6.sin6_addr; 81456668Sshin a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 81556668Sshin a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19; 81656668Sshin a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27; 81756668Sshin a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35; 81856668Sshin if (his_addr.su_family == AF_INET6) { 81956668Sshin /* XXX more sanity checks! */ 82056668Sshin data_dest.su_sin6.sin6_scope_id = 82156668Sshin his_addr.su_sin6.sin6_scope_id; 82256668Sshin } 82356668Sshin if ($1 != 6 || $3 != 16 || $37 != 2) 82456668Sshin memset(&data_dest, 0, sizeof(data_dest)); 82556668Sshin } 82656668Sshin | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 82756668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 82856668Sshin NUMBER 82956668Sshin { 83056668Sshin char *a, *p; 83156668Sshin 83256668Sshin memset(&data_dest, 0, sizeof(data_dest)); 83356668Sshin data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 83456668Sshin data_dest.su_family = AF_INET; 83556668Sshin p = (char *)&data_dest.su_port; 83656668Sshin p[0] = $15; p[1] = $17; 83756668Sshin a = (char *)&data_dest.su_sin.sin_addr; 83856668Sshin a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 83956668Sshin if ($1 != 4 || $3 != 4 || $13 != 2) 84056668Sshin memset(&data_dest, 0, sizeof(data_dest)); 84156668Sshin } 84256668Sshin ; 84356668Sshin 8441592Srgrimesform_code 8451592Srgrimes : N 8461592Srgrimes { 8471592Srgrimes $$ = FORM_N; 8481592Srgrimes } 8491592Srgrimes | T 8501592Srgrimes { 8511592Srgrimes $$ = FORM_T; 8521592Srgrimes } 8531592Srgrimes | C 8541592Srgrimes { 8551592Srgrimes $$ = FORM_C; 8561592Srgrimes } 8571592Srgrimes ; 8581592Srgrimes 8591592Srgrimestype_code 8601592Srgrimes : A 8611592Srgrimes { 8621592Srgrimes cmd_type = TYPE_A; 8631592Srgrimes cmd_form = FORM_N; 8641592Srgrimes } 8651592Srgrimes | A SP form_code 8661592Srgrimes { 8671592Srgrimes cmd_type = TYPE_A; 8681592Srgrimes cmd_form = $3; 8691592Srgrimes } 8701592Srgrimes | E 8711592Srgrimes { 8721592Srgrimes cmd_type = TYPE_E; 8731592Srgrimes cmd_form = FORM_N; 8741592Srgrimes } 8751592Srgrimes | E SP form_code 8761592Srgrimes { 8771592Srgrimes cmd_type = TYPE_E; 8781592Srgrimes cmd_form = $3; 8791592Srgrimes } 8801592Srgrimes | I 8811592Srgrimes { 8821592Srgrimes cmd_type = TYPE_I; 8831592Srgrimes } 8841592Srgrimes | L 8851592Srgrimes { 8861592Srgrimes cmd_type = TYPE_L; 8871592Srgrimes cmd_bytesz = NBBY; 8881592Srgrimes } 8891592Srgrimes | L SP byte_size 8901592Srgrimes { 8911592Srgrimes cmd_type = TYPE_L; 8921592Srgrimes cmd_bytesz = $3; 8931592Srgrimes } 8941592Srgrimes /* this is for a bug in the BBN ftp */ 8951592Srgrimes | L byte_size 8961592Srgrimes { 8971592Srgrimes cmd_type = TYPE_L; 8981592Srgrimes cmd_bytesz = $2; 8991592Srgrimes } 9001592Srgrimes ; 9011592Srgrimes 9021592Srgrimesstruct_code 9031592Srgrimes : F 9041592Srgrimes { 9051592Srgrimes $$ = STRU_F; 9061592Srgrimes } 9071592Srgrimes | R 9081592Srgrimes { 9091592Srgrimes $$ = STRU_R; 9101592Srgrimes } 9111592Srgrimes | P 9121592Srgrimes { 9131592Srgrimes $$ = STRU_P; 9141592Srgrimes } 9151592Srgrimes ; 9161592Srgrimes 9171592Srgrimesmode_code 9181592Srgrimes : S 9191592Srgrimes { 9201592Srgrimes $$ = MODE_S; 9211592Srgrimes } 9221592Srgrimes | B 9231592Srgrimes { 9241592Srgrimes $$ = MODE_B; 9251592Srgrimes } 9261592Srgrimes | C 9271592Srgrimes { 9281592Srgrimes $$ = MODE_C; 9291592Srgrimes } 9301592Srgrimes ; 9311592Srgrimes 9321592Srgrimespathname 9331592Srgrimes : pathstring 9341592Srgrimes { 9351592Srgrimes /* 9361592Srgrimes * Problem: this production is used for all pathname 9371592Srgrimes * processing, but only gives a 550 error reply. 9381592Srgrimes * This is a valid reply in some cases but not in others. 9391592Srgrimes */ 9401592Srgrimes if (logged_in && $1 && *$1 == '~') { 9411592Srgrimes glob_t gl; 9421592Srgrimes int flags = 9431592Srgrimes GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 9441592Srgrimes 9451592Srgrimes memset(&gl, 0, sizeof(gl)); 9461592Srgrimes if (glob($1, flags, NULL, &gl) || 9471592Srgrimes gl.gl_pathc == 0) { 9481592Srgrimes reply(550, "not found"); 9491592Srgrimes $$ = NULL; 9501592Srgrimes } else { 9511592Srgrimes $$ = strdup(gl.gl_pathv[0]); 9521592Srgrimes } 9531592Srgrimes globfree(&gl); 9541592Srgrimes free($1); 9551592Srgrimes } else 9561592Srgrimes $$ = $1; 9571592Srgrimes } 9581592Srgrimes ; 9591592Srgrimes 9601592Srgrimespathstring 9611592Srgrimes : STRING 9621592Srgrimes ; 9631592Srgrimes 9641592Srgrimesoctal_number 9651592Srgrimes : NUMBER 9661592Srgrimes { 9671592Srgrimes int ret, dec, multby, digit; 9681592Srgrimes 9691592Srgrimes /* 9701592Srgrimes * Convert a number that was read as decimal number 9711592Srgrimes * to what it would be if it had been read as octal. 9721592Srgrimes */ 9731592Srgrimes dec = $1; 9741592Srgrimes multby = 1; 9751592Srgrimes ret = 0; 9761592Srgrimes while (dec) { 9771592Srgrimes digit = dec%10; 9781592Srgrimes if (digit > 7) { 9791592Srgrimes ret = -1; 9801592Srgrimes break; 9811592Srgrimes } 9821592Srgrimes ret += digit * multby; 9831592Srgrimes multby *= 8; 9841592Srgrimes dec /= 10; 9851592Srgrimes } 9861592Srgrimes $$ = ret; 9871592Srgrimes } 9881592Srgrimes ; 9891592Srgrimes 9901592Srgrimes 9911592Srgrimescheck_login 9921592Srgrimes : /* empty */ 9931592Srgrimes { 99470102Sphk $$ = check_login1(); 9951592Srgrimes } 9961592Srgrimes ; 9971592Srgrimes 99870102Sphkcheck_login_epsv 99970102Sphk : /* empty */ 100070102Sphk { 100170102Sphk if (noepsv) { 100270102Sphk reply(500, "EPSV command disabled"); 100370102Sphk $$ = 0; 100470102Sphk } 100570102Sphk else 100670102Sphk $$ = check_login1(); 100770102Sphk } 100870102Sphk ; 100970102Sphk 101070102Sphkcheck_login_ro 101170102Sphk : /* empty */ 101270102Sphk { 101370102Sphk if (readonly) { 101472710Sdes reply(550, "Permission denied."); 101570102Sphk $$ = 0; 101670102Sphk } 101770102Sphk else 101870102Sphk $$ = check_login1(); 101970102Sphk } 102070102Sphk ; 102170102Sphk 10221592Srgrimes%% 10231592Srgrimes 10241592Srgrimesextern jmp_buf errcatch; 10251592Srgrimes 10261592Srgrimes#define CMD 0 /* beginning of command */ 10271592Srgrimes#define ARGS 1 /* expect miscellaneous arguments */ 10281592Srgrimes#define STR1 2 /* expect SP followed by STRING */ 10291592Srgrimes#define STR2 3 /* expect STRING */ 10301592Srgrimes#define OSTR 4 /* optional SP then STRING */ 10311592Srgrimes#define ZSTR1 5 /* SP then optional STRING */ 10321592Srgrimes#define ZSTR2 6 /* optional STRING after SP */ 10331592Srgrimes#define SITECMD 7 /* SITE command */ 10341592Srgrimes#define NSTR 8 /* Number followed by a string */ 10351592Srgrimes 10361592Srgrimesstruct tab { 10371592Srgrimes char *name; 10381592Srgrimes short token; 10391592Srgrimes short state; 10401592Srgrimes short implemented; /* 1 if command is implemented */ 10411592Srgrimes char *help; 10421592Srgrimes}; 10431592Srgrimes 10441592Srgrimesstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 10451592Srgrimes { "USER", USER, STR1, 1, "<sp> username" }, 10461592Srgrimes { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 10471592Srgrimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 10481592Srgrimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 10491592Srgrimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 10501592Srgrimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 10511592Srgrimes { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 105256668Sshin { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 105356668Sshin { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 10541592Srgrimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 105556668Sshin { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 105656668Sshin { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 10571592Srgrimes { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 10581592Srgrimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 10591592Srgrimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 10601592Srgrimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 10611592Srgrimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 10621592Srgrimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 10631592Srgrimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 10641592Srgrimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 10651592Srgrimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 10661592Srgrimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 10671592Srgrimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 10681592Srgrimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 10691592Srgrimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 10701592Srgrimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 10711592Srgrimes { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 10721592Srgrimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 10731592Srgrimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 10741592Srgrimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 10751592Srgrimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 10761592Srgrimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 10771592Srgrimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 10781592Srgrimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 10791592Srgrimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 10801592Srgrimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 10811592Srgrimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 10821592Srgrimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 10831592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 10841592Srgrimes { "NOOP", NOOP, ARGS, 1, "" }, 10851592Srgrimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 10861592Srgrimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 10871592Srgrimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 10881592Srgrimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 10891592Srgrimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 10901592Srgrimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 10911592Srgrimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 10921592Srgrimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 10931592Srgrimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 10941592Srgrimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 10951592Srgrimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 10961592Srgrimes { NULL, 0, 0, 0, 0 } 10971592Srgrimes}; 10981592Srgrimes 10991592Srgrimesstruct tab sitetab[] = { 110075535Sphk { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" }, 11011592Srgrimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 11021592Srgrimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 11031592Srgrimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 11041592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 11051592Srgrimes { NULL, 0, 0, 0, 0 } 11061592Srgrimes}; 11071592Srgrimes 11081592Srgrimesstatic char *copy __P((char *)); 11091592Srgrimesstatic void help __P((struct tab *, char *)); 11101592Srgrimesstatic struct tab * 11111592Srgrimes lookup __P((struct tab *, char *)); 111256668Sshinstatic int port_check __P((const char *)); 111356668Sshinstatic int port_check_v6 __P((const char *)); 11141592Srgrimesstatic void sizecmd __P((char *)); 11151592Srgrimesstatic void toolong __P((int)); 111656668Sshinstatic void v4map_data_dest __P((void)); 11171592Srgrimesstatic int yylex __P((void)); 11181592Srgrimes 11191592Srgrimesstatic struct tab * 11201592Srgrimeslookup(p, cmd) 11211592Srgrimes struct tab *p; 11221592Srgrimes char *cmd; 11231592Srgrimes{ 11241592Srgrimes 11251592Srgrimes for (; p->name != NULL; p++) 11261592Srgrimes if (strcmp(cmd, p->name) == 0) 11271592Srgrimes return (p); 11281592Srgrimes return (0); 11291592Srgrimes} 11301592Srgrimes 11311592Srgrimes#include <arpa/telnet.h> 11321592Srgrimes 11331592Srgrimes/* 11341592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes. 11351592Srgrimes */ 11361592Srgrimeschar * 11371592Srgrimesgetline(s, n, iop) 11381592Srgrimes char *s; 11391592Srgrimes int n; 11401592Srgrimes FILE *iop; 11411592Srgrimes{ 11421592Srgrimes int c; 11431592Srgrimes register char *cs; 11441592Srgrimes 11451592Srgrimes cs = s; 11461592Srgrimes/* tmpline may contain saved command from urgent mode interruption */ 11471592Srgrimes for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 11481592Srgrimes *cs++ = tmpline[c]; 11491592Srgrimes if (tmpline[c] == '\n') { 11501592Srgrimes *cs++ = '\0'; 11511592Srgrimes if (debug) 11521592Srgrimes syslog(LOG_DEBUG, "command: %s", s); 11531592Srgrimes tmpline[0] = '\0'; 11541592Srgrimes return(s); 11551592Srgrimes } 11561592Srgrimes if (c == 0) 11571592Srgrimes tmpline[0] = '\0'; 11581592Srgrimes } 11591592Srgrimes while ((c = getc(iop)) != EOF) { 11601592Srgrimes c &= 0377; 11611592Srgrimes if (c == IAC) { 11621592Srgrimes if ((c = getc(iop)) != EOF) { 11631592Srgrimes c &= 0377; 11641592Srgrimes switch (c) { 11651592Srgrimes case WILL: 11661592Srgrimes case WONT: 11671592Srgrimes c = getc(iop); 11681592Srgrimes printf("%c%c%c", IAC, DONT, 0377&c); 11691592Srgrimes (void) fflush(stdout); 11701592Srgrimes continue; 11711592Srgrimes case DO: 11721592Srgrimes case DONT: 11731592Srgrimes c = getc(iop); 11741592Srgrimes printf("%c%c%c", IAC, WONT, 0377&c); 11751592Srgrimes (void) fflush(stdout); 11761592Srgrimes continue; 11771592Srgrimes case IAC: 11781592Srgrimes break; 11791592Srgrimes default: 11801592Srgrimes continue; /* ignore command */ 11811592Srgrimes } 11821592Srgrimes } 11831592Srgrimes } 11841592Srgrimes *cs++ = c; 11851592Srgrimes if (--n <= 0 || c == '\n') 11861592Srgrimes break; 11871592Srgrimes } 11881592Srgrimes if (c == EOF && cs == s) 11891592Srgrimes return (NULL); 11901592Srgrimes *cs++ = '\0'; 11911592Srgrimes if (debug) { 11921592Srgrimes if (!guest && strncasecmp("pass ", s, 5) == 0) { 11931592Srgrimes /* Don't syslog passwords */ 11941592Srgrimes syslog(LOG_DEBUG, "command: %.5s ???", s); 11951592Srgrimes } else { 11961592Srgrimes register char *cp; 11971592Srgrimes register int len; 11981592Srgrimes 11991592Srgrimes /* Don't syslog trailing CR-LF */ 12001592Srgrimes len = strlen(s); 12011592Srgrimes cp = s + len - 1; 12021592Srgrimes while (cp >= s && (*cp == '\n' || *cp == '\r')) { 12031592Srgrimes --cp; 12041592Srgrimes --len; 12051592Srgrimes } 12061592Srgrimes syslog(LOG_DEBUG, "command: %.*s", len, s); 12071592Srgrimes } 12081592Srgrimes } 12091592Srgrimes return (s); 12101592Srgrimes} 12111592Srgrimes 12121592Srgrimesstatic void 12131592Srgrimestoolong(signo) 12141592Srgrimes int signo; 12151592Srgrimes{ 12161592Srgrimes 12171592Srgrimes reply(421, 12181592Srgrimes "Timeout (%d seconds): closing control connection.", timeout); 12191592Srgrimes if (logging) 12201592Srgrimes syslog(LOG_INFO, "User %s timed out after %d seconds", 12211592Srgrimes (pw ? pw -> pw_name : "unknown"), timeout); 12221592Srgrimes dologout(1); 12231592Srgrimes} 12241592Srgrimes 12251592Srgrimesstatic int 12261592Srgrimesyylex() 12271592Srgrimes{ 12281592Srgrimes static int cpos, state; 12291592Srgrimes char *cp, *cp2; 12301592Srgrimes struct tab *p; 12311592Srgrimes int n; 12321592Srgrimes char c; 12331592Srgrimes 12341592Srgrimes for (;;) { 12351592Srgrimes switch (state) { 12361592Srgrimes 12371592Srgrimes case CMD: 12381592Srgrimes (void) signal(SIGALRM, toolong); 12391592Srgrimes (void) alarm((unsigned) timeout); 12401592Srgrimes if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 12411592Srgrimes reply(221, "You could at least say goodbye."); 12421592Srgrimes dologout(0); 12431592Srgrimes } 12441592Srgrimes (void) alarm(0); 12451592Srgrimes#ifdef SETPROCTITLE 124629574Sphk if (strncasecmp(cbuf, "PASS", 4) != 0) 12471592Srgrimes setproctitle("%s: %s", proctitle, cbuf); 12481592Srgrimes#endif /* SETPROCTITLE */ 12491592Srgrimes if ((cp = strchr(cbuf, '\r'))) { 12501592Srgrimes *cp++ = '\n'; 12511592Srgrimes *cp = '\0'; 12521592Srgrimes } 12531592Srgrimes if ((cp = strpbrk(cbuf, " \n"))) 12541592Srgrimes cpos = cp - cbuf; 12551592Srgrimes if (cpos == 0) 12561592Srgrimes cpos = 4; 12571592Srgrimes c = cbuf[cpos]; 12581592Srgrimes cbuf[cpos] = '\0'; 12591592Srgrimes upper(cbuf); 12601592Srgrimes p = lookup(cmdtab, cbuf); 12611592Srgrimes cbuf[cpos] = c; 12623776Spst if (p != 0) { 12631592Srgrimes if (p->implemented == 0) { 12641592Srgrimes nack(p->name); 12651592Srgrimes longjmp(errcatch,0); 12661592Srgrimes /* NOTREACHED */ 12671592Srgrimes } 12681592Srgrimes state = p->state; 12691592Srgrimes yylval.s = p->name; 12701592Srgrimes return (p->token); 12711592Srgrimes } 12721592Srgrimes break; 12731592Srgrimes 12741592Srgrimes case SITECMD: 12751592Srgrimes if (cbuf[cpos] == ' ') { 12761592Srgrimes cpos++; 12771592Srgrimes return (SP); 12781592Srgrimes } 12791592Srgrimes cp = &cbuf[cpos]; 12801592Srgrimes if ((cp2 = strpbrk(cp, " \n"))) 12811592Srgrimes cpos = cp2 - cbuf; 12821592Srgrimes c = cbuf[cpos]; 12831592Srgrimes cbuf[cpos] = '\0'; 12841592Srgrimes upper(cp); 12851592Srgrimes p = lookup(sitetab, cp); 12861592Srgrimes cbuf[cpos] = c; 12873777Spst if (guest == 0 && p != 0) { 12881592Srgrimes if (p->implemented == 0) { 12891592Srgrimes state = CMD; 12901592Srgrimes nack(p->name); 12911592Srgrimes longjmp(errcatch,0); 12921592Srgrimes /* NOTREACHED */ 12931592Srgrimes } 12941592Srgrimes state = p->state; 12951592Srgrimes yylval.s = p->name; 12961592Srgrimes return (p->token); 12971592Srgrimes } 12981592Srgrimes state = CMD; 12991592Srgrimes break; 13001592Srgrimes 13011592Srgrimes case OSTR: 13021592Srgrimes if (cbuf[cpos] == '\n') { 13031592Srgrimes state = CMD; 13041592Srgrimes return (CRLF); 13051592Srgrimes } 13061592Srgrimes /* FALLTHROUGH */ 13071592Srgrimes 13081592Srgrimes case STR1: 13091592Srgrimes case ZSTR1: 13101592Srgrimes dostr1: 13111592Srgrimes if (cbuf[cpos] == ' ') { 13121592Srgrimes cpos++; 131351979Salfred state = state == OSTR ? STR2 : state+1; 13141592Srgrimes return (SP); 13151592Srgrimes } 13161592Srgrimes break; 13171592Srgrimes 13181592Srgrimes case ZSTR2: 13191592Srgrimes if (cbuf[cpos] == '\n') { 13201592Srgrimes state = CMD; 13211592Srgrimes return (CRLF); 13221592Srgrimes } 13231592Srgrimes /* FALLTHROUGH */ 13241592Srgrimes 13251592Srgrimes case STR2: 13261592Srgrimes cp = &cbuf[cpos]; 13271592Srgrimes n = strlen(cp); 13281592Srgrimes cpos += n - 1; 13291592Srgrimes /* 13301592Srgrimes * Make sure the string is nonempty and \n terminated. 13311592Srgrimes */ 13321592Srgrimes if (n > 1 && cbuf[cpos] == '\n') { 13331592Srgrimes cbuf[cpos] = '\0'; 13341592Srgrimes yylval.s = copy(cp); 13351592Srgrimes cbuf[cpos] = '\n'; 13361592Srgrimes state = ARGS; 13371592Srgrimes return (STRING); 13381592Srgrimes } 13391592Srgrimes break; 13401592Srgrimes 13411592Srgrimes case NSTR: 13421592Srgrimes if (cbuf[cpos] == ' ') { 13431592Srgrimes cpos++; 13441592Srgrimes return (SP); 13451592Srgrimes } 13461592Srgrimes if (isdigit(cbuf[cpos])) { 13471592Srgrimes cp = &cbuf[cpos]; 13481592Srgrimes while (isdigit(cbuf[++cpos])) 13491592Srgrimes ; 13501592Srgrimes c = cbuf[cpos]; 13511592Srgrimes cbuf[cpos] = '\0'; 13521592Srgrimes yylval.i = atoi(cp); 13531592Srgrimes cbuf[cpos] = c; 13541592Srgrimes state = STR1; 13551592Srgrimes return (NUMBER); 13561592Srgrimes } 13571592Srgrimes state = STR1; 13581592Srgrimes goto dostr1; 13591592Srgrimes 13601592Srgrimes case ARGS: 13611592Srgrimes if (isdigit(cbuf[cpos])) { 13621592Srgrimes cp = &cbuf[cpos]; 13631592Srgrimes while (isdigit(cbuf[++cpos])) 13641592Srgrimes ; 13651592Srgrimes c = cbuf[cpos]; 13661592Srgrimes cbuf[cpos] = '\0'; 13671592Srgrimes yylval.i = atoi(cp); 13681592Srgrimes cbuf[cpos] = c; 13691592Srgrimes return (NUMBER); 13701592Srgrimes } 137156668Sshin if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 137256668Sshin && !isalnum(cbuf[cpos + 3])) { 137356668Sshin cpos += 3; 137456668Sshin return ALL; 137556668Sshin } 13761592Srgrimes switch (cbuf[cpos++]) { 13771592Srgrimes 13781592Srgrimes case '\n': 13791592Srgrimes state = CMD; 13801592Srgrimes return (CRLF); 13811592Srgrimes 13821592Srgrimes case ' ': 13831592Srgrimes return (SP); 13841592Srgrimes 13851592Srgrimes case ',': 13861592Srgrimes return (COMMA); 13871592Srgrimes 13881592Srgrimes case 'A': 13891592Srgrimes case 'a': 13901592Srgrimes return (A); 13911592Srgrimes 13921592Srgrimes case 'B': 13931592Srgrimes case 'b': 13941592Srgrimes return (B); 13951592Srgrimes 13961592Srgrimes case 'C': 13971592Srgrimes case 'c': 13981592Srgrimes return (C); 13991592Srgrimes 14001592Srgrimes case 'E': 14011592Srgrimes case 'e': 14021592Srgrimes return (E); 14031592Srgrimes 14041592Srgrimes case 'F': 14051592Srgrimes case 'f': 14061592Srgrimes return (F); 14071592Srgrimes 14081592Srgrimes case 'I': 14091592Srgrimes case 'i': 14101592Srgrimes return (I); 14111592Srgrimes 14121592Srgrimes case 'L': 14131592Srgrimes case 'l': 14141592Srgrimes return (L); 14151592Srgrimes 14161592Srgrimes case 'N': 14171592Srgrimes case 'n': 14181592Srgrimes return (N); 14191592Srgrimes 14201592Srgrimes case 'P': 14211592Srgrimes case 'p': 14221592Srgrimes return (P); 14231592Srgrimes 14241592Srgrimes case 'R': 14251592Srgrimes case 'r': 14261592Srgrimes return (R); 14271592Srgrimes 14281592Srgrimes case 'S': 14291592Srgrimes case 's': 14301592Srgrimes return (S); 14311592Srgrimes 14321592Srgrimes case 'T': 14331592Srgrimes case 't': 14341592Srgrimes return (T); 14351592Srgrimes 14361592Srgrimes } 14371592Srgrimes break; 14381592Srgrimes 14391592Srgrimes default: 14401592Srgrimes fatal("Unknown state in scanner."); 14411592Srgrimes } 14421592Srgrimes yyerror((char *) 0); 14431592Srgrimes state = CMD; 14441592Srgrimes longjmp(errcatch,0); 14451592Srgrimes } 14461592Srgrimes} 14471592Srgrimes 14481592Srgrimesvoid 14491592Srgrimesupper(s) 14501592Srgrimes char *s; 14511592Srgrimes{ 14521592Srgrimes while (*s != '\0') { 14531592Srgrimes if (islower(*s)) 14541592Srgrimes *s = toupper(*s); 14551592Srgrimes s++; 14561592Srgrimes } 14571592Srgrimes} 14581592Srgrimes 14591592Srgrimesstatic char * 14601592Srgrimescopy(s) 14611592Srgrimes char *s; 14621592Srgrimes{ 14631592Srgrimes char *p; 14641592Srgrimes 14651592Srgrimes p = malloc((unsigned) strlen(s) + 1); 14661592Srgrimes if (p == NULL) 14671592Srgrimes fatal("Ran out of memory."); 14681592Srgrimes (void) strcpy(p, s); 14691592Srgrimes return (p); 14701592Srgrimes} 14711592Srgrimes 14721592Srgrimesstatic void 14731592Srgrimeshelp(ctab, s) 14741592Srgrimes struct tab *ctab; 14751592Srgrimes char *s; 14761592Srgrimes{ 14771592Srgrimes struct tab *c; 14781592Srgrimes int width, NCMDS; 14791592Srgrimes char *type; 14801592Srgrimes 14811592Srgrimes if (ctab == sitetab) 14821592Srgrimes type = "SITE "; 14831592Srgrimes else 14841592Srgrimes type = ""; 14851592Srgrimes width = 0, NCMDS = 0; 14861592Srgrimes for (c = ctab; c->name != NULL; c++) { 14871592Srgrimes int len = strlen(c->name); 14881592Srgrimes 14891592Srgrimes if (len > width) 14901592Srgrimes width = len; 14911592Srgrimes NCMDS++; 14921592Srgrimes } 14931592Srgrimes width = (width + 8) &~ 7; 14941592Srgrimes if (s == 0) { 14951592Srgrimes int i, j, w; 14961592Srgrimes int columns, lines; 14971592Srgrimes 14981592Srgrimes lreply(214, "The following %scommands are recognized %s.", 14991592Srgrimes type, "(* =>'s unimplemented)"); 15001592Srgrimes columns = 76 / width; 15011592Srgrimes if (columns == 0) 15021592Srgrimes columns = 1; 15031592Srgrimes lines = (NCMDS + columns - 1) / columns; 15041592Srgrimes for (i = 0; i < lines; i++) { 15051592Srgrimes printf(" "); 15061592Srgrimes for (j = 0; j < columns; j++) { 15071592Srgrimes c = ctab + j * lines + i; 15081592Srgrimes printf("%s%c", c->name, 15091592Srgrimes c->implemented ? ' ' : '*'); 15101592Srgrimes if (c + lines >= &ctab[NCMDS]) 15111592Srgrimes break; 15121592Srgrimes w = strlen(c->name) + 1; 15131592Srgrimes while (w < width) { 15141592Srgrimes putchar(' '); 15151592Srgrimes w++; 15161592Srgrimes } 15171592Srgrimes } 15181592Srgrimes printf("\r\n"); 15191592Srgrimes } 15201592Srgrimes (void) fflush(stdout); 15211592Srgrimes reply(214, "Direct comments to ftp-bugs@%s.", hostname); 15221592Srgrimes return; 15231592Srgrimes } 15241592Srgrimes upper(s); 15251592Srgrimes c = lookup(ctab, s); 15261592Srgrimes if (c == (struct tab *)0) { 15271592Srgrimes reply(502, "Unknown command %s.", s); 15281592Srgrimes return; 15291592Srgrimes } 15301592Srgrimes if (c->implemented) 15311592Srgrimes reply(214, "Syntax: %s%s %s", type, c->name, c->help); 15321592Srgrimes else 15331592Srgrimes reply(214, "%s%-*s\t%s; unimplemented.", type, width, 15341592Srgrimes c->name, c->help); 15351592Srgrimes} 15361592Srgrimes 15371592Srgrimesstatic void 15381592Srgrimessizecmd(filename) 15391592Srgrimes char *filename; 15401592Srgrimes{ 15411592Srgrimes switch (type) { 15421592Srgrimes case TYPE_L: 15431592Srgrimes case TYPE_I: { 15441592Srgrimes struct stat stbuf; 154563350Sdes if (stat(filename, &stbuf) < 0) 154663350Sdes perror_reply(550, filename); 154763350Sdes else if (!S_ISREG(stbuf.st_mode)) 15481592Srgrimes reply(550, "%s: not a plain file.", filename); 15491592Srgrimes else 15501592Srgrimes reply(213, "%qu", stbuf.st_size); 15511592Srgrimes break; } 15521592Srgrimes case TYPE_A: { 15531592Srgrimes FILE *fin; 15541592Srgrimes int c; 15551592Srgrimes off_t count; 15561592Srgrimes struct stat stbuf; 15571592Srgrimes fin = fopen(filename, "r"); 15581592Srgrimes if (fin == NULL) { 15591592Srgrimes perror_reply(550, filename); 15601592Srgrimes return; 15611592Srgrimes } 156263350Sdes if (fstat(fileno(fin), &stbuf) < 0) { 156363350Sdes perror_reply(550, filename); 156463350Sdes (void) fclose(fin); 156563350Sdes return; 156663350Sdes } else if (!S_ISREG(stbuf.st_mode)) { 15671592Srgrimes reply(550, "%s: not a plain file.", filename); 15681592Srgrimes (void) fclose(fin); 15691592Srgrimes return; 15701592Srgrimes } 15711592Srgrimes 15721592Srgrimes count = 0; 15731592Srgrimes while((c=getc(fin)) != EOF) { 15741592Srgrimes if (c == '\n') /* will get expanded to \r\n */ 15751592Srgrimes count++; 15761592Srgrimes count++; 15771592Srgrimes } 15781592Srgrimes (void) fclose(fin); 15791592Srgrimes 15801592Srgrimes reply(213, "%qd", count); 15811592Srgrimes break; } 15821592Srgrimes default: 15831592Srgrimes reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 15841592Srgrimes } 15851592Srgrimes} 158656668Sshin 158756668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 158856668Sshinstatic int 158956668Sshinport_check(pcmd) 159056668Sshin const char *pcmd; 159156668Sshin{ 159256668Sshin if (his_addr.su_family == AF_INET) { 159356668Sshin if (data_dest.su_family != AF_INET) { 159456668Sshin usedefault = 1; 159556668Sshin reply(500, "Invalid address rejected."); 159656668Sshin return 1; 159756668Sshin } 159856668Sshin if (paranoid && 159956668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 160056668Sshin memcmp(&data_dest.su_sin.sin_addr, 160156668Sshin &his_addr.su_sin.sin_addr, 160256668Sshin sizeof(data_dest.su_sin.sin_addr)))) { 160356668Sshin usedefault = 1; 160456668Sshin reply(500, "Illegal PORT range rejected."); 160556668Sshin } else { 160656668Sshin usedefault = 0; 160756668Sshin if (pdata >= 0) { 160856668Sshin (void) close(pdata); 160956668Sshin pdata = -1; 161056668Sshin } 161156668Sshin reply(200, "%s command successful.", pcmd); 161256668Sshin } 161356668Sshin return 1; 161456668Sshin } 161556668Sshin return 0; 161656668Sshin} 161756668Sshin 161870102Sphkstatic int 161970102Sphkcheck_login1() 162070102Sphk{ 162170102Sphk if (logged_in) 162270102Sphk return 1; 162370102Sphk else { 162470102Sphk reply(530, "Please login with USER and PASS."); 162570102Sphk return 0; 162670102Sphk } 162770102Sphk} 162870102Sphk 162956668Sshin#ifdef INET6 163056668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 163156668Sshinstatic int 163256668Sshinport_check_v6(pcmd) 163356668Sshin const char *pcmd; 163456668Sshin{ 163556668Sshin if (his_addr.su_family == AF_INET6) { 163656668Sshin if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 163756668Sshin /* Convert data_dest into v4 mapped sockaddr.*/ 163856668Sshin v4map_data_dest(); 163956668Sshin if (data_dest.su_family != AF_INET6) { 164056668Sshin usedefault = 1; 164156668Sshin reply(500, "Invalid address rejected."); 164256668Sshin return 1; 164356668Sshin } 164456668Sshin if (paranoid && 164556668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 164656668Sshin memcmp(&data_dest.su_sin6.sin6_addr, 164756668Sshin &his_addr.su_sin6.sin6_addr, 164856668Sshin sizeof(data_dest.su_sin6.sin6_addr)))) { 164956668Sshin usedefault = 1; 165056668Sshin reply(500, "Illegal PORT range rejected."); 165156668Sshin } else { 165256668Sshin usedefault = 0; 165356668Sshin if (pdata >= 0) { 165456668Sshin (void) close(pdata); 165556668Sshin pdata = -1; 165656668Sshin } 165756668Sshin reply(200, "%s command successful.", pcmd); 165856668Sshin } 165956668Sshin return 1; 166056668Sshin } 166156668Sshin return 0; 166256668Sshin} 166356668Sshin 166456668Sshinstatic void 166556668Sshinv4map_data_dest() 166656668Sshin{ 166756668Sshin struct in_addr savedaddr; 166856668Sshin int savedport; 166956668Sshin 167056668Sshin if (data_dest.su_family != AF_INET) { 167156668Sshin usedefault = 1; 167256668Sshin reply(500, "Invalid address rejected."); 167356668Sshin return; 167456668Sshin } 167556668Sshin 167656668Sshin savedaddr = data_dest.su_sin.sin_addr; 167756668Sshin savedport = data_dest.su_port; 167856668Sshin 167956668Sshin memset(&data_dest, 0, sizeof(data_dest)); 168056668Sshin data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 168156668Sshin data_dest.su_sin6.sin6_family = AF_INET6; 168256668Sshin data_dest.su_sin6.sin6_port = savedport; 168356668Sshin memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 168456668Sshin memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 168556668Sshin (caddr_t)&savedaddr, sizeof(savedaddr)); 168656668Sshin} 168756668Sshin#endif 1688