ftpcmd.y revision 70102
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 70102 2000-12-16 19:19:19Z 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> 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 } 3581592Srgrimes | TYPE SP type_code CRLF 3591592Srgrimes { 3601592Srgrimes switch (cmd_type) { 3611592Srgrimes 3621592Srgrimes case TYPE_A: 3631592Srgrimes if (cmd_form == FORM_N) { 3641592Srgrimes reply(200, "Type set to A."); 3651592Srgrimes type = cmd_type; 3661592Srgrimes form = cmd_form; 3671592Srgrimes } else 3681592Srgrimes reply(504, "Form must be N."); 3691592Srgrimes break; 3701592Srgrimes 3711592Srgrimes case TYPE_E: 3721592Srgrimes reply(504, "Type E not implemented."); 3731592Srgrimes break; 3741592Srgrimes 3751592Srgrimes case TYPE_I: 3761592Srgrimes reply(200, "Type set to I."); 3771592Srgrimes type = cmd_type; 3781592Srgrimes break; 3791592Srgrimes 3801592Srgrimes case TYPE_L: 3811592Srgrimes#if NBBY == 8 3821592Srgrimes if (cmd_bytesz == 8) { 3831592Srgrimes reply(200, 3841592Srgrimes "Type set to L (byte size 8)."); 3851592Srgrimes type = cmd_type; 3861592Srgrimes } else 3871592Srgrimes reply(504, "Byte size must be 8."); 3881592Srgrimes#else /* NBBY == 8 */ 3891592Srgrimes UNIMPLEMENTED for NBBY != 8 3901592Srgrimes#endif /* NBBY == 8 */ 3911592Srgrimes } 3921592Srgrimes } 3931592Srgrimes | STRU SP struct_code CRLF 3941592Srgrimes { 3951592Srgrimes switch ($3) { 3961592Srgrimes 3971592Srgrimes case STRU_F: 3981592Srgrimes reply(200, "STRU F ok."); 3991592Srgrimes break; 4001592Srgrimes 4011592Srgrimes default: 4021592Srgrimes reply(504, "Unimplemented STRU type."); 4031592Srgrimes } 4041592Srgrimes } 4051592Srgrimes | MODE SP mode_code CRLF 4061592Srgrimes { 4071592Srgrimes switch ($3) { 4081592Srgrimes 4091592Srgrimes case MODE_S: 4101592Srgrimes reply(200, "MODE S ok."); 4111592Srgrimes break; 4121592Srgrimes 4131592Srgrimes default: 4141592Srgrimes reply(502, "Unimplemented MODE type."); 4151592Srgrimes } 4161592Srgrimes } 4171592Srgrimes | ALLO SP NUMBER CRLF 4181592Srgrimes { 4191592Srgrimes reply(202, "ALLO command ignored."); 4201592Srgrimes } 4211592Srgrimes | ALLO SP NUMBER SP R SP NUMBER CRLF 4221592Srgrimes { 4231592Srgrimes reply(202, "ALLO command ignored."); 4241592Srgrimes } 4251592Srgrimes | RETR check_login SP pathname CRLF 4261592Srgrimes { 4271592Srgrimes if ($2 && $4 != NULL) 4281592Srgrimes retrieve((char *) 0, $4); 4291592Srgrimes if ($4 != NULL) 4301592Srgrimes free($4); 4311592Srgrimes } 43270102Sphk | STOR check_login_ro SP pathname CRLF 4331592Srgrimes { 4341592Srgrimes if ($2 && $4 != NULL) 4351592Srgrimes store($4, "w", 0); 4361592Srgrimes if ($4 != NULL) 4371592Srgrimes free($4); 4381592Srgrimes } 43970102Sphk | APPE check_login_ro SP pathname CRLF 4401592Srgrimes { 4411592Srgrimes if ($2 && $4 != NULL) 4421592Srgrimes store($4, "a", 0); 4431592Srgrimes if ($4 != NULL) 4441592Srgrimes free($4); 4451592Srgrimes } 4461592Srgrimes | NLST check_login CRLF 4471592Srgrimes { 4481592Srgrimes if ($2) 4491592Srgrimes send_file_list("."); 4501592Srgrimes } 4511592Srgrimes | NLST check_login SP STRING CRLF 4521592Srgrimes { 4531592Srgrimes if ($2 && $4 != NULL) 4541592Srgrimes send_file_list($4); 4551592Srgrimes if ($4 != NULL) 4561592Srgrimes free($4); 4571592Srgrimes } 4581592Srgrimes | LIST check_login CRLF 4591592Srgrimes { 4601592Srgrimes if ($2) 4611592Srgrimes retrieve("/bin/ls -lgA", ""); 4621592Srgrimes } 4631592Srgrimes | LIST check_login SP pathname CRLF 4641592Srgrimes { 4651592Srgrimes if ($2 && $4 != NULL) 4661592Srgrimes retrieve("/bin/ls -lgA %s", $4); 4671592Srgrimes if ($4 != NULL) 4681592Srgrimes free($4); 4691592Srgrimes } 4701592Srgrimes | STAT check_login SP pathname CRLF 4711592Srgrimes { 4721592Srgrimes if ($2 && $4 != NULL) 4731592Srgrimes statfilecmd($4); 4741592Srgrimes if ($4 != NULL) 4751592Srgrimes free($4); 4761592Srgrimes } 4771592Srgrimes | STAT CRLF 4781592Srgrimes { 4791592Srgrimes statcmd(); 4801592Srgrimes } 48170102Sphk | DELE check_login_ro SP pathname CRLF 4821592Srgrimes { 4831592Srgrimes if ($2 && $4 != NULL) 4841592Srgrimes delete($4); 4851592Srgrimes if ($4 != NULL) 4861592Srgrimes free($4); 4871592Srgrimes } 48870102Sphk | RNTO check_login_ro SP pathname CRLF 4891592Srgrimes { 49017433Spst if ($2) { 49117433Spst if (fromname) { 49217433Spst renamecmd(fromname, $4); 49317433Spst free(fromname); 49417433Spst fromname = (char *) 0; 49517433Spst } else { 49617433Spst reply(503, "Bad sequence of commands."); 49717433Spst } 4981592Srgrimes } 49917433Spst free($4); 5001592Srgrimes } 5011592Srgrimes | ABOR CRLF 5021592Srgrimes { 5031592Srgrimes reply(225, "ABOR command successful."); 5041592Srgrimes } 5051592Srgrimes | CWD check_login CRLF 5061592Srgrimes { 50769234Sdanny if ($2) { 50869234Sdanny if (guest) 50969234Sdanny cwd("/"); 51069234Sdanny else 51169234Sdanny cwd(pw->pw_dir); 51269234Sdanny } 5131592Srgrimes } 5141592Srgrimes | CWD check_login SP pathname CRLF 5151592Srgrimes { 5161592Srgrimes if ($2 && $4 != NULL) 5171592Srgrimes cwd($4); 5181592Srgrimes if ($4 != NULL) 5191592Srgrimes free($4); 5201592Srgrimes } 5211592Srgrimes | HELP CRLF 5221592Srgrimes { 5231592Srgrimes help(cmdtab, (char *) 0); 5241592Srgrimes } 5251592Srgrimes | HELP SP STRING CRLF 5261592Srgrimes { 5271592Srgrimes char *cp = $3; 5281592Srgrimes 5291592Srgrimes if (strncasecmp(cp, "SITE", 4) == 0) { 5301592Srgrimes cp = $3 + 4; 5311592Srgrimes if (*cp == ' ') 5321592Srgrimes cp++; 5331592Srgrimes if (*cp) 5341592Srgrimes help(sitetab, cp); 5351592Srgrimes else 5361592Srgrimes help(sitetab, (char *) 0); 5371592Srgrimes } else 5381592Srgrimes help(cmdtab, $3); 5391592Srgrimes } 5401592Srgrimes | NOOP CRLF 5411592Srgrimes { 5421592Srgrimes reply(200, "NOOP command successful."); 5431592Srgrimes } 54470102Sphk | MKD check_login_ro SP pathname CRLF 5451592Srgrimes { 5461592Srgrimes if ($2 && $4 != NULL) 5471592Srgrimes makedir($4); 5481592Srgrimes if ($4 != NULL) 5491592Srgrimes free($4); 5501592Srgrimes } 55170102Sphk | RMD check_login_ro SP pathname CRLF 5521592Srgrimes { 5531592Srgrimes if ($2 && $4 != NULL) 5541592Srgrimes removedir($4); 5551592Srgrimes if ($4 != NULL) 5561592Srgrimes free($4); 5571592Srgrimes } 5581592Srgrimes | PWD check_login CRLF 5591592Srgrimes { 5601592Srgrimes if ($2) 5611592Srgrimes pwd(); 5621592Srgrimes } 5631592Srgrimes | CDUP check_login CRLF 5641592Srgrimes { 5651592Srgrimes if ($2) 5661592Srgrimes cwd(".."); 5671592Srgrimes } 5681592Srgrimes | SITE SP HELP CRLF 5691592Srgrimes { 5701592Srgrimes help(sitetab, (char *) 0); 5711592Srgrimes } 5721592Srgrimes | SITE SP HELP SP STRING CRLF 5731592Srgrimes { 5741592Srgrimes help(sitetab, $5); 5751592Srgrimes } 5761592Srgrimes | SITE SP UMASK check_login CRLF 5771592Srgrimes { 5781592Srgrimes int oldmask; 5791592Srgrimes 5801592Srgrimes if ($4) { 5811592Srgrimes oldmask = umask(0); 5821592Srgrimes (void) umask(oldmask); 5831592Srgrimes reply(200, "Current UMASK is %03o", oldmask); 5841592Srgrimes } 5851592Srgrimes } 5861592Srgrimes | SITE SP UMASK check_login SP octal_number CRLF 5871592Srgrimes { 5881592Srgrimes int oldmask; 5891592Srgrimes 5901592Srgrimes if ($4) { 5911592Srgrimes if (($6 == -1) || ($6 > 0777)) { 5921592Srgrimes reply(501, "Bad UMASK value"); 5931592Srgrimes } else { 5941592Srgrimes oldmask = umask($6); 5951592Srgrimes reply(200, 5961592Srgrimes "UMASK set to %03o (was %03o)", 5971592Srgrimes $6, oldmask); 5981592Srgrimes } 5991592Srgrimes } 6001592Srgrimes } 60170102Sphk | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF 6021592Srgrimes { 6031592Srgrimes if ($4 && ($8 != NULL)) { 6041592Srgrimes if ($6 > 0777) 6051592Srgrimes reply(501, 6061592Srgrimes "CHMOD: Mode value must be between 0 and 0777"); 6071592Srgrimes else if (chmod($8, $6) < 0) 6081592Srgrimes perror_reply(550, $8); 6091592Srgrimes else 6101592Srgrimes reply(200, "CHMOD command successful."); 6111592Srgrimes } 6121592Srgrimes if ($8 != NULL) 6131592Srgrimes free($8); 6141592Srgrimes } 6151592Srgrimes | SITE SP IDLE CRLF 6161592Srgrimes { 6171592Srgrimes reply(200, 6181592Srgrimes "Current IDLE time limit is %d seconds; max %d", 6191592Srgrimes timeout, maxtimeout); 6201592Srgrimes } 6211592Srgrimes | SITE SP IDLE SP NUMBER CRLF 6221592Srgrimes { 6231592Srgrimes if ($5 < 30 || $5 > maxtimeout) { 6241592Srgrimes reply(501, 6251592Srgrimes "Maximum IDLE time must be between 30 and %d seconds", 6261592Srgrimes maxtimeout); 6271592Srgrimes } else { 6281592Srgrimes timeout = $5; 6291592Srgrimes (void) alarm((unsigned) timeout); 6301592Srgrimes reply(200, 6311592Srgrimes "Maximum IDLE time set to %d seconds", 6321592Srgrimes timeout); 6331592Srgrimes } 6341592Srgrimes } 63570102Sphk | STOU check_login_ro SP pathname CRLF 6361592Srgrimes { 6371592Srgrimes if ($2 && $4 != NULL) 6381592Srgrimes store($4, "w", 1); 6391592Srgrimes if ($4 != NULL) 6401592Srgrimes free($4); 6411592Srgrimes } 6421592Srgrimes | SYST CRLF 6431592Srgrimes { 6441592Srgrimes#ifdef unix 6451592Srgrimes#ifdef BSD 6461592Srgrimes reply(215, "UNIX Type: L%d Version: BSD-%d", 6471592Srgrimes NBBY, BSD); 6481592Srgrimes#else /* BSD */ 6491592Srgrimes reply(215, "UNIX Type: L%d", NBBY); 6501592Srgrimes#endif /* BSD */ 6511592Srgrimes#else /* unix */ 6521592Srgrimes reply(215, "UNKNOWN Type: L%d", NBBY); 6531592Srgrimes#endif /* unix */ 6541592Srgrimes } 6551592Srgrimes 6561592Srgrimes /* 6571592Srgrimes * SIZE is not in RFC959, but Postel has blessed it and 6581592Srgrimes * it will be in the updated RFC. 6591592Srgrimes * 6601592Srgrimes * Return size of file in a format suitable for 6611592Srgrimes * using with RESTART (we just count bytes). 6621592Srgrimes */ 6631592Srgrimes | SIZE check_login SP pathname CRLF 6641592Srgrimes { 6651592Srgrimes if ($2 && $4 != NULL) 6661592Srgrimes sizecmd($4); 6671592Srgrimes if ($4 != NULL) 6681592Srgrimes free($4); 6691592Srgrimes } 6701592Srgrimes 6711592Srgrimes /* 6721592Srgrimes * MDTM is not in RFC959, but Postel has blessed it and 6731592Srgrimes * it will be in the updated RFC. 6741592Srgrimes * 6751592Srgrimes * Return modification time of file as an ISO 3307 6761592Srgrimes * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 6771592Srgrimes * where xxx is the fractional second (of any precision, 6781592Srgrimes * not necessarily 3 digits) 6791592Srgrimes */ 6801592Srgrimes | MDTM check_login SP pathname CRLF 6811592Srgrimes { 6821592Srgrimes if ($2 && $4 != NULL) { 6831592Srgrimes struct stat stbuf; 6841592Srgrimes if (stat($4, &stbuf) < 0) 6851592Srgrimes reply(550, "%s: %s", 6861592Srgrimes $4, strerror(errno)); 6871592Srgrimes else if (!S_ISREG(stbuf.st_mode)) { 6881592Srgrimes reply(550, "%s: not a plain file.", $4); 6891592Srgrimes } else { 6901592Srgrimes struct tm *t; 6911592Srgrimes t = gmtime(&stbuf.st_mtime); 6921592Srgrimes reply(213, 69317435Spst "%04d%02d%02d%02d%02d%02d", 69417435Spst 1900 + t->tm_year, 69517435Spst t->tm_mon+1, t->tm_mday, 6961592Srgrimes t->tm_hour, t->tm_min, t->tm_sec); 6971592Srgrimes } 6981592Srgrimes } 6991592Srgrimes if ($4 != NULL) 7001592Srgrimes free($4); 7011592Srgrimes } 7021592Srgrimes | QUIT CRLF 7031592Srgrimes { 7041592Srgrimes reply(221, "Goodbye."); 7051592Srgrimes dologout(0); 7061592Srgrimes } 7071592Srgrimes | error CRLF 7081592Srgrimes { 7091592Srgrimes yyerrok; 7101592Srgrimes } 7111592Srgrimes ; 7121592Srgrimesrcmd 71370102Sphk : RNFR check_login_ro SP pathname CRLF 7141592Srgrimes { 7151592Srgrimes char *renamefrom(); 7161592Srgrimes 7171592Srgrimes restart_point = (off_t) 0; 7181592Srgrimes if ($2 && $4) { 7191592Srgrimes fromname = renamefrom($4); 7201592Srgrimes if (fromname == (char *) 0 && $4) { 7211592Srgrimes free($4); 7221592Srgrimes } 7231592Srgrimes } 7241592Srgrimes } 7251592Srgrimes | REST SP byte_size CRLF 7261592Srgrimes { 7271592Srgrimes fromname = (char *) 0; 7281592Srgrimes restart_point = $3; /* XXX $3 is only "int" */ 7291592Srgrimes reply(350, "Restarting at %qd. %s", restart_point, 7301592Srgrimes "Send STORE or RETRIEVE to initiate transfer."); 7311592Srgrimes } 7321592Srgrimes ; 7331592Srgrimes 7341592Srgrimesusername 7351592Srgrimes : STRING 7361592Srgrimes ; 7371592Srgrimes 7381592Srgrimespassword 7391592Srgrimes : /* empty */ 7401592Srgrimes { 7411592Srgrimes $$ = (char *)calloc(1, sizeof(char)); 7421592Srgrimes } 7431592Srgrimes | STRING 7441592Srgrimes ; 7451592Srgrimes 7461592Srgrimesbyte_size 7471592Srgrimes : NUMBER 7481592Srgrimes ; 7491592Srgrimes 7501592Srgrimeshost_port 7511592Srgrimes : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 7521592Srgrimes NUMBER COMMA NUMBER 7531592Srgrimes { 7541592Srgrimes char *a, *p; 7551592Srgrimes 75656668Sshin data_dest.su_len = sizeof(struct sockaddr_in); 75756668Sshin data_dest.su_family = AF_INET; 75856668Sshin p = (char *)&data_dest.su_sin.sin_port; 75917435Spst p[0] = $9; p[1] = $11; 76056668Sshin a = (char *)&data_dest.su_sin.sin_addr; 7611592Srgrimes a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 7621592Srgrimes } 7631592Srgrimes ; 7641592Srgrimes 76556668Sshinhost_long_port 76656668Sshin : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 76756668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 76856668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 76956668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 77056668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 77156668Sshin NUMBER 77256668Sshin { 77356668Sshin char *a, *p; 77456668Sshin 77556668Sshin memset(&data_dest, 0, sizeof(data_dest)); 77656668Sshin data_dest.su_len = sizeof(struct sockaddr_in6); 77756668Sshin data_dest.su_family = AF_INET6; 77856668Sshin p = (char *)&data_dest.su_port; 77956668Sshin p[0] = $39; p[1] = $41; 78056668Sshin a = (char *)&data_dest.su_sin6.sin6_addr; 78156668Sshin a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 78256668Sshin a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19; 78356668Sshin a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27; 78456668Sshin a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35; 78556668Sshin if (his_addr.su_family == AF_INET6) { 78656668Sshin /* XXX more sanity checks! */ 78756668Sshin data_dest.su_sin6.sin6_scope_id = 78856668Sshin his_addr.su_sin6.sin6_scope_id; 78956668Sshin } 79056668Sshin if ($1 != 6 || $3 != 16 || $37 != 2) 79156668Sshin memset(&data_dest, 0, sizeof(data_dest)); 79256668Sshin } 79356668Sshin | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 79456668Sshin NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 79556668Sshin NUMBER 79656668Sshin { 79756668Sshin char *a, *p; 79856668Sshin 79956668Sshin memset(&data_dest, 0, sizeof(data_dest)); 80056668Sshin data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 80156668Sshin data_dest.su_family = AF_INET; 80256668Sshin p = (char *)&data_dest.su_port; 80356668Sshin p[0] = $15; p[1] = $17; 80456668Sshin a = (char *)&data_dest.su_sin.sin_addr; 80556668Sshin a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11; 80656668Sshin if ($1 != 4 || $3 != 4 || $13 != 2) 80756668Sshin memset(&data_dest, 0, sizeof(data_dest)); 80856668Sshin } 80956668Sshin ; 81056668Sshin 8111592Srgrimesform_code 8121592Srgrimes : N 8131592Srgrimes { 8141592Srgrimes $$ = FORM_N; 8151592Srgrimes } 8161592Srgrimes | T 8171592Srgrimes { 8181592Srgrimes $$ = FORM_T; 8191592Srgrimes } 8201592Srgrimes | C 8211592Srgrimes { 8221592Srgrimes $$ = FORM_C; 8231592Srgrimes } 8241592Srgrimes ; 8251592Srgrimes 8261592Srgrimestype_code 8271592Srgrimes : A 8281592Srgrimes { 8291592Srgrimes cmd_type = TYPE_A; 8301592Srgrimes cmd_form = FORM_N; 8311592Srgrimes } 8321592Srgrimes | A SP form_code 8331592Srgrimes { 8341592Srgrimes cmd_type = TYPE_A; 8351592Srgrimes cmd_form = $3; 8361592Srgrimes } 8371592Srgrimes | E 8381592Srgrimes { 8391592Srgrimes cmd_type = TYPE_E; 8401592Srgrimes cmd_form = FORM_N; 8411592Srgrimes } 8421592Srgrimes | E SP form_code 8431592Srgrimes { 8441592Srgrimes cmd_type = TYPE_E; 8451592Srgrimes cmd_form = $3; 8461592Srgrimes } 8471592Srgrimes | I 8481592Srgrimes { 8491592Srgrimes cmd_type = TYPE_I; 8501592Srgrimes } 8511592Srgrimes | L 8521592Srgrimes { 8531592Srgrimes cmd_type = TYPE_L; 8541592Srgrimes cmd_bytesz = NBBY; 8551592Srgrimes } 8561592Srgrimes | L SP byte_size 8571592Srgrimes { 8581592Srgrimes cmd_type = TYPE_L; 8591592Srgrimes cmd_bytesz = $3; 8601592Srgrimes } 8611592Srgrimes /* this is for a bug in the BBN ftp */ 8621592Srgrimes | L byte_size 8631592Srgrimes { 8641592Srgrimes cmd_type = TYPE_L; 8651592Srgrimes cmd_bytesz = $2; 8661592Srgrimes } 8671592Srgrimes ; 8681592Srgrimes 8691592Srgrimesstruct_code 8701592Srgrimes : F 8711592Srgrimes { 8721592Srgrimes $$ = STRU_F; 8731592Srgrimes } 8741592Srgrimes | R 8751592Srgrimes { 8761592Srgrimes $$ = STRU_R; 8771592Srgrimes } 8781592Srgrimes | P 8791592Srgrimes { 8801592Srgrimes $$ = STRU_P; 8811592Srgrimes } 8821592Srgrimes ; 8831592Srgrimes 8841592Srgrimesmode_code 8851592Srgrimes : S 8861592Srgrimes { 8871592Srgrimes $$ = MODE_S; 8881592Srgrimes } 8891592Srgrimes | B 8901592Srgrimes { 8911592Srgrimes $$ = MODE_B; 8921592Srgrimes } 8931592Srgrimes | C 8941592Srgrimes { 8951592Srgrimes $$ = MODE_C; 8961592Srgrimes } 8971592Srgrimes ; 8981592Srgrimes 8991592Srgrimespathname 9001592Srgrimes : pathstring 9011592Srgrimes { 9021592Srgrimes /* 9031592Srgrimes * Problem: this production is used for all pathname 9041592Srgrimes * processing, but only gives a 550 error reply. 9051592Srgrimes * This is a valid reply in some cases but not in others. 9061592Srgrimes */ 9071592Srgrimes if (logged_in && $1 && *$1 == '~') { 9081592Srgrimes glob_t gl; 9091592Srgrimes int flags = 9101592Srgrimes GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 9111592Srgrimes 9121592Srgrimes memset(&gl, 0, sizeof(gl)); 9131592Srgrimes if (glob($1, flags, NULL, &gl) || 9141592Srgrimes gl.gl_pathc == 0) { 9151592Srgrimes reply(550, "not found"); 9161592Srgrimes $$ = NULL; 9171592Srgrimes } else { 9181592Srgrimes $$ = strdup(gl.gl_pathv[0]); 9191592Srgrimes } 9201592Srgrimes globfree(&gl); 9211592Srgrimes free($1); 9221592Srgrimes } else 9231592Srgrimes $$ = $1; 9241592Srgrimes } 9251592Srgrimes ; 9261592Srgrimes 9271592Srgrimespathstring 9281592Srgrimes : STRING 9291592Srgrimes ; 9301592Srgrimes 9311592Srgrimesoctal_number 9321592Srgrimes : NUMBER 9331592Srgrimes { 9341592Srgrimes int ret, dec, multby, digit; 9351592Srgrimes 9361592Srgrimes /* 9371592Srgrimes * Convert a number that was read as decimal number 9381592Srgrimes * to what it would be if it had been read as octal. 9391592Srgrimes */ 9401592Srgrimes dec = $1; 9411592Srgrimes multby = 1; 9421592Srgrimes ret = 0; 9431592Srgrimes while (dec) { 9441592Srgrimes digit = dec%10; 9451592Srgrimes if (digit > 7) { 9461592Srgrimes ret = -1; 9471592Srgrimes break; 9481592Srgrimes } 9491592Srgrimes ret += digit * multby; 9501592Srgrimes multby *= 8; 9511592Srgrimes dec /= 10; 9521592Srgrimes } 9531592Srgrimes $$ = ret; 9541592Srgrimes } 9551592Srgrimes ; 9561592Srgrimes 9571592Srgrimes 9581592Srgrimescheck_login 9591592Srgrimes : /* empty */ 9601592Srgrimes { 96170102Sphk $$ = check_login1(); 9621592Srgrimes } 9631592Srgrimes ; 9641592Srgrimes 96570102Sphkcheck_login_epsv 96670102Sphk : /* empty */ 96770102Sphk { 96870102Sphk if (noepsv) { 96970102Sphk reply(500, "EPSV command disabled"); 97070102Sphk $$ = 0; 97170102Sphk } 97270102Sphk else 97370102Sphk $$ = check_login1(); 97470102Sphk } 97570102Sphk ; 97670102Sphk 97770102Sphkcheck_login_ro 97870102Sphk : /* empty */ 97970102Sphk { 98070102Sphk if (readonly) { 98170102Sphk reply(202, "Command ignored. Server is in readonly mode."); 98270102Sphk $$ = 0; 98370102Sphk } 98470102Sphk else 98570102Sphk $$ = check_login1(); 98670102Sphk } 98770102Sphk ; 98870102Sphk 9891592Srgrimes%% 9901592Srgrimes 9911592Srgrimesextern jmp_buf errcatch; 9921592Srgrimes 9931592Srgrimes#define CMD 0 /* beginning of command */ 9941592Srgrimes#define ARGS 1 /* expect miscellaneous arguments */ 9951592Srgrimes#define STR1 2 /* expect SP followed by STRING */ 9961592Srgrimes#define STR2 3 /* expect STRING */ 9971592Srgrimes#define OSTR 4 /* optional SP then STRING */ 9981592Srgrimes#define ZSTR1 5 /* SP then optional STRING */ 9991592Srgrimes#define ZSTR2 6 /* optional STRING after SP */ 10001592Srgrimes#define SITECMD 7 /* SITE command */ 10011592Srgrimes#define NSTR 8 /* Number followed by a string */ 10021592Srgrimes 10031592Srgrimesstruct tab { 10041592Srgrimes char *name; 10051592Srgrimes short token; 10061592Srgrimes short state; 10071592Srgrimes short implemented; /* 1 if command is implemented */ 10081592Srgrimes char *help; 10091592Srgrimes}; 10101592Srgrimes 10111592Srgrimesstruct tab cmdtab[] = { /* In order defined in RFC 765 */ 10121592Srgrimes { "USER", USER, STR1, 1, "<sp> username" }, 10131592Srgrimes { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 10141592Srgrimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 10151592Srgrimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 10161592Srgrimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 10171592Srgrimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 10181592Srgrimes { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 101956668Sshin { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 102056668Sshin { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 10211592Srgrimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 102256668Sshin { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 102356668Sshin { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 10241592Srgrimes { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 10251592Srgrimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 10261592Srgrimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 10271592Srgrimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 10281592Srgrimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 10291592Srgrimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 10301592Srgrimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 10311592Srgrimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 10321592Srgrimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 10331592Srgrimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 10341592Srgrimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 10351592Srgrimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 10361592Srgrimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 10371592Srgrimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 10381592Srgrimes { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 10391592Srgrimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 10401592Srgrimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 10411592Srgrimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 10421592Srgrimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 10431592Srgrimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 10441592Srgrimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 10451592Srgrimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 10461592Srgrimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 10471592Srgrimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 10481592Srgrimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 10491592Srgrimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 10501592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 10511592Srgrimes { "NOOP", NOOP, ARGS, 1, "" }, 10521592Srgrimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 10531592Srgrimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 10541592Srgrimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 10551592Srgrimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 10561592Srgrimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 10571592Srgrimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 10581592Srgrimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 10591592Srgrimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 10601592Srgrimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 10611592Srgrimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 10621592Srgrimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 10631592Srgrimes { NULL, 0, 0, 0, 0 } 10641592Srgrimes}; 10651592Srgrimes 10661592Srgrimesstruct tab sitetab[] = { 10671592Srgrimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 10681592Srgrimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 10691592Srgrimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 10701592Srgrimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 10711592Srgrimes { NULL, 0, 0, 0, 0 } 10721592Srgrimes}; 10731592Srgrimes 10741592Srgrimesstatic char *copy __P((char *)); 10751592Srgrimesstatic void help __P((struct tab *, char *)); 10761592Srgrimesstatic struct tab * 10771592Srgrimes lookup __P((struct tab *, char *)); 107856668Sshinstatic int port_check __P((const char *)); 107956668Sshinstatic int port_check_v6 __P((const char *)); 10801592Srgrimesstatic void sizecmd __P((char *)); 10811592Srgrimesstatic void toolong __P((int)); 108256668Sshinstatic void v4map_data_dest __P((void)); 10831592Srgrimesstatic int yylex __P((void)); 10841592Srgrimes 10851592Srgrimesstatic struct tab * 10861592Srgrimeslookup(p, cmd) 10871592Srgrimes struct tab *p; 10881592Srgrimes char *cmd; 10891592Srgrimes{ 10901592Srgrimes 10911592Srgrimes for (; p->name != NULL; p++) 10921592Srgrimes if (strcmp(cmd, p->name) == 0) 10931592Srgrimes return (p); 10941592Srgrimes return (0); 10951592Srgrimes} 10961592Srgrimes 10971592Srgrimes#include <arpa/telnet.h> 10981592Srgrimes 10991592Srgrimes/* 11001592Srgrimes * getline - a hacked up version of fgets to ignore TELNET escape codes. 11011592Srgrimes */ 11021592Srgrimeschar * 11031592Srgrimesgetline(s, n, iop) 11041592Srgrimes char *s; 11051592Srgrimes int n; 11061592Srgrimes FILE *iop; 11071592Srgrimes{ 11081592Srgrimes int c; 11091592Srgrimes register char *cs; 11101592Srgrimes 11111592Srgrimes cs = s; 11121592Srgrimes/* tmpline may contain saved command from urgent mode interruption */ 11131592Srgrimes for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 11141592Srgrimes *cs++ = tmpline[c]; 11151592Srgrimes if (tmpline[c] == '\n') { 11161592Srgrimes *cs++ = '\0'; 11171592Srgrimes if (debug) 11181592Srgrimes syslog(LOG_DEBUG, "command: %s", s); 11191592Srgrimes tmpline[0] = '\0'; 11201592Srgrimes return(s); 11211592Srgrimes } 11221592Srgrimes if (c == 0) 11231592Srgrimes tmpline[0] = '\0'; 11241592Srgrimes } 11251592Srgrimes while ((c = getc(iop)) != EOF) { 11261592Srgrimes c &= 0377; 11271592Srgrimes if (c == IAC) { 11281592Srgrimes if ((c = getc(iop)) != EOF) { 11291592Srgrimes c &= 0377; 11301592Srgrimes switch (c) { 11311592Srgrimes case WILL: 11321592Srgrimes case WONT: 11331592Srgrimes c = getc(iop); 11341592Srgrimes printf("%c%c%c", IAC, DONT, 0377&c); 11351592Srgrimes (void) fflush(stdout); 11361592Srgrimes continue; 11371592Srgrimes case DO: 11381592Srgrimes case DONT: 11391592Srgrimes c = getc(iop); 11401592Srgrimes printf("%c%c%c", IAC, WONT, 0377&c); 11411592Srgrimes (void) fflush(stdout); 11421592Srgrimes continue; 11431592Srgrimes case IAC: 11441592Srgrimes break; 11451592Srgrimes default: 11461592Srgrimes continue; /* ignore command */ 11471592Srgrimes } 11481592Srgrimes } 11491592Srgrimes } 11501592Srgrimes *cs++ = c; 11511592Srgrimes if (--n <= 0 || c == '\n') 11521592Srgrimes break; 11531592Srgrimes } 11541592Srgrimes if (c == EOF && cs == s) 11551592Srgrimes return (NULL); 11561592Srgrimes *cs++ = '\0'; 11571592Srgrimes if (debug) { 11581592Srgrimes if (!guest && strncasecmp("pass ", s, 5) == 0) { 11591592Srgrimes /* Don't syslog passwords */ 11601592Srgrimes syslog(LOG_DEBUG, "command: %.5s ???", s); 11611592Srgrimes } else { 11621592Srgrimes register char *cp; 11631592Srgrimes register int len; 11641592Srgrimes 11651592Srgrimes /* Don't syslog trailing CR-LF */ 11661592Srgrimes len = strlen(s); 11671592Srgrimes cp = s + len - 1; 11681592Srgrimes while (cp >= s && (*cp == '\n' || *cp == '\r')) { 11691592Srgrimes --cp; 11701592Srgrimes --len; 11711592Srgrimes } 11721592Srgrimes syslog(LOG_DEBUG, "command: %.*s", len, s); 11731592Srgrimes } 11741592Srgrimes } 11751592Srgrimes return (s); 11761592Srgrimes} 11771592Srgrimes 11781592Srgrimesstatic void 11791592Srgrimestoolong(signo) 11801592Srgrimes int signo; 11811592Srgrimes{ 11821592Srgrimes 11831592Srgrimes reply(421, 11841592Srgrimes "Timeout (%d seconds): closing control connection.", timeout); 11851592Srgrimes if (logging) 11861592Srgrimes syslog(LOG_INFO, "User %s timed out after %d seconds", 11871592Srgrimes (pw ? pw -> pw_name : "unknown"), timeout); 11881592Srgrimes dologout(1); 11891592Srgrimes} 11901592Srgrimes 11911592Srgrimesstatic int 11921592Srgrimesyylex() 11931592Srgrimes{ 11941592Srgrimes static int cpos, state; 11951592Srgrimes char *cp, *cp2; 11961592Srgrimes struct tab *p; 11971592Srgrimes int n; 11981592Srgrimes char c; 11991592Srgrimes 12001592Srgrimes for (;;) { 12011592Srgrimes switch (state) { 12021592Srgrimes 12031592Srgrimes case CMD: 12041592Srgrimes (void) signal(SIGALRM, toolong); 12051592Srgrimes (void) alarm((unsigned) timeout); 12061592Srgrimes if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 12071592Srgrimes reply(221, "You could at least say goodbye."); 12081592Srgrimes dologout(0); 12091592Srgrimes } 12101592Srgrimes (void) alarm(0); 12111592Srgrimes#ifdef SETPROCTITLE 121229574Sphk if (strncasecmp(cbuf, "PASS", 4) != 0) 12131592Srgrimes setproctitle("%s: %s", proctitle, cbuf); 12141592Srgrimes#endif /* SETPROCTITLE */ 12151592Srgrimes if ((cp = strchr(cbuf, '\r'))) { 12161592Srgrimes *cp++ = '\n'; 12171592Srgrimes *cp = '\0'; 12181592Srgrimes } 12191592Srgrimes if ((cp = strpbrk(cbuf, " \n"))) 12201592Srgrimes cpos = cp - cbuf; 12211592Srgrimes if (cpos == 0) 12221592Srgrimes cpos = 4; 12231592Srgrimes c = cbuf[cpos]; 12241592Srgrimes cbuf[cpos] = '\0'; 12251592Srgrimes upper(cbuf); 12261592Srgrimes p = lookup(cmdtab, cbuf); 12271592Srgrimes cbuf[cpos] = c; 12283776Spst if (p != 0) { 12291592Srgrimes if (p->implemented == 0) { 12301592Srgrimes nack(p->name); 12311592Srgrimes longjmp(errcatch,0); 12321592Srgrimes /* NOTREACHED */ 12331592Srgrimes } 12341592Srgrimes state = p->state; 12351592Srgrimes yylval.s = p->name; 12361592Srgrimes return (p->token); 12371592Srgrimes } 12381592Srgrimes break; 12391592Srgrimes 12401592Srgrimes case SITECMD: 12411592Srgrimes if (cbuf[cpos] == ' ') { 12421592Srgrimes cpos++; 12431592Srgrimes return (SP); 12441592Srgrimes } 12451592Srgrimes cp = &cbuf[cpos]; 12461592Srgrimes if ((cp2 = strpbrk(cp, " \n"))) 12471592Srgrimes cpos = cp2 - cbuf; 12481592Srgrimes c = cbuf[cpos]; 12491592Srgrimes cbuf[cpos] = '\0'; 12501592Srgrimes upper(cp); 12511592Srgrimes p = lookup(sitetab, cp); 12521592Srgrimes cbuf[cpos] = c; 12533777Spst if (guest == 0 && p != 0) { 12541592Srgrimes if (p->implemented == 0) { 12551592Srgrimes state = CMD; 12561592Srgrimes nack(p->name); 12571592Srgrimes longjmp(errcatch,0); 12581592Srgrimes /* NOTREACHED */ 12591592Srgrimes } 12601592Srgrimes state = p->state; 12611592Srgrimes yylval.s = p->name; 12621592Srgrimes return (p->token); 12631592Srgrimes } 12641592Srgrimes state = CMD; 12651592Srgrimes break; 12661592Srgrimes 12671592Srgrimes case OSTR: 12681592Srgrimes if (cbuf[cpos] == '\n') { 12691592Srgrimes state = CMD; 12701592Srgrimes return (CRLF); 12711592Srgrimes } 12721592Srgrimes /* FALLTHROUGH */ 12731592Srgrimes 12741592Srgrimes case STR1: 12751592Srgrimes case ZSTR1: 12761592Srgrimes dostr1: 12771592Srgrimes if (cbuf[cpos] == ' ') { 12781592Srgrimes cpos++; 127951979Salfred state = state == OSTR ? STR2 : state+1; 12801592Srgrimes return (SP); 12811592Srgrimes } 12821592Srgrimes break; 12831592Srgrimes 12841592Srgrimes case ZSTR2: 12851592Srgrimes if (cbuf[cpos] == '\n') { 12861592Srgrimes state = CMD; 12871592Srgrimes return (CRLF); 12881592Srgrimes } 12891592Srgrimes /* FALLTHROUGH */ 12901592Srgrimes 12911592Srgrimes case STR2: 12921592Srgrimes cp = &cbuf[cpos]; 12931592Srgrimes n = strlen(cp); 12941592Srgrimes cpos += n - 1; 12951592Srgrimes /* 12961592Srgrimes * Make sure the string is nonempty and \n terminated. 12971592Srgrimes */ 12981592Srgrimes if (n > 1 && cbuf[cpos] == '\n') { 12991592Srgrimes cbuf[cpos] = '\0'; 13001592Srgrimes yylval.s = copy(cp); 13011592Srgrimes cbuf[cpos] = '\n'; 13021592Srgrimes state = ARGS; 13031592Srgrimes return (STRING); 13041592Srgrimes } 13051592Srgrimes break; 13061592Srgrimes 13071592Srgrimes case NSTR: 13081592Srgrimes if (cbuf[cpos] == ' ') { 13091592Srgrimes cpos++; 13101592Srgrimes return (SP); 13111592Srgrimes } 13121592Srgrimes if (isdigit(cbuf[cpos])) { 13131592Srgrimes cp = &cbuf[cpos]; 13141592Srgrimes while (isdigit(cbuf[++cpos])) 13151592Srgrimes ; 13161592Srgrimes c = cbuf[cpos]; 13171592Srgrimes cbuf[cpos] = '\0'; 13181592Srgrimes yylval.i = atoi(cp); 13191592Srgrimes cbuf[cpos] = c; 13201592Srgrimes state = STR1; 13211592Srgrimes return (NUMBER); 13221592Srgrimes } 13231592Srgrimes state = STR1; 13241592Srgrimes goto dostr1; 13251592Srgrimes 13261592Srgrimes case ARGS: 13271592Srgrimes if (isdigit(cbuf[cpos])) { 13281592Srgrimes cp = &cbuf[cpos]; 13291592Srgrimes while (isdigit(cbuf[++cpos])) 13301592Srgrimes ; 13311592Srgrimes c = cbuf[cpos]; 13321592Srgrimes cbuf[cpos] = '\0'; 13331592Srgrimes yylval.i = atoi(cp); 13341592Srgrimes cbuf[cpos] = c; 13351592Srgrimes return (NUMBER); 13361592Srgrimes } 133756668Sshin if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 133856668Sshin && !isalnum(cbuf[cpos + 3])) { 133956668Sshin cpos += 3; 134056668Sshin return ALL; 134156668Sshin } 13421592Srgrimes switch (cbuf[cpos++]) { 13431592Srgrimes 13441592Srgrimes case '\n': 13451592Srgrimes state = CMD; 13461592Srgrimes return (CRLF); 13471592Srgrimes 13481592Srgrimes case ' ': 13491592Srgrimes return (SP); 13501592Srgrimes 13511592Srgrimes case ',': 13521592Srgrimes return (COMMA); 13531592Srgrimes 13541592Srgrimes case 'A': 13551592Srgrimes case 'a': 13561592Srgrimes return (A); 13571592Srgrimes 13581592Srgrimes case 'B': 13591592Srgrimes case 'b': 13601592Srgrimes return (B); 13611592Srgrimes 13621592Srgrimes case 'C': 13631592Srgrimes case 'c': 13641592Srgrimes return (C); 13651592Srgrimes 13661592Srgrimes case 'E': 13671592Srgrimes case 'e': 13681592Srgrimes return (E); 13691592Srgrimes 13701592Srgrimes case 'F': 13711592Srgrimes case 'f': 13721592Srgrimes return (F); 13731592Srgrimes 13741592Srgrimes case 'I': 13751592Srgrimes case 'i': 13761592Srgrimes return (I); 13771592Srgrimes 13781592Srgrimes case 'L': 13791592Srgrimes case 'l': 13801592Srgrimes return (L); 13811592Srgrimes 13821592Srgrimes case 'N': 13831592Srgrimes case 'n': 13841592Srgrimes return (N); 13851592Srgrimes 13861592Srgrimes case 'P': 13871592Srgrimes case 'p': 13881592Srgrimes return (P); 13891592Srgrimes 13901592Srgrimes case 'R': 13911592Srgrimes case 'r': 13921592Srgrimes return (R); 13931592Srgrimes 13941592Srgrimes case 'S': 13951592Srgrimes case 's': 13961592Srgrimes return (S); 13971592Srgrimes 13981592Srgrimes case 'T': 13991592Srgrimes case 't': 14001592Srgrimes return (T); 14011592Srgrimes 14021592Srgrimes } 14031592Srgrimes break; 14041592Srgrimes 14051592Srgrimes default: 14061592Srgrimes fatal("Unknown state in scanner."); 14071592Srgrimes } 14081592Srgrimes yyerror((char *) 0); 14091592Srgrimes state = CMD; 14101592Srgrimes longjmp(errcatch,0); 14111592Srgrimes } 14121592Srgrimes} 14131592Srgrimes 14141592Srgrimesvoid 14151592Srgrimesupper(s) 14161592Srgrimes char *s; 14171592Srgrimes{ 14181592Srgrimes while (*s != '\0') { 14191592Srgrimes if (islower(*s)) 14201592Srgrimes *s = toupper(*s); 14211592Srgrimes s++; 14221592Srgrimes } 14231592Srgrimes} 14241592Srgrimes 14251592Srgrimesstatic char * 14261592Srgrimescopy(s) 14271592Srgrimes char *s; 14281592Srgrimes{ 14291592Srgrimes char *p; 14301592Srgrimes 14311592Srgrimes p = malloc((unsigned) strlen(s) + 1); 14321592Srgrimes if (p == NULL) 14331592Srgrimes fatal("Ran out of memory."); 14341592Srgrimes (void) strcpy(p, s); 14351592Srgrimes return (p); 14361592Srgrimes} 14371592Srgrimes 14381592Srgrimesstatic void 14391592Srgrimeshelp(ctab, s) 14401592Srgrimes struct tab *ctab; 14411592Srgrimes char *s; 14421592Srgrimes{ 14431592Srgrimes struct tab *c; 14441592Srgrimes int width, NCMDS; 14451592Srgrimes char *type; 14461592Srgrimes 14471592Srgrimes if (ctab == sitetab) 14481592Srgrimes type = "SITE "; 14491592Srgrimes else 14501592Srgrimes type = ""; 14511592Srgrimes width = 0, NCMDS = 0; 14521592Srgrimes for (c = ctab; c->name != NULL; c++) { 14531592Srgrimes int len = strlen(c->name); 14541592Srgrimes 14551592Srgrimes if (len > width) 14561592Srgrimes width = len; 14571592Srgrimes NCMDS++; 14581592Srgrimes } 14591592Srgrimes width = (width + 8) &~ 7; 14601592Srgrimes if (s == 0) { 14611592Srgrimes int i, j, w; 14621592Srgrimes int columns, lines; 14631592Srgrimes 14641592Srgrimes lreply(214, "The following %scommands are recognized %s.", 14651592Srgrimes type, "(* =>'s unimplemented)"); 14661592Srgrimes columns = 76 / width; 14671592Srgrimes if (columns == 0) 14681592Srgrimes columns = 1; 14691592Srgrimes lines = (NCMDS + columns - 1) / columns; 14701592Srgrimes for (i = 0; i < lines; i++) { 14711592Srgrimes printf(" "); 14721592Srgrimes for (j = 0; j < columns; j++) { 14731592Srgrimes c = ctab + j * lines + i; 14741592Srgrimes printf("%s%c", c->name, 14751592Srgrimes c->implemented ? ' ' : '*'); 14761592Srgrimes if (c + lines >= &ctab[NCMDS]) 14771592Srgrimes break; 14781592Srgrimes w = strlen(c->name) + 1; 14791592Srgrimes while (w < width) { 14801592Srgrimes putchar(' '); 14811592Srgrimes w++; 14821592Srgrimes } 14831592Srgrimes } 14841592Srgrimes printf("\r\n"); 14851592Srgrimes } 14861592Srgrimes (void) fflush(stdout); 14871592Srgrimes reply(214, "Direct comments to ftp-bugs@%s.", hostname); 14881592Srgrimes return; 14891592Srgrimes } 14901592Srgrimes upper(s); 14911592Srgrimes c = lookup(ctab, s); 14921592Srgrimes if (c == (struct tab *)0) { 14931592Srgrimes reply(502, "Unknown command %s.", s); 14941592Srgrimes return; 14951592Srgrimes } 14961592Srgrimes if (c->implemented) 14971592Srgrimes reply(214, "Syntax: %s%s %s", type, c->name, c->help); 14981592Srgrimes else 14991592Srgrimes reply(214, "%s%-*s\t%s; unimplemented.", type, width, 15001592Srgrimes c->name, c->help); 15011592Srgrimes} 15021592Srgrimes 15031592Srgrimesstatic void 15041592Srgrimessizecmd(filename) 15051592Srgrimes char *filename; 15061592Srgrimes{ 15071592Srgrimes switch (type) { 15081592Srgrimes case TYPE_L: 15091592Srgrimes case TYPE_I: { 15101592Srgrimes struct stat stbuf; 151163350Sdes if (stat(filename, &stbuf) < 0) 151263350Sdes perror_reply(550, filename); 151363350Sdes else if (!S_ISREG(stbuf.st_mode)) 15141592Srgrimes reply(550, "%s: not a plain file.", filename); 15151592Srgrimes else 15161592Srgrimes reply(213, "%qu", stbuf.st_size); 15171592Srgrimes break; } 15181592Srgrimes case TYPE_A: { 15191592Srgrimes FILE *fin; 15201592Srgrimes int c; 15211592Srgrimes off_t count; 15221592Srgrimes struct stat stbuf; 15231592Srgrimes fin = fopen(filename, "r"); 15241592Srgrimes if (fin == NULL) { 15251592Srgrimes perror_reply(550, filename); 15261592Srgrimes return; 15271592Srgrimes } 152863350Sdes if (fstat(fileno(fin), &stbuf) < 0) { 152963350Sdes perror_reply(550, filename); 153063350Sdes (void) fclose(fin); 153163350Sdes return; 153263350Sdes } else if (!S_ISREG(stbuf.st_mode)) { 15331592Srgrimes reply(550, "%s: not a plain file.", filename); 15341592Srgrimes (void) fclose(fin); 15351592Srgrimes return; 15361592Srgrimes } 15371592Srgrimes 15381592Srgrimes count = 0; 15391592Srgrimes while((c=getc(fin)) != EOF) { 15401592Srgrimes if (c == '\n') /* will get expanded to \r\n */ 15411592Srgrimes count++; 15421592Srgrimes count++; 15431592Srgrimes } 15441592Srgrimes (void) fclose(fin); 15451592Srgrimes 15461592Srgrimes reply(213, "%qd", count); 15471592Srgrimes break; } 15481592Srgrimes default: 15491592Srgrimes reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 15501592Srgrimes } 15511592Srgrimes} 155256668Sshin 155356668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 155456668Sshinstatic int 155556668Sshinport_check(pcmd) 155656668Sshin const char *pcmd; 155756668Sshin{ 155856668Sshin if (his_addr.su_family == AF_INET) { 155956668Sshin if (data_dest.su_family != AF_INET) { 156056668Sshin usedefault = 1; 156156668Sshin reply(500, "Invalid address rejected."); 156256668Sshin return 1; 156356668Sshin } 156456668Sshin if (paranoid && 156556668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 156656668Sshin memcmp(&data_dest.su_sin.sin_addr, 156756668Sshin &his_addr.su_sin.sin_addr, 156856668Sshin sizeof(data_dest.su_sin.sin_addr)))) { 156956668Sshin usedefault = 1; 157056668Sshin reply(500, "Illegal PORT range rejected."); 157156668Sshin } else { 157256668Sshin usedefault = 0; 157356668Sshin if (pdata >= 0) { 157456668Sshin (void) close(pdata); 157556668Sshin pdata = -1; 157656668Sshin } 157756668Sshin reply(200, "%s command successful.", pcmd); 157856668Sshin } 157956668Sshin return 1; 158056668Sshin } 158156668Sshin return 0; 158256668Sshin} 158356668Sshin 158470102Sphkstatic int 158570102Sphkcheck_login1() 158670102Sphk{ 158770102Sphk if (logged_in) 158870102Sphk return 1; 158970102Sphk else { 159070102Sphk reply(530, "Please login with USER and PASS."); 159170102Sphk return 0; 159270102Sphk } 159370102Sphk} 159470102Sphk 159556668Sshin#ifdef INET6 159656668Sshin/* Return 1, if port check is done. Return 0, if not yet. */ 159756668Sshinstatic int 159856668Sshinport_check_v6(pcmd) 159956668Sshin const char *pcmd; 160056668Sshin{ 160156668Sshin if (his_addr.su_family == AF_INET6) { 160256668Sshin if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 160356668Sshin /* Convert data_dest into v4 mapped sockaddr.*/ 160456668Sshin v4map_data_dest(); 160556668Sshin if (data_dest.su_family != AF_INET6) { 160656668Sshin usedefault = 1; 160756668Sshin reply(500, "Invalid address rejected."); 160856668Sshin return 1; 160956668Sshin } 161056668Sshin if (paranoid && 161156668Sshin ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 161256668Sshin memcmp(&data_dest.su_sin6.sin6_addr, 161356668Sshin &his_addr.su_sin6.sin6_addr, 161456668Sshin sizeof(data_dest.su_sin6.sin6_addr)))) { 161556668Sshin usedefault = 1; 161656668Sshin reply(500, "Illegal PORT range rejected."); 161756668Sshin } else { 161856668Sshin usedefault = 0; 161956668Sshin if (pdata >= 0) { 162056668Sshin (void) close(pdata); 162156668Sshin pdata = -1; 162256668Sshin } 162356668Sshin reply(200, "%s command successful.", pcmd); 162456668Sshin } 162556668Sshin return 1; 162656668Sshin } 162756668Sshin return 0; 162856668Sshin} 162956668Sshin 163056668Sshinstatic void 163156668Sshinv4map_data_dest() 163256668Sshin{ 163356668Sshin struct in_addr savedaddr; 163456668Sshin int savedport; 163556668Sshin 163656668Sshin if (data_dest.su_family != AF_INET) { 163756668Sshin usedefault = 1; 163856668Sshin reply(500, "Invalid address rejected."); 163956668Sshin return; 164056668Sshin } 164156668Sshin 164256668Sshin savedaddr = data_dest.su_sin.sin_addr; 164356668Sshin savedport = data_dest.su_port; 164456668Sshin 164556668Sshin memset(&data_dest, 0, sizeof(data_dest)); 164656668Sshin data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 164756668Sshin data_dest.su_sin6.sin6_family = AF_INET6; 164856668Sshin data_dest.su_sin6.sin6_port = savedport; 164956668Sshin memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 165056668Sshin memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 165156668Sshin (caddr_t)&savedaddr, sizeof(savedaddr)); 165256668Sshin} 165356668Sshin#endif 1654