1 /* $NetBSD: ftpcmd.y,v 1.84 2006/02/01 14:20:12 christos Exp $ */
4 * Copyright (c) 1997-2005 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. Neither the name of the University nor the names of its contributors
52 * may be used to endorse or promote products derived from this software
53 * without specific prior written permission.
55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
67 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
71 * Grammar for FTP commands.
76 #include <sys/cdefs.h>
80 static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94";
82 __RCSID("$NetBSD: ftpcmd.y,v 1.84 2006/02/01 14:20:12 christos Exp $");
86 #include <sys/param.h>
87 #include <sys/socket.h>
90 #include <netinet/in.h>
92 #include <arpa/inet.h>
107 #include <krb5/krb5.h>
115 static int cmd_bytesz;
117 char cbuf[FTP_BUFLEN];
122 struct tab sitetab[];
124 static int check_write(const char *, int);
125 static void help(struct tab *, const char *);
126 static void port_check(const char *, int);
145 USER PASS ACCT CWD CDUP SMNT
146 QUIT REIN PORT PASV TYPE STRU
147 MODE RETR STOR STOU APPE ALLO
148 REST RNFR RNTO ABOR DELE RMD
149 MKD PWD LIST NLST SITE SYST
152 AUTH ADAT PROT PBSZ CCC MIC
161 MAIL MLFL MRCP MRSQ MSAM MSND
164 CHMOD IDLE RATEGET RATEPUT UMASK
171 %type <u.i> check_login octal_number byte_size
172 %type <u.i> struct_code mode_code type_code form_code decimal_integer
173 %type <s> pathstring pathname password username
174 %type <s> mechanism_name base64data prot_code
183 REASSIGN(fromname, NULL);
184 restart_point = (off_t) 0;
193 : USER SP username CRLF
199 | PASS SP password CRLF
202 memset($3, 0, strlen($3));
206 | CWD check_login CRLF
212 | CWD check_login SP pathname CRLF
214 if ($2 && $4 != NULL)
220 | CDUP check_login CRLF
229 reply(-221, "%s", "");
231 "Data traffic for this session was " LLF " byte%s in " LLF " file%s.",
232 (LLT)total_data, PLURAL(total_data),
233 (LLT)total_files, PLURAL(total_files));
235 "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.",
236 (LLT)total_bytes, PLURAL(total_bytes),
237 (LLT)total_xfers, PLURAL(total_xfers));
240 "Thank you for using the FTP service on %s.",
242 if (logged_in && logging) {
244 "Data traffic: " LLF " byte%s in " LLF " file%s",
245 (LLT)total_data, PLURAL(total_data),
246 (LLT)total_files, PLURAL(total_files));
248 "Total traffic: " LLF " byte%s in " LLF " transfer%s",
249 (LLT)total_bytes, PLURAL(total_bytes),
250 (LLT)total_xfers, PLURAL(total_xfers));
256 | PORT check_login SP host_port CRLF
259 port_check("PORT", AF_INET);
262 | LPRT check_login SP host_long_port4 CRLF
265 port_check("LPRT", AF_INET);
268 | LPRT check_login SP host_long_port6 CRLF
272 port_check("LPRT", AF_INET6);
274 reply(500, "IPv6 support not available.");
278 | EPRT check_login SP STRING CRLF
281 if (extended_port($4) == 0)
282 port_check("EPRT", -1);
287 | PASV check_login CRLF
290 if (CURCLASS_FLAGS_ISSET(passive))
293 reply(500, "PASV mode not available.");
297 | LPSV check_login CRLF
300 if (CURCLASS_FLAGS_ISSET(passive)) {
303 "LPSV disallowed after EPSV ALL");
305 long_passive("LPSV", PF_UNSPEC);
307 reply(500, "LPSV mode not available.");
311 | EPSV check_login SP NUMBER CRLF
314 if (CURCLASS_FLAGS_ISSET(passive))
318 reply(500, "EPSV mode not available.");
322 | EPSV check_login SP ALL CRLF
325 if (CURCLASS_FLAGS_ISSET(passive)) {
327 "EPSV ALL command successful.");
330 reply(500, "EPSV mode not available.");
334 | EPSV check_login CRLF
337 if (CURCLASS_FLAGS_ISSET(passive))
338 long_passive("EPSV", PF_UNSPEC);
340 reply(500, "EPSV mode not available.");
344 | TYPE check_login SP type_code CRLF
351 if (cmd_form == FORM_N) {
352 reply(200, "Type set to A.");
356 reply(504, "Form must be N.");
360 reply(504, "Type E not implemented.");
364 reply(200, "Type set to I.");
370 if (cmd_bytesz == 8) {
372 "Type set to L (byte size 8).");
375 reply(504, "Byte size must be 8.");
376 #else /* NBBY == 8 */
377 UNIMPLEMENTED for NBBY != 8
378 #endif /* NBBY == 8 */
384 | STRU check_login SP struct_code CRLF
390 reply(200, "STRU F ok.");
394 reply(504, "Unimplemented STRU type.");
399 | MODE check_login SP mode_code CRLF
405 reply(200, "MODE S ok.");
409 reply(502, "Unimplemented MODE type.");
414 | RETR check_login SP pathname CRLF
416 if ($2 && $4 != NULL)
422 | STOR SP pathname CRLF
424 if (check_write($3, 1))
430 | STOU SP pathname CRLF
432 if (check_write($3, 1))
438 | APPE SP pathname CRLF
440 if (check_write($3, 1))
446 | ALLO check_login SP NUMBER CRLF
449 reply(202, "ALLO command ignored.");
452 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
455 reply(202, "ALLO command ignored.");
458 | RNTO SP pathname CRLF
460 if (check_write($3, 0)) {
462 renamecmd(fromname, $3);
463 REASSIGN(fromname, NULL);
465 reply(503, "Bad sequence of commands.");
472 | ABOR check_login CRLF
477 reply(225, "ABOR command successful.");
480 | DELE SP pathname CRLF
482 if (check_write($3, 0))
488 | RMD SP pathname CRLF
490 if (check_write($3, 0))
496 | MKD SP pathname CRLF
498 if (check_write($3, 0))
504 | PWD check_login CRLF
510 | LIST check_login CRLF
512 char *argv[] = { INTERNAL_LS, "-lgA", NULL };
514 if (CURCLASS_FLAGS_ISSET(hidesymlinks))
520 | LIST check_login SP pathname CRLF
522 char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL };
524 if (CURCLASS_FLAGS_ISSET(hidesymlinks))
526 if ($2 && $4 != NULL) {
534 | NLST check_login CRLF
540 | NLST check_login SP pathname CRLF
552 | SITE SP CHMOD SP octal_number SP pathname CRLF
554 if (check_write($7, 0)) {
555 if (($5 == -1) || ($5 > 0777))
557 "CHMOD: Mode value must be between 0 and 0777");
558 else if (chmod($7, $5) < 0)
559 perror_reply(550, $7);
561 reply(200, "CHMOD command successful.");
567 | SITE SP HELP SP STRING CRLF
573 | SITE SP IDLE check_login CRLF
577 "Current IDLE time limit is " LLF
578 " seconds; max " LLF,
579 (LLT)curclass.timeout,
580 (LLT)curclass.maxtimeout);
584 | SITE SP IDLE check_login SP NUMBER CRLF
587 if ($6.i < 30 || $6.i > curclass.maxtimeout) {
589 "IDLE time limit must be between 30 and "
591 (LLT)curclass.maxtimeout);
593 curclass.timeout = $6.i;
594 (void) alarm(curclass.timeout);
596 "IDLE time limit set to "
598 (LLT)curclass.timeout);
603 | SITE SP RATEGET check_login CRLF
607 "Current RATEGET is " LLF " bytes/sec",
608 (LLT)curclass.rateget);
612 | SITE SP RATEGET check_login SP STRING CRLF
619 rate = strsuftollx("RATEGET", p, 0,
621 ? curclass.maxrateget
622 : LLTMAX, errbuf, sizeof(errbuf));
624 reply(501, "%s", errbuf);
626 curclass.rateget = rate;
628 "RATEGET set to " LLF " bytes/sec",
629 (LLT)curclass.rateget);
635 | SITE SP RATEPUT check_login CRLF
639 "Current RATEPUT is " LLF " bytes/sec",
640 (LLT)curclass.rateput);
644 | SITE SP RATEPUT check_login SP STRING CRLF
651 rate = strsuftollx("RATEPUT", p, 0,
653 ? curclass.maxrateput
654 : LLTMAX, errbuf, sizeof(errbuf));
656 reply(501, "%s", errbuf);
658 curclass.rateput = rate;
660 "RATEPUT set to " LLF " bytes/sec",
661 (LLT)curclass.rateput);
667 | SITE SP UMASK check_login CRLF
673 (void) umask(oldmask);
674 reply(200, "Current UMASK is %03o", oldmask);
678 | SITE SP UMASK check_login SP octal_number CRLF
682 if ($4 && check_write("", 0)) {
683 if (($6 == -1) || ($6 > 0777)) {
684 reply(501, "Bad UMASK value");
688 "UMASK set to %03o (was %03o)",
696 if (EMPTYSTR(version))
697 reply(215, "UNIX Type: L%d", NBBY);
699 reply(215, "UNIX Type: L%d Version: %s", NBBY,
703 | STAT check_login SP pathname CRLF
705 if ($2 && $4 != NULL)
724 | HELP SP STRING CRLF
728 if (strncasecmp(cp, "SITE", 4) == 0) {
743 reply(200, "NOOP command successful.");
747 | AUTH SP mechanism_name CRLF
749 reply(502, "RFC 2228 authentication not implemented.");
753 | ADAT SP base64data CRLF
756 "Please set authentication state with AUTH.");
760 | PROT SP prot_code CRLF
763 "Please set protection buffer size with PBSZ.");
767 | PBSZ SP decimal_integer CRLF
770 "Please set authentication state with AUTH.");
775 reply(533, "No protection enabled.");
778 | MIC SP base64data CRLF
780 reply(502, "RFC 2228 authentication not implemented.");
784 | CONF SP base64data CRLF
786 reply(502, "RFC 2228 authentication not implemented.");
790 | ENC SP base64data CRLF
792 reply(502, "RFC 2228 authentication not implemented.");
803 | OPTS SP STRING CRLF
811 /* extensions from draft-ietf-ftpext-mlst-11 */
814 * Return size of file in a format suitable for
815 * using with RESTART (we just count bytes).
817 | SIZE check_login SP pathname CRLF
819 if ($2 && $4 != NULL)
826 * Return modification time of file as an ISO 3307
827 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
828 * where xxx is the fractional second (of any precision,
829 * not necessarily 3 digits)
831 | MDTM check_login SP pathname CRLF
833 if ($2 && $4 != NULL) {
835 if (stat($4, &stbuf) < 0)
836 perror_reply(550, $4);
837 else if (!S_ISREG(stbuf.st_mode)) {
838 reply(550, "%s: not a plain file.", $4);
842 t = gmtime(&stbuf.st_mtime);
844 "%04d%02d%02d%02d%02d%02d",
845 TM_YEAR_BASE + t->tm_year,
846 t->tm_mon+1, t->tm_mday,
847 t->tm_hour, t->tm_min, t->tm_sec);
854 | MLST check_login SP pathname CRLF
856 if ($2 && $4 != NULL)
862 | MLST check_login CRLF
867 | MLSD check_login SP pathname CRLF
869 if ($2 && $4 != NULL)
875 | MLSD check_login CRLF
887 : REST check_login SP NUMBER CRLF
890 REASSIGN(fromname, NULL);
891 restart_point = (off_t)$4.ll;
893 "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.",
898 | RNFR SP pathname CRLF
900 restart_point = (off_t) 0;
901 if (check_write($3, 0)) {
902 REASSIGN(fromname, NULL);
903 fromname = renamefrom($3);
917 $$ = (char *)calloc(1, sizeof(char));
931 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
936 memset(&data_dest, 0, sizeof(data_dest));
937 data_dest.su_len = sizeof(struct sockaddr_in);
938 data_dest.su_family = AF_INET;
939 p = (char *)&data_dest.su_port;
940 p[0] = $9.i; p[1] = $11.i;
941 a = (char *)&data_dest.su_addr;
942 a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
947 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
948 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
953 memset(&data_dest, 0, sizeof(data_dest));
954 data_dest.su_len = sizeof(struct sockaddr_in);
955 data_dest.su_family = AF_INET;
956 p = (char *)&data_dest.su_port;
957 p[0] = $15.i; p[1] = $17.i;
958 a = (char *)&data_dest.su_addr;
959 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
961 /* reject invalid LPRT command */
962 if ($1.i != 4 || $3.i != 4 || $13.i != 2)
963 memset(&data_dest, 0, sizeof(data_dest));
968 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
969 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
970 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
971 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
972 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
978 memset(&data_dest, 0, sizeof(data_dest));
979 data_dest.su_len = sizeof(struct sockaddr_in6);
980 data_dest.su_family = AF_INET6;
981 p = (char *)&data_dest.su_port;
982 p[0] = $39.i; p[1] = $41.i;
983 a = (char *)&data_dest.si_su.su_sin6.sin6_addr;
984 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
985 a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
986 a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
987 a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
988 if (his_addr.su_family == AF_INET6) {
989 /* XXX: more sanity checks! */
990 data_dest.su_scope_id = his_addr.su_scope_id;
993 memset(&data_dest, 0, sizeof(data_dest));
995 /* reject invalid LPRT command */
996 if ($1.i != 6 || $3.i != 16 || $37.i != 2)
997 memset(&data_dest, 0, sizeof(data_dest));
1060 /* this is for a bug in the BBN ftp */
1106 * Problem: this production is used for all pathname
1107 * processing, but only gives a 550 error reply.
1108 * This is a valid reply in some cases but not in
1111 if (logged_in && $1 && *$1 == '~') {
1112 char *path, *home, *result;
1115 path = strchr($1 + 1, '/');
1123 if ((hpw = getpwnam($1 + 1)) != NULL)
1128 len = strlen(home) + 1;
1130 len += strlen(path) + 1;
1131 if ((result = malloc(len)) == NULL)
1132 fatal("Local resource failure: malloc");
1133 strlcpy(result, home, len);
1135 strlcat(result, "/", len);
1136 strlcat(result, path, len);
1152 int ret, dec, multby, digit;
1155 * Convert a number that was read as decimal number
1156 * to what it would be if it had been read as octal.
1167 ret += digit * multby;
1200 reply(530, "Please login with USER and PASS.");
1209 #define CMD 0 /* beginning of command */
1210 #define ARGS 1 /* expect miscellaneous arguments */
1211 #define STR1 2 /* expect SP followed by STRING */
1212 #define STR2 3 /* expect STRING */
1213 #define OSTR 4 /* optional SP then STRING */
1214 #define ZSTR1 5 /* SP then optional STRING */
1215 #define ZSTR2 6 /* optional STRING after SP */
1216 #define SITECMD 7 /* SITE command */
1217 #define NSTR 8 /* Number followed by a string */
1218 #define NOARGS 9 /* No arguments allowed */
1219 #define EOLN 10 /* End of line */
1221 struct tab cmdtab[] = {
1222 /* From RFC 959, in order defined (5.3.1) */
1223 { "USER", USER, STR1, 1, "<sp> username" },
1224 { "PASS", PASS, ZSTR1, 1, "<sp> password" },
1225 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1226 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1227 { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)" },
1228 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1229 { "QUIT", QUIT, NOARGS, 1, "(terminate service)" },
1230 { "REIN", REIN, NOARGS, 0, "(reinitialize server state)" },
1231 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" },
1232 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1233 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1234 { "PASV", PASV, NOARGS, 1, "(set server in passive mode)" },
1235 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1236 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1237 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
1238 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1239 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1240 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1241 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1242 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1243 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1244 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1245 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1246 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1247 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1248 { "ABOR", ABOR, NOARGS, 4, "(abort operation)" },
1249 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1250 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1251 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1252 { "PWD", PWD, NOARGS, 1, "(return current directory)" },
1253 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1254 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1255 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1256 { "SYST", SYST, NOARGS, 1, "(get type of operating system)" },
1257 { "STAT", STAT, OSTR, 4, "[ <sp> path-name ]" },
1258 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1259 { "NOOP", NOOP, NOARGS, 2, "" },
1261 /* From RFC 2228, in order defined */
1262 { "AUTH", AUTH, STR1, 1, "<sp> mechanism-name" },
1263 { "ADAT", ADAT, STR1, 1, "<sp> base-64-data" },
1264 { "PROT", PROT, STR1, 1, "<sp> prot-code" },
1265 { "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer" },
1266 { "CCC", CCC, NOARGS, 1, "(Disable data protection)" },
1267 { "MIC", MIC, STR1, 4, "<sp> base64data" },
1268 { "CONF", CONF, STR1, 4, "<sp> base64data" },
1269 { "ENC", ENC, STR1, 4, "<sp> base64data" },
1271 /* From RFC 2389, in order defined */
1272 { "FEAT", FEAT, NOARGS, 1, "(display extended features)" },
1273 { "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]" },
1275 /* from draft-ietf-ftpext-mlst-11 */
1276 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1277 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1278 { "MLST", MLST, OSTR, 2, "[ <sp> path-name ]" },
1279 { "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]" },
1281 /* obsolete commands */
1282 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1283 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1284 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1285 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1286 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1287 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1288 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1289 { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)" },
1290 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1291 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1292 { "XPWD", PWD, NOARGS, 1, "(return current directory)" },
1293 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1295 { NULL, 0, 0, 0, 0 }
1298 struct tab sitetab[] = {
1299 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1300 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1301 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1302 { "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]" },
1303 { "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]" },
1304 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1305 { NULL, 0, 0, 0, NULL }
1309 * Check if a filename is allowed to be modified (isupload == 0) or
1310 * uploaded (isupload == 1), and if necessary, check the filename is `sane'.
1311 * If the filename is NULL, fail.
1312 * If the filename is "", don't do the sane name check.
1315 check_write(const char *file, int isupload)
1320 reply(530, "Please login with USER and PASS.");
1323 /* checking modify */
1324 if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) {
1325 reply(502, "No permission to use this command.");
1328 /* checking upload */
1329 if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) {
1330 reply(502, "No permission to use this command.");
1334 /* checking sanenames */
1335 if (file[0] != '\0' && CURCLASS_FLAGS_ISSET(sanenames)) {
1340 for (p = file; *p; p++) {
1341 if (isalnum((unsigned char)*p) || *p == '-' || *p == '+' ||
1342 *p == ',' || *p == '.' || *p == '_')
1345 reply(553, "File name `%s' not allowed.", file);
1353 lookup(struct tab *p, const char *cmd)
1356 for (; p->name != NULL; p++)
1357 if (strcasecmp(cmd, p->name) == 0)
1362 #include <arpa/telnet.h>
1365 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1366 * `s' is the buffer to read into.
1367 * `n' is the 1 less than the size of the buffer, to allow trailing NUL
1368 * `iop' is the FILE to read from.
1369 * Returns 0 on success, -1 on EOF, -2 if the command was too long.
1372 getline(char *s, int n, FILE *iop)
1378 /* tmpline may contain saved command from urgent mode interruption */
1379 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1381 if (tmpline[c] == '\n') {
1384 syslog(LOG_DEBUG, "command: %s", s);
1391 while ((c = getc(iop)) != EOF) {
1396 if ((c = getc(iop)) != EOF) {
1406 cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c);
1407 (void) fflush(stdout);
1414 cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c);
1415 (void) fflush(stdout);
1420 continue; /* ignore command */
1427 * If command doesn't fit into buffer, discard the
1428 * rest of the command and indicate truncation.
1429 * This prevents the command to be split up into
1430 * multiple commands.
1434 "command too long, last char: %d", c);
1435 while (c != '\n' && (c = getc(iop)) != EOF)
1442 if (c == EOF && cs == s)
1446 if ((curclass.type != CLASS_GUEST &&
1447 strncasecmp(s, "PASS ", 5) == 0) ||
1448 strncasecmp(s, "ACCT ", 5) == 0) {
1449 /* Don't syslog passwords */
1450 syslog(LOG_DEBUG, "command: %.4s ???", s);
1455 /* Don't syslog trailing CR-LF */
1458 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1462 syslog(LOG_DEBUG, "command: %.*s", len, s);
1469 ftp_handle_line(char *cp)
1482 (void) alarm(curclass.timeout);
1483 ret = getline(cbuf, sizeof(cbuf)-1, stdin);
1486 reply(221, "You could at least say goodbye.");
1488 } else if (ret == -2) {
1489 reply(500, "Command too long.");
1491 ftp_handle_line(cbuf);
1500 static int cpos, state;
1510 if ((cp = strchr(cmdp, '\r'))) {
1512 #if HAVE_SETPROCTITLE
1513 if (strncasecmp(cmdp, "PASS", 4) != 0 &&
1514 strncasecmp(cmdp, "ACCT", 4) != 0)
1515 setproctitle("%s: %s", proctitle, cmdp);
1516 #endif /* HAVE_SETPROCTITLE */
1520 if ((cp = strpbrk(cmdp, " \n")))
1526 p = lookup(cmdtab, cmdp);
1529 if (is_oob && ! CMD_OOB(p)) {
1530 /* command will be handled in-band */
1532 } else if (! CMD_IMPLEMENTED(p)) {
1533 reply(502, "%s command not implemented.",
1545 if (cmdp[cpos] == ' ') {
1550 if ((cp2 = strpbrk(cp, " \n")))
1554 p = lookup(sitetab, cp);
1557 if (!CMD_IMPLEMENTED(p)) {
1558 reply(502, "SITE %s command not implemented.",
1570 if (cmdp[cpos] == '\n') {
1579 if (cmdp[cpos] == ' ') {
1581 state = state == OSTR ? STR2 : state+1;
1587 if (cmdp[cpos] == '\n') {
1598 * Make sure the string is nonempty and \n terminated.
1600 if (n > 1 && cmdp[cpos] == '\n') {
1602 yylval.s = ftpd_strdup(cp);
1610 if (cmdp[cpos] == ' ') {
1614 if (isdigit((unsigned char)cmdp[cpos])) {
1616 while (isdigit((unsigned char)cmdp[++cpos]))
1620 yylval.u.i = atoi(cp);
1629 if (isdigit((unsigned char)cmdp[cpos])) {
1631 while (isdigit((unsigned char)cmdp[++cpos]))
1635 yylval.u.i = atoi(cp);
1636 yylval.u.ll = STRTOLL(cp, (char **)NULL, 10);
1640 if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0
1641 && !isalnum((unsigned char)cmdp[cpos + 3])) {
1645 switch (cmdp[cpos++]) {
1709 if (cmdp[cpos] == '\n') {
1715 reply(501, "'%s' command does not take any arguments.", cmdp);
1725 fatal("Unknown state in scanner.");
1738 if (hasyyerrored || is_oob)
1740 if ((cp = strchr(cmdp,'\n')) != NULL)
1742 reply(500, "'%s': command not understood.", cmdp);
1747 help(struct tab *ctab, const char *s)
1753 if (ctab == sitetab)
1757 width = 0, NCMDS = 0;
1758 for (c = ctab; c->name != NULL; c++) {
1759 int len = strlen(c->name);
1765 width = (width + 8) &~ 7;
1770 reply(-214, "%s", "");
1771 reply(0, "The following %scommands are recognized.", htype);
1772 reply(0, "(`-' = not implemented, `+' = supports options)");
1773 columns = 76 / width;
1776 lines = (NCMDS + columns - 1) / columns;
1777 for (i = 0; i < lines; i++) {
1778 cprintf(stdout, " ");
1779 for (j = 0; j < columns; j++) {
1780 c = ctab + j * lines + i;
1781 cprintf(stdout, "%s", c->name);
1782 w = strlen(c->name);
1783 if (! CMD_IMPLEMENTED(c)) {
1787 if (CMD_HAS_OPTIONS(c)) {
1791 if (c + lines >= &ctab[NCMDS])
1798 cprintf(stdout, "\r\n");
1800 (void) fflush(stdout);
1801 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1804 c = lookup(ctab, s);
1805 if (c == (struct tab *)0) {
1806 reply(502, "Unknown command '%s'.", s);
1809 if (CMD_IMPLEMENTED(c))
1810 reply(214, "Syntax: %s%s %s", htype, c->name, c->help);
1812 reply(504, "%s%-*s\t%s; not implemented.", htype, width,
1817 * Check that the structures used for a PORT, LPRT or EPRT command are
1818 * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks.
1819 * If family != -1 check that his_addr.su_family == family.
1822 port_check(const char *cmd, int family)
1824 char h1[NI_MAXHOST], h2[NI_MAXHOST];
1825 char s1[NI_MAXHOST], s2[NI_MAXHOST];
1826 #ifdef NI_WITHSCOPEID
1827 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
1829 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
1833 reply(501, "%s disallowed after EPSV ALL", cmd);
1837 if (family != -1 && his_addr.su_family != family) {
1839 reply(500, "Illegal %s command rejected", cmd);
1843 if (data_dest.su_family != his_addr.su_family)
1844 goto port_check_fail;
1846 /* be paranoid, if told so */
1847 if (CURCLASS_FLAGS_ISSET(checkportcmd)) {
1850 * be paranoid, there are getnameinfo implementation that does
1851 * not present scopeid portion
1853 if (data_dest.su_family == AF_INET6 &&
1854 data_dest.su_scope_id != his_addr.su_scope_id)
1855 goto port_check_fail;
1858 if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len,
1859 h1, sizeof(h1), s1, sizeof(s1), niflags))
1860 goto port_check_fail;
1861 if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
1862 h2, sizeof(h2), s2, sizeof(s2), niflags))
1863 goto port_check_fail;
1865 if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0)
1866 goto port_check_fail;
1871 (void) close(pdata);
1874 reply(200, "%s command successful.", cmd);