1 /* $NetBSD: ftpcmd.y,v 1.66 2001/12/01 10:25:30 lukem Exp $ */
4 * Copyright (c) 1997-2001 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
40 * Copyright (c) 1985, 1988, 1993, 1994
41 * The Regents of the University of California. All rights reserved.
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 3. All advertising materials mentioning features or use of this software
52 * must display the following acknowledgement:
53 * This product includes software developed by the University of
54 * California, Berkeley and its contributors.
55 * 4. Neither the name of the University nor the names of its contributors
56 * may be used to endorse or promote products derived from this software
57 * without specific prior written permission.
59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
71 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
75 * Grammar for FTP commands.
80 #include "lukemftpd.h"
87 static int cmd_bytesz;
89 char cbuf[FTP_BUFLEN];
106 USER PASS ACCT CWD CDUP SMNT
107 QUIT REIN PORT PASV TYPE STRU
108 MODE RETR STOR STOU APPE ALLO
109 REST RNFR RNTO ABOR DELE RMD
110 MKD PWD LIST NLST SITE SYST
113 AUTH ADAT PROT PBSZ CCC MIC
122 MAIL MLFL MRCP MRSQ MSAM MSND
125 CHMOD IDLE RATEGET RATEPUT UMASK
133 %type <i> check_login octal_number byte_size
134 %type <i> struct_code mode_code type_code form_code decimal_integer
135 %type <s> pathstring pathname password username
136 %type <s> mechanism_name base64data prot_code
146 restart_point = (off_t) 0;
155 : USER SP username CRLF
161 | PASS SP password CRLF
164 memset($3, 0, strlen($3));
168 | CWD check_login CRLF
174 | CWD check_login SP pathname CRLF
176 if ($2 && $4 != NULL)
182 | CDUP check_login CRLF
191 reply(-221, "%s", "");
193 "Data traffic for this session was " LLF " byte%s in " LLF " file%s.",
194 (LLT)total_data, PLURAL(total_data),
195 (LLT)total_files, PLURAL(total_files));
197 "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.",
198 (LLT)total_bytes, PLURAL(total_bytes),
199 (LLT)total_xfers, PLURAL(total_xfers));
202 "Thank you for using the FTP service on %s.",
204 if (logged_in && logging) {
206 "Data traffic: " LLF " byte%s in " LLF " file%s",
207 (LLT)total_data, PLURAL(total_data),
208 (LLT)total_files, PLURAL(total_files));
210 "Total traffic: " LLF " byte%s in " LLF " transfer%s",
211 (LLT)total_bytes, PLURAL(total_bytes),
212 (LLT)total_xfers, PLURAL(total_xfers));
218 | PORT check_login SP host_port CRLF
221 port_check("PORT", AF_INET);
224 | LPRT check_login SP host_long_port4 CRLF
227 port_check("LPRT", AF_INET);
230 | LPRT check_login SP host_long_port6 CRLF
234 port_check("LPRT", AF_INET6);
236 reply(500, "IPv6 support not available.");
240 | EPRT check_login SP STRING CRLF
243 if (extended_port($4) == 0)
244 port_check("EPRT", -1);
249 | PASV check_login CRLF
252 if (CURCLASS_FLAGS_ISSET(passive))
255 reply(500, "PASV mode not available.");
259 | LPSV check_login CRLF
264 "LPSV disallowed after EPSV ALL");
266 long_passive("LPSV", PF_UNSPEC);
270 | EPSV check_login SP NUMBER CRLF
273 long_passive("EPSV", epsvproto2af($4));
276 | EPSV check_login SP ALL CRLF
279 reply(200, "EPSV ALL command successful.");
284 | EPSV check_login CRLF
287 long_passive("EPSV", PF_UNSPEC);
290 | TYPE check_login SP type_code CRLF
297 if (cmd_form == FORM_N) {
298 reply(200, "Type set to A.");
302 reply(504, "Form must be N.");
306 reply(504, "Type E not implemented.");
310 reply(200, "Type set to I.");
316 if (cmd_bytesz == 8) {
318 "Type set to L (byte size 8).");
321 reply(504, "Byte size must be 8.");
322 #else /* NBBY == 8 */
323 UNIMPLEMENTED for NBBY != 8
324 #endif /* NBBY == 8 */
330 | STRU check_login SP struct_code CRLF
336 reply(200, "STRU F ok.");
340 reply(504, "Unimplemented STRU type.");
345 | MODE check_login SP mode_code CRLF
351 reply(200, "MODE S ok.");
355 reply(502, "Unimplemented MODE type.");
360 | RETR check_login SP pathname CRLF
362 if ($2 && $4 != NULL)
368 | STOR SP pathname CRLF
370 if (check_write($3, 1))
376 | STOU SP pathname CRLF
378 if (check_write($3, 1))
384 | APPE SP pathname CRLF
386 if (check_write($3, 1))
392 | ALLO check_login SP NUMBER CRLF
395 reply(202, "ALLO command ignored.");
398 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
401 reply(202, "ALLO command ignored.");
404 | RNTO SP pathname CRLF
406 if (check_write($3, 0)) {
408 renamecmd(fromname, $3);
412 reply(503, "Bad sequence of commands.");
419 | ABOR check_login CRLF
424 reply(225, "ABOR command successful.");
427 | DELE SP pathname CRLF
429 if (check_write($3, 0))
435 | RMD SP pathname CRLF
437 if (check_write($3, 0))
443 | MKD SP pathname CRLF
445 if (check_write($3, 0))
451 | PWD check_login CRLF
457 | LIST check_login CRLF
459 char *argv[] = { INTERNAL_LS, "-lgA", NULL };
465 | LIST check_login SP pathname CRLF
467 char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL };
469 if ($2 && $4 != NULL) {
477 | NLST check_login CRLF
483 | NLST check_login SP pathname CRLF
495 | SITE SP CHMOD SP octal_number SP pathname CRLF
497 if (check_write($7, 0)) {
500 "CHMOD: Mode value must be between 0 and 0777");
501 else if (chmod($7, $5) < 0)
502 perror_reply(550, $7);
504 reply(200, "CHMOD command successful.");
510 | SITE SP HELP SP STRING CRLF
516 | SITE SP IDLE check_login CRLF
520 "Current IDLE time limit is %d seconds; max %d",
521 curclass.timeout, curclass.maxtimeout);
525 | SITE SP IDLE check_login SP NUMBER CRLF
528 if ($6 < 30 || $6 > curclass.maxtimeout) {
530 "IDLE time limit must be between 30 and %d seconds",
531 curclass.maxtimeout);
533 curclass.timeout = $6;
534 (void) alarm(curclass.timeout);
536 "IDLE time limit set to %d seconds",
542 | SITE SP RATEGET check_login CRLF
546 "Current RATEGET is " LLF " bytes/sec",
547 (LLT)curclass.rateget);
551 | SITE SP RATEGET check_login SP STRING CRLF
557 rate = strsuftoll(p);
559 reply(501, "Invalid RATEGET %s", p);
560 else if (curclass.maxrateget &&
561 rate > curclass.maxrateget)
563 "RATEGET " LLF " is larger than maximum RATEGET " LLF,
565 (LLT)curclass.maxrateget);
567 curclass.rateget = rate;
569 "RATEGET set to " LLF " bytes/sec",
570 (LLT)curclass.rateget);
576 | SITE SP RATEPUT check_login CRLF
580 "Current RATEPUT is " LLF " bytes/sec",
581 (LLT)curclass.rateput);
585 | SITE SP RATEPUT check_login SP STRING CRLF
591 rate = strsuftoll(p);
593 reply(501, "Invalid RATEPUT %s", p);
594 else if (curclass.maxrateput &&
595 rate > curclass.maxrateput)
597 "RATEPUT " LLF " is larger than maximum RATEPUT " LLF,
599 (LLT)curclass.maxrateput);
601 curclass.rateput = rate;
603 "RATEPUT set to " LLF " bytes/sec",
604 (LLT)curclass.rateput);
610 | SITE SP UMASK check_login CRLF
616 (void) umask(oldmask);
617 reply(200, "Current UMASK is %03o", oldmask);
621 | SITE SP UMASK check_login SP octal_number CRLF
625 if ($4 && CURCLASS_FLAGS_ISSET(modify)) {
626 if (($6 == -1) || ($6 > 0777)) {
627 reply(501, "Bad UMASK value");
631 "UMASK set to %03o (was %03o)",
639 if (EMPTYSTR(version))
640 reply(215, "UNIX Type: L%d", NBBY);
642 reply(215, "UNIX Type: L%d Version: %s", NBBY,
646 | STAT check_login SP pathname CRLF
648 if ($2 && $4 != NULL)
667 | HELP SP STRING CRLF
671 if (strncasecmp(cp, "SITE", 4) == 0) {
686 reply(200, "NOOP command successful.");
690 | AUTH SP mechanism_name CRLF
692 reply(502, "RFC 2228 authentication not implemented.");
696 | ADAT SP base64data CRLF
699 "Please set authentication state with AUTH.");
703 | PROT SP prot_code CRLF
706 "Please set protection buffer size with PBSZ.");
710 | PBSZ SP decimal_integer CRLF
713 "Please set authentication state with AUTH.");
718 reply(533, "No protection enabled.");
721 | MIC SP base64data CRLF
723 reply(502, "RFC 2228 authentication not implemented.");
727 | CONF SP base64data CRLF
729 reply(502, "RFC 2228 authentication not implemented.");
733 | ENC SP base64data CRLF
735 reply(502, "RFC 2228 authentication not implemented.");
746 | OPTS SP STRING CRLF
754 /* extensions from draft-ietf-ftpext-mlst-11 */
757 * Return size of file in a format suitable for
758 * using with RESTART (we just count bytes).
760 | SIZE check_login SP pathname CRLF
762 if ($2 && $4 != NULL)
769 * Return modification time of file as an ISO 3307
770 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
771 * where xxx is the fractional second (of any precision,
772 * not necessarily 3 digits)
774 | MDTM check_login SP pathname CRLF
776 if ($2 && $4 != NULL) {
778 if (stat($4, &stbuf) < 0)
779 perror_reply(550, $4);
780 else if (!S_ISREG(stbuf.st_mode)) {
781 reply(550, "%s: not a plain file.", $4);
785 t = gmtime(&stbuf.st_mtime);
787 "%04d%02d%02d%02d%02d%02d",
788 TM_YEAR_BASE + t->tm_year,
789 t->tm_mon+1, t->tm_mday,
790 t->tm_hour, t->tm_min, t->tm_sec);
797 | MLST check_login SP pathname CRLF
799 if ($2 && $4 != NULL)
805 | MLST check_login CRLF
810 | MLSD check_login SP pathname CRLF
812 if ($2 && $4 != NULL)
818 | MLSD check_login CRLF
830 : REST check_login SP byte_size CRLF
834 restart_point = $4; /* XXX: $4 is only "int" */
836 "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.",
841 | RNFR SP pathname CRLF
843 restart_point = (off_t) 0;
844 if (check_write($3, 0))
845 fromname = renamefrom($3);
858 $$ = (char *)calloc(1, sizeof(char));
869 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
874 memset(&data_dest, 0, sizeof(data_dest));
875 data_dest.su_len = sizeof(struct sockaddr_in);
876 data_dest.su_family = AF_INET;
877 p = (char *)&data_dest.su_port;
878 p[0] = $9; p[1] = $11;
879 a = (char *)&data_dest.su_addr;
880 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
885 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
886 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
891 memset(&data_dest, 0, sizeof(data_dest));
892 data_dest.su_len = sizeof(struct sockaddr_in);
893 data_dest.su_family = AF_INET;
894 p = (char *)&data_dest.su_port;
895 p[0] = $15; p[1] = $17;
896 a = (char *)&data_dest.su_addr;
897 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
899 /* reject invalid LPRT command */
900 if ($1 != 4 || $3 != 4 || $13 != 2)
901 memset(&data_dest, 0, sizeof(data_dest));
906 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
907 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
908 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
909 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
910 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
916 memset(&data_dest, 0, sizeof(data_dest));
917 data_dest.su_len = sizeof(struct sockaddr_in6);
918 data_dest.su_family = AF_INET6;
919 p = (char *)&data_dest.su_port;
920 p[0] = $39; p[1] = $41;
921 a = (char *)&data_dest.si_su.su_sin6.sin6_addr;
922 a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
923 a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19;
924 a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27;
925 a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
926 if (his_addr.su_family == AF_INET6) {
927 /* XXX: more sanity checks! */
928 data_dest.su_scope_id = his_addr.su_scope_id;
931 memset(&data_dest, 0, sizeof(data_dest));
933 /* reject invalid LPRT command */
934 if ($1 != 6 || $3 != 16 || $37 != 2)
935 memset(&data_dest, 0, sizeof(data_dest));
998 /* this is for a bug in the BBN ftp */
1044 * Problem: this production is used for all pathname
1045 * processing, but only gives a 550 error reply.
1046 * This is a valid reply in some cases but not in
1049 if (logged_in && $1 && *$1 == '~') {
1050 char *path, *home, *result;
1053 path = strchr($1 + 1, '/');
1061 if ((hpw = getpwnam($1 + 1)) != NULL)
1066 len = strlen(home) + 1;
1068 len += strlen(path) + 1;
1069 if ((result = malloc(len)) == NULL)
1070 fatal("Local resource failure: malloc");
1071 strlcpy(result, home, len);
1073 strlcat(result, "/", len);
1074 strlcat(result, path, len);
1090 int ret, dec, multby, digit;
1093 * Convert a number that was read as decimal number
1094 * to what it would be if it had been read as octal.
1105 ret += digit * multby;
1135 reply(530, "Please login with USER and PASS.");
1144 #define CMD 0 /* beginning of command */
1145 #define ARGS 1 /* expect miscellaneous arguments */
1146 #define STR1 2 /* expect SP followed by STRING */
1147 #define STR2 3 /* expect STRING */
1148 #define OSTR 4 /* optional SP then STRING */
1149 #define ZSTR1 5 /* SP then optional STRING */
1150 #define ZSTR2 6 /* optional STRING after SP */
1151 #define SITECMD 7 /* SITE command */
1152 #define NSTR 8 /* Number followed by a string */
1153 #define NOARGS 9 /* No arguments allowed */
1154 #define EOLN 10 /* End of line */
1156 struct tab cmdtab[] = {
1157 /* From RFC 959, in order defined (5.3.1) */
1158 { "USER", USER, STR1, 1, "<sp> username" },
1159 { "PASS", PASS, ZSTR1, 1, "<sp> password" },
1160 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1161 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1162 { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)" },
1163 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1164 { "QUIT", QUIT, NOARGS, 1, "(terminate service)" },
1165 { "REIN", REIN, NOARGS, 0, "(reinitialize server state)" },
1166 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
1167 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1168 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1169 { "PASV", PASV, NOARGS, 1, "(set server in passive mode)" },
1170 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1171 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1172 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
1173 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1174 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1175 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1176 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1177 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1178 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1179 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1180 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1181 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1182 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1183 { "ABOR", ABOR, NOARGS, 4, "(abort operation)" },
1184 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1185 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1186 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1187 { "PWD", PWD, NOARGS, 1, "(return current directory)" },
1188 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1189 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1190 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1191 { "SYST", SYST, NOARGS, 1, "(get type of operating system)" },
1192 { "STAT", STAT, OSTR, 4, "[ <sp> path-name ]" },
1193 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1194 { "NOOP", NOOP, NOARGS, 2, "" },
1196 /* From RFC 2228, in order defined */
1197 { "AUTH", AUTH, STR1, 1, "<sp> mechanism-name" },
1198 { "ADAT", ADAT, STR1, 1, "<sp> base-64-data" },
1199 { "PROT", PROT, STR1, 1, "<sp> prot-code" },
1200 { "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer" },
1201 { "CCC", CCC, NOARGS, 1, "(Disable data protection)" },
1202 { "MIC", MIC, STR1, 4, "<sp> base64data" },
1203 { "CONF", CONF, STR1, 4, "<sp> base64data" },
1204 { "ENC", ENC, STR1, 4, "<sp> base64data" },
1206 /* From RFC 2389, in order defined */
1207 { "FEAT", FEAT, NOARGS, 1, "(display extended features)" },
1208 { "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]" },
1210 /* from draft-ietf-ftpext-mlst-11 */
1211 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1212 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1213 { "MLST", MLST, OSTR, 2, "[ <sp> path-name ]" },
1214 { "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]" },
1216 /* obsolete commands */
1217 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1218 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1219 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1220 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1221 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1222 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1223 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1224 { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)" },
1225 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1226 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1227 { "XPWD", PWD, NOARGS, 1, "(return current directory)" },
1228 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1230 { NULL, 0, 0, 0, 0 }
1233 struct tab sitetab[] = {
1234 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1235 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1236 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1237 { "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]" },
1238 { "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]" },
1239 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1240 { NULL, 0, 0, 0, NULL }
1243 static int check_write(const char *, int);
1244 static void help(struct tab *, const char *);
1245 static void port_check(const char *, int);
1246 static void toolong(int);
1247 static int yylex(void);
1252 * Check if a filename is allowed to be modified (isupload == 0) or
1253 * uploaded (isupload == 1), and if necessary, check the filename is `sane'.
1256 check_write(const char *file, int isupload)
1261 reply(530, "Please login with USER and PASS.");
1264 /* checking modify */
1265 if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) {
1266 reply(502, "No permission to use this command.");
1269 /* checking upload */
1270 if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) {
1271 reply(502, "No permission to use this command.");
1274 /* checking sanenames */
1275 if (CURCLASS_FLAGS_ISSET(sanenames)) {
1280 for (p = file; *p; p++) {
1281 if (isalnum(*p) || *p == '-' || *p == '+' ||
1282 *p == ',' || *p == '.' || *p == '_')
1285 reply(553, "File name `%s' not allowed.", file);
1293 lookup(struct tab *p, const char *cmd)
1296 for (; p->name != NULL; p++)
1297 if (strcasecmp(cmd, p->name) == 0)
1302 #include <arpa/telnet.h>
1305 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1308 getline(char *s, int n, FILE *iop)
1314 /* tmpline may contain saved command from urgent mode interruption */
1315 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1317 if (tmpline[c] == '\n') {
1320 syslog(LOG_DEBUG, "command: %s", s);
1327 while ((c = getc(iop)) != EOF) {
1332 if ((c = getc(iop)) != EOF) {
1342 cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c);
1343 (void) fflush(stdout);
1350 cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c);
1351 (void) fflush(stdout);
1356 continue; /* ignore command */
1361 if (--n <= 0 || c == '\n')
1364 if (c == EOF && cs == s)
1368 if ((curclass.type != CLASS_GUEST &&
1369 strncasecmp(s, "PASS ", 5) == 0) ||
1370 strncasecmp(s, "ACCT ", 5) == 0) {
1371 /* Don't syslog passwords */
1372 syslog(LOG_DEBUG, "command: %.4s ???", s);
1377 /* Don't syslog trailing CR-LF */
1380 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1384 syslog(LOG_DEBUG, "command: %.*s", len, s);
1395 "Timeout (%d seconds): closing control connection.",
1398 syslog(LOG_INFO, "User %s timed out after %d seconds",
1399 (pw ? pw->pw_name : "unknown"), curclass.timeout);
1404 ftp_handle_line(char *cp)
1416 (void) signal(SIGALRM, toolong);
1417 (void) alarm(curclass.timeout);
1418 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1419 reply(221, "You could at least say goodbye.");
1423 ftp_handle_line(cbuf);
1431 static int cpos, state;
1441 if ((cp = strchr(cmdp, '\r'))) {
1443 #if HAVE_SETPROCTITLE
1444 if (strncasecmp(cmdp, "PASS", 4) != 0 &&
1445 strncasecmp(cmdp, "ACCT", 4) != 0)
1446 setproctitle("%s: %s", proctitle, cmdp);
1447 #endif /* HAVE_SETPROCTITLE */
1451 if ((cp = strpbrk(cmdp, " \n")))
1457 p = lookup(cmdtab, cmdp);
1460 if (is_oob && ! CMD_OOB(p)) {
1461 /* command will be handled in-band */
1463 } else if (! CMD_IMPLEMENTED(p)) {
1464 reply(502, "%s command not implemented.",
1476 if (cmdp[cpos] == ' ') {
1481 if ((cp2 = strpbrk(cp, " \n")))
1485 p = lookup(sitetab, cp);
1488 if (!CMD_IMPLEMENTED(p)) {
1489 reply(502, "SITE %s command not implemented.",
1501 if (cmdp[cpos] == '\n') {
1510 if (cmdp[cpos] == ' ') {
1512 state = state == OSTR ? STR2 : state+1;
1518 if (cmdp[cpos] == '\n') {
1529 * Make sure the string is nonempty and \n terminated.
1531 if (n > 1 && cmdp[cpos] == '\n') {
1533 yylval.s = xstrdup(cp);
1541 if (cmdp[cpos] == ' ') {
1545 if (isdigit(cmdp[cpos])) {
1547 while (isdigit(cmdp[++cpos]))
1551 yylval.i = atoi(cp);
1560 if (isdigit(cmdp[cpos])) {
1562 while (isdigit(cmdp[++cpos]))
1566 yylval.i = atoi(cp);
1570 if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0
1571 && !isalnum(cmdp[cpos + 3])) {
1572 yylval.s = xstrdup("ALL");
1576 switch (cmdp[cpos++]) {
1640 if (cmdp[cpos] == '\n') {
1646 reply(501, "'%s' command does not take any arguments.", cmdp);
1656 fatal("Unknown state in scanner.");
1661 longjmp(errcatch, 0);
1671 if (hasyyerrored || is_oob)
1673 if ((cp = strchr(cmdp,'\n')) != NULL)
1675 reply(500, "'%s': command not understood.", cmdp);
1680 help(struct tab *ctab, const char *s)
1686 if (ctab == sitetab)
1690 width = 0, NCMDS = 0;
1691 for (c = ctab; c->name != NULL; c++) {
1692 int len = strlen(c->name);
1698 width = (width + 8) &~ 7;
1703 reply(-214, "%s", "");
1704 reply(0, "The following %scommands are recognized.", htype);
1705 reply(0, "(`-' = not implemented, `+' = supports options)");
1706 columns = 76 / width;
1709 lines = (NCMDS + columns - 1) / columns;
1710 for (i = 0; i < lines; i++) {
1711 cprintf(stdout, " ");
1712 for (j = 0; j < columns; j++) {
1713 c = ctab + j * lines + i;
1714 cprintf(stdout, "%s", c->name);
1715 w = strlen(c->name);
1716 if (! CMD_IMPLEMENTED(c)) {
1720 if (CMD_HAS_OPTIONS(c)) {
1724 if (c + lines >= &ctab[NCMDS])
1731 cprintf(stdout, "\r\n");
1733 (void) fflush(stdout);
1734 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1737 c = lookup(ctab, s);
1738 if (c == (struct tab *)0) {
1739 reply(502, "Unknown command %s.", s);
1742 if (CMD_IMPLEMENTED(c))
1743 reply(214, "Syntax: %s%s %s", htype, c->name, c->help);
1745 reply(214, "%s%-*s\t%s; not implemented.", htype, width,
1750 * Check that the structures used for a PORT, LPRT or EPRT command are
1751 * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks.
1752 * If family != -1 check that his_addr.su_family == family.
1755 port_check(const char *cmd, int family)
1757 char h1[NI_MAXHOST], h2[NI_MAXHOST];
1758 char s1[NI_MAXHOST], s2[NI_MAXHOST];
1759 #ifdef NI_WITHSCOPEID
1760 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
1762 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
1766 reply(501, "%s disallowed after EPSV ALL", cmd);
1770 if (family != -1 && his_addr.su_family != family) {
1772 reply(500, "Illegal %s command rejected", cmd);
1776 if (data_dest.su_family != his_addr.su_family)
1777 goto port_check_fail;
1779 /* be paranoid, if told so */
1780 if (CURCLASS_FLAGS_ISSET(checkportcmd)) {
1783 * be paranoid, there are getnameinfo implementation that does
1784 * not present scopeid portion
1786 if (data_dest.su_family == AF_INET6 &&
1787 data_dest.su_scope_id != his_addr.su_scope_id)
1788 goto port_check_fail;
1791 if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len,
1792 h1, sizeof(h1), s1, sizeof(s1), niflags))
1793 goto port_check_fail;
1794 if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
1795 h2, sizeof(h2), s2, sizeof(s2), niflags))
1796 goto port_check_fail;
1798 if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0)
1799 goto port_check_fail;
1804 (void) close(pdata);
1807 reply(200, "%s command successful.", cmd);