1 /* $NetBSD: ftpcmd.y,v 1.89 2009/03/15 07:48:36 lukem Exp $ */
4 * Copyright (c) 1997-2009 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.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * Copyright (c) 1985, 1988, 1993, 1994
34 * The Regents of the University of California. All rights reserved.
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. Neither the name of the University nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
64 * Grammar for FTP commands.
69 #include <sys/cdefs.h>
73 static char sccsid
[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94";
75 __RCSID
("$NetBSD: ftpcmd.y,v 1.89 2009/03/15 07:48:36 lukem Exp $");
79 #include <sys/param.h>
80 #include <sys/socket.h>
83 #include <netinet/in.h>
85 #include <arpa/inet.h>
100 #include <krb5/krb5.h>
108 static int cmd_bytesz
;
110 char cbuf
[FTP_BUFLEN
];
115 struct tab sitetab
[];
117 static int check_write
(const char *, int);
118 static void help
(struct tab
*, const char *);
119 static void port_check
(const char *, int);
139 USER PASS ACCT CWD CDUP SMNT
140 QUIT REIN PORT PASV TYPE STRU
141 MODE RETR STOR STOU APPE ALLO
142 REST RNFR RNTO ABOR DELE RMD
143 MKD PWD LIST NLST SITE SYST
146 AUTH ADAT PROT PBSZ CCC MIC
155 MAIL MLFL MRCP MRSQ MSAM MSND
158 CHMOD IDLE RATEGET RATEPUT UMASK
165 %type
<u.i
> check_login octal_number byte_size
166 %type
<u.i
> struct_code mode_code type_code form_code decimal_integer
167 %type
<s
> pathstring pathname password username
168 %type
<s
> mechanism_name base64data prot_code
177 REASSIGN
(fromname
, NULL
);
178 restart_point
= (off_t
) 0;
187 : USER SP username CRLF
193 | PASS SP password CRLF
196 memset
($3, 0, strlen
($3));
200 | CWD check_login CRLF
206 | CWD check_login SP pathname CRLF
208 if
($2 && $4 != NULL
)
214 | CDUP check_login CRLF
223 reply
(-221, "%s", "");
225 "Data traffic for this session was " LLF
" byte%s in " LLF
" file%s.",
226 (LLT
)total_data
, PLURAL
(total_data
),
227 (LLT
)total_files
, PLURAL
(total_files
));
229 "Total traffic for this session was " LLF
" byte%s in " LLF
" transfer%s.",
230 (LLT
)total_bytes
, PLURAL
(total_bytes
),
231 (LLT
)total_xfers
, PLURAL
(total_xfers
));
234 "Thank you for using the FTP service on %s.",
236 if
(logged_in
&& logging
) {
238 "Data traffic: " LLF
" byte%s in " LLF
" file%s",
239 (LLT
)total_data
, PLURAL
(total_data
),
240 (LLT
)total_files
, PLURAL
(total_files
));
242 "Total traffic: " LLF
" byte%s in " LLF
" transfer%s",
243 (LLT
)total_bytes
, PLURAL
(total_bytes
),
244 (LLT
)total_xfers
, PLURAL
(total_xfers
));
250 | PORT check_login SP host_port CRLF
253 port_check
("PORT", AF_INET
);
256 | LPRT check_login SP host_long_port4 CRLF
259 port_check
("LPRT", AF_INET
);
262 | LPRT check_login SP host_long_port6 CRLF
266 port_check
("LPRT", AF_INET6
);
268 reply
(500, "IPv6 support not available.");
272 | EPRT check_login SP STRING CRLF
275 if
(extended_port
($4) == 0)
276 port_check
("EPRT", -1);
281 | PASV check_login CRLF
284 if
(CURCLASS_FLAGS_ISSET
(passive
))
287 reply
(500, "PASV mode not available.");
291 | LPSV check_login CRLF
294 if
(CURCLASS_FLAGS_ISSET
(passive
)) {
297 "LPSV disallowed after EPSV ALL");
299 long_passive
("LPSV", PF_UNSPEC
);
301 reply
(500, "LPSV mode not available.");
305 | EPSV check_login SP NUMBER CRLF
308 if
(CURCLASS_FLAGS_ISSET
(passive
))
312 reply
(500, "EPSV mode not available.");
316 | EPSV check_login SP ALL CRLF
319 if
(CURCLASS_FLAGS_ISSET
(passive
)) {
321 "EPSV ALL command successful.");
324 reply
(500, "EPSV mode not available.");
328 | EPSV check_login CRLF
331 if
(CURCLASS_FLAGS_ISSET
(passive
))
332 long_passive
("EPSV", PF_UNSPEC
);
334 reply
(500, "EPSV mode not available.");
338 | TYPE check_login SP type_code CRLF
345 if
(cmd_form
== FORM_N
) {
346 reply
(200, "Type set to A.");
350 reply
(504, "Form must be N.");
354 reply
(504, "Type E not implemented.");
358 reply
(200, "Type set to I.");
364 if
(cmd_bytesz
== 8) {
366 "Type set to L (byte size 8).");
369 reply
(504, "Byte size must be 8.");
370 #else /* NBBY == 8 */
371 UNIMPLEMENTED for NBBY
!= 8
372 #endif /* NBBY == 8 */
378 | STRU check_login SP struct_code CRLF
384 reply
(200, "STRU F ok.");
388 reply
(504, "Unimplemented STRU type.");
393 | MODE check_login SP mode_code CRLF
399 reply
(200, "MODE S ok.");
403 reply
(502, "Unimplemented MODE type.");
408 | RETR check_login SP pathname CRLF
410 if
($2 && $4 != NULL
)
416 | STOR SP pathname CRLF
418 if
(check_write
($3, 1))
424 | STOU SP pathname CRLF
426 if
(check_write
($3, 1))
432 | APPE SP pathname CRLF
434 if
(check_write
($3, 1))
440 | ALLO check_login SP NUMBER CRLF
443 reply
(202, "ALLO command ignored.");
446 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
449 reply
(202, "ALLO command ignored.");
452 | RNTO SP pathname CRLF
454 if
(check_write
($3, 0)) {
456 renamecmd
(fromname
, $3);
457 REASSIGN
(fromname
, NULL
);
459 reply
(503, "Bad sequence of commands.");
466 | ABOR check_login CRLF
471 reply
(225, "ABOR command successful.");
474 | DELE SP pathname CRLF
476 if
(check_write
($3, 0))
482 | RMD SP pathname CRLF
484 if
(check_write
($3, 0))
490 | MKD SP pathname CRLF
492 if
(check_write
($3, 0))
498 | PWD check_login CRLF
504 | LIST check_login CRLF
506 const char *argv
[] = { INTERNAL_LS
, "-lgA", NULL
};
508 if
(CURCLASS_FLAGS_ISSET
(hidesymlinks
))
514 | LIST check_login SP pathname CRLF
516 const char *argv
[] = { INTERNAL_LS
, "-lgA", NULL
, NULL
};
518 if
(CURCLASS_FLAGS_ISSET
(hidesymlinks
))
520 if
($2 && $4 != NULL
) {
528 | NLST check_login CRLF
534 | NLST check_login SP pathname CRLF
546 | SITE SP CHMOD SP octal_number SP pathname CRLF
548 if
(check_write
($7, 0)) {
549 if
(($5 == -1) ||
($5 > 0777))
551 "CHMOD: Mode value must be between 0 and 0777");
552 else if
(chmod
($7, $5) < 0)
553 perror_reply
(550, $7);
555 reply
(200, "CHMOD command successful.");
561 | SITE SP HELP SP STRING CRLF
567 | SITE SP IDLE check_login CRLF
571 "Current IDLE time limit is " LLF
572 " seconds; max " LLF
,
573 (LLT
)curclass.timeout
,
574 (LLT
)curclass.maxtimeout
);
578 | SITE SP IDLE check_login SP NUMBER CRLF
581 if
($6.i
< 30 ||
$6.i
> curclass.maxtimeout
) {
583 "IDLE time limit must be between 30 and "
585 (LLT
)curclass.maxtimeout
);
587 curclass.timeout
= $6.i
;
588 (void) alarm
(curclass.timeout
);
590 "IDLE time limit set to "
592 (LLT
)curclass.timeout
);
597 | SITE SP RATEGET check_login CRLF
601 "Current RATEGET is " LLF
" bytes/sec",
602 (LLT
)curclass.rateget
);
606 | SITE SP RATEGET check_login SP STRING CRLF
613 rate
= strsuftollx
("RATEGET", p
, 0,
615 ? curclass.maxrateget
616 : LLTMAX
, errbuf
, sizeof
(errbuf
));
618 reply
(501, "%s", errbuf
);
620 curclass.rateget
= rate
;
622 "RATEGET set to " LLF
" bytes/sec",
623 (LLT
)curclass.rateget
);
629 | SITE SP RATEPUT check_login CRLF
633 "Current RATEPUT is " LLF
" bytes/sec",
634 (LLT
)curclass.rateput
);
638 | SITE SP RATEPUT check_login SP STRING CRLF
645 rate
= strsuftollx
("RATEPUT", p
, 0,
647 ? curclass.maxrateput
648 : LLTMAX
, errbuf
, sizeof
(errbuf
));
650 reply
(501, "%s", errbuf
);
652 curclass.rateput
= rate
;
654 "RATEPUT set to " LLF
" bytes/sec",
655 (LLT
)curclass.rateput
);
661 | SITE SP UMASK check_login CRLF
667 (void) umask
(oldmask
);
668 reply
(200, "Current UMASK is %03o", oldmask
);
672 | SITE SP UMASK check_login SP octal_number CRLF
676 if
($4 && check_write
("", 0)) {
677 if
(($6 == -1) ||
($6 > 0777)) {
678 reply
(501, "Bad UMASK value");
682 "UMASK set to %03o (was %03o)",
690 if
(EMPTYSTR
(version
))
691 reply
(215, "UNIX Type: L%d", NBBY
);
693 reply
(215, "UNIX Type: L%d Version: %s", NBBY
,
697 | STAT check_login SP pathname CRLF
699 if
($2 && $4 != NULL
)
718 | HELP SP STRING CRLF
722 if
(strncasecmp
(cp
, "SITE", 4) == 0) {
737 reply
(200, "NOOP command successful.");
741 | AUTH SP mechanism_name CRLF
743 reply
(502, "RFC 2228 authentication not implemented.");
747 | ADAT SP base64data CRLF
750 "Please set authentication state with AUTH.");
754 | PROT SP prot_code CRLF
757 "Please set protection buffer size with PBSZ.");
761 | PBSZ SP decimal_integer CRLF
764 "Please set authentication state with AUTH.");
769 reply
(533, "No protection enabled.");
772 | MIC SP base64data CRLF
774 reply
(502, "RFC 2228 authentication not implemented.");
778 | CONF SP base64data CRLF
780 reply
(502, "RFC 2228 authentication not implemented.");
784 | ENC SP base64data CRLF
786 reply
(502, "RFC 2228 authentication not implemented.");
797 | OPTS SP STRING CRLF
808 * Return size of file in a format suitable for
809 * using with RESTART (we just count bytes).
811 | SIZE check_login SP pathname CRLF
813 if
($2 && $4 != NULL
)
820 * Return modification time of file as an ISO 3307
821 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
822 * where xxx is the fractional second (of any precision,
823 * not necessarily 3 digits)
825 | MDTM check_login SP pathname CRLF
827 if
($2 && $4 != NULL
) {
829 if
(stat
($4, &stbuf
) < 0)
830 perror_reply
(550, $4);
831 else if
(!S_ISREG
(stbuf.st_mode
)) {
832 reply
(550, "%s: not a plain file.", $4);
836 t
= gmtime
(&stbuf.st_mtime
);
838 "%04d%02d%02d%02d%02d%02d",
839 TM_YEAR_BASE
+ t
->tm_year
,
840 t
->tm_mon
+1, t
->tm_mday
,
841 t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
848 | MLST check_login SP pathname CRLF
850 if
($2 && $4 != NULL
)
856 | MLST check_login CRLF
861 | MLSD check_login SP pathname CRLF
863 if
($2 && $4 != NULL
)
869 | MLSD check_login CRLF
881 : REST check_login SP NUMBER CRLF
884 REASSIGN
(fromname
, NULL
);
885 restart_point
= (off_t
)$4.ll
;
887 "Restarting at " LLF
". Send STORE or RETRIEVE to initiate transfer.",
892 | RNFR SP pathname CRLF
894 restart_point
= (off_t
) 0;
895 if
(check_write
($3, 0)) {
896 REASSIGN
(fromname
, NULL
);
897 fromname
= renamefrom
($3);
911 $$
= (char *)calloc
(1, sizeof
(char));
925 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
930 memset
(&data_dest
, 0, sizeof
(data_dest
));
931 data_dest.su_len
= sizeof
(struct sockaddr_in
);
932 data_dest.su_family
= AF_INET
;
933 p
= (char *)&data_dest.su_port
;
934 p
[0] = $9.i
; p
[1] = $11.i
;
935 a
= (char *)&data_dest.su_addr
;
936 a
[0] = $1.i
; a
[1] = $3.i
; a
[2] = $5.i
; a
[3] = $7.i
;
941 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
942 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
947 memset
(&data_dest
, 0, sizeof
(data_dest
));
948 data_dest.su_len
= sizeof
(struct sockaddr_in
);
949 data_dest.su_family
= AF_INET
;
950 p
= (char *)&data_dest.su_port
;
951 p
[0] = $15.i
; p
[1] = $17.i
;
952 a
= (char *)&data_dest.su_addr
;
953 a
[0] = $5.i
; a
[1] = $7.i
; a
[2] = $9.i
; a
[3] = $11.i
;
955 /* reject invalid LPRT command */
956 if
($1.i
!= 4 ||
$3.i
!= 4 ||
$13.i
!= 2)
957 memset
(&data_dest
, 0, sizeof
(data_dest
));
962 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
963 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
964 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
965 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
966 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
972 memset
(&data_dest
, 0, sizeof
(data_dest
));
973 data_dest.su_len
= sizeof
(struct sockaddr_in6
);
974 data_dest.su_family
= AF_INET6
;
975 p
= (char *)&data_dest.su_port
;
976 p
[0] = $39.i
; p
[1] = $41.i
;
977 a
= (char *)&data_dest.si_su.su_sin6.sin6_addr
;
978 a
[0] = $5.i
; a
[1] = $7.i
; a
[2] = $9.i
; a
[3] = $11.i
;
979 a
[4] = $13.i
; a
[5] = $15.i
; a
[6] = $17.i
; a
[7] = $19.i
;
980 a
[8] = $21.i
; a
[9] = $23.i
; a
[10] = $25.i
; a
[11] = $27.i
;
981 a
[12] = $29.i
; a
[13] = $31.i
; a
[14] = $33.i
; a
[15] = $35.i
;
982 if
(his_addr.su_family
== AF_INET6
) {
983 /* XXX: more sanity checks! */
984 data_dest.su_scope_id
= his_addr.su_scope_id
;
987 memset
(&data_dest
, 0, sizeof
(data_dest
));
989 /* reject invalid LPRT command */
990 if
($1.i
!= 6 ||
$3.i
!= 16 ||
$37.i
!= 2)
991 memset
(&data_dest
, 0, sizeof
(data_dest
));
1054 /* this is for a bug in the BBN ftp */
1100 * Problem: this production is used for all pathname
1101 * processing, but only gives a 550 error reply.
1102 * This is a valid reply in some cases but not in
1105 if
(logged_in
&& $1 && *$1 == '~') {
1106 char *path
, *home
, *result
;
1109 path
= strchr
($1 + 1, '/');
1117 if
((hpw
= getpwnam
($1 + 1)) != NULL
)
1122 len
= strlen
(home
) + 1;
1124 len
+= strlen
(path
) + 1;
1125 if
((result
= malloc
(len
)) == NULL
)
1126 fatal
("Local resource failure: malloc");
1127 strlcpy
(result
, home
, len
);
1129 strlcat
(result
, "/", len
);
1130 strlcat
(result
, path
, len
);
1146 int ret
, dec
, multby
, digit
;
1149 * Convert a number that was read as decimal number
1150 * to what it would be if it had been read as octal.
1161 ret
+= digit
* multby
;
1194 reply
(530, "Please login with USER and PASS.");
1203 #define CMD 0 /* beginning of command */
1204 #define ARGS 1 /* expect miscellaneous arguments */
1205 #define STR1 2 /* expect SP followed by STRING */
1206 #define STR2 3 /* expect STRING */
1207 #define OSTR 4 /* optional SP then STRING */
1208 #define ZSTR1 5 /* SP then optional STRING */
1209 #define ZSTR2 6 /* optional STRING after SP */
1210 #define SITECMD 7 /* SITE command */
1211 #define NSTR 8 /* Number followed by a string */
1212 #define NOARGS 9 /* No arguments allowed */
1213 #define EOLN 10 /* End of line */
1215 struct tab cmdtab
[] = {
1216 /* From RFC 959, in order defined (5.3.1) */
1217 { "USER", USER
, STR1
, 1, "<sp> username", 0, },
1218 { "PASS", PASS
, ZSTR1
, 1, "<sp> password", 0, },
1219 { "ACCT", ACCT
, STR1
, 0, "(specify account)", 0, },
1220 { "CWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]", 0, },
1221 { "CDUP", CDUP
, NOARGS
, 1, "(change to parent directory)", 0, },
1222 { "SMNT", SMNT
, ARGS
, 0, "(structure mount)", 0, },
1223 { "QUIT", QUIT
, NOARGS
, 1, "(terminate service)", 0, },
1224 { "REIN", REIN
, NOARGS
, 0, "(reinitialize server state)", 0, },
1225 { "PORT", PORT
, ARGS
, 1, "<sp> b0, b1, b2, b3, b4, b5", 0, },
1226 { "LPRT", LPRT
, ARGS
, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2...", 0, },
1227 { "EPRT", EPRT
, STR1
, 1, "<sp> |af|addr|port|", 0, },
1228 { "PASV", PASV
, NOARGS
, 1, "(set server in passive mode)", 0, },
1229 { "LPSV", LPSV
, ARGS
, 1, "(set server in passive mode)", 0, },
1230 { "EPSV", EPSV
, ARGS
, 1, "[<sp> af|ALL]", 0, },
1231 { "TYPE", TYPE
, ARGS
, 1, "<sp> [ A | E | I | L ]", 0, },
1232 { "STRU", STRU
, ARGS
, 1, "(specify file structure)", 0, },
1233 { "MODE", MODE
, ARGS
, 1, "(specify transfer mode)", 0, },
1234 { "RETR", RETR
, STR1
, 1, "<sp> file-name", 0, },
1235 { "STOR", STOR
, STR1
, 1, "<sp> file-name", 0, },
1236 { "STOU", STOU
, STR1
, 1, "<sp> file-name", 0, },
1237 { "APPE", APPE
, STR1
, 1, "<sp> file-name", 0, },
1238 { "ALLO", ALLO
, ARGS
, 1, "allocate storage (vacuously)", 0, },
1239 { "REST", REST
, ARGS
, 1, "<sp> offset (restart command)", 0, },
1240 { "RNFR", RNFR
, STR1
, 1, "<sp> file-name", 0, },
1241 { "RNTO", RNTO
, STR1
, 1, "<sp> file-name", 0, },
1242 { "ABOR", ABOR
, NOARGS
, 4, "(abort operation)", 0, },
1243 { "DELE", DELE
, STR1
, 1, "<sp> file-name", 0, },
1244 { "RMD", RMD
, STR1
, 1, "<sp> path-name", 0, },
1245 { "MKD", MKD
, STR1
, 1, "<sp> path-name", 0, },
1246 { "PWD", PWD
, NOARGS
, 1, "(return current directory)", 0, },
1247 { "LIST", LIST
, OSTR
, 1, "[ <sp> path-name ]", 0, },
1248 { "NLST", NLST
, OSTR
, 1, "[ <sp> path-name ]", 0, },
1249 { "SITE", SITE
, SITECMD
, 1, "site-cmd [ <sp> arguments ]", 0, },
1250 { "SYST", SYST
, NOARGS
, 1, "(get type of operating system)", 0, },
1251 { "STAT", STAT
, OSTR
, 4, "[ <sp> path-name ]", 0, },
1252 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]", 0, },
1253 { "NOOP", NOOP
, NOARGS
, 2, "", 0, },
1255 /* From RFC 2228, in order defined */
1256 { "AUTH", AUTH
, STR1
, 1, "<sp> mechanism-name", 0, },
1257 { "ADAT", ADAT
, STR1
, 1, "<sp> base-64-data", 0, },
1258 { "PROT", PROT
, STR1
, 1, "<sp> prot-code", 0, },
1259 { "PBSZ", PBSZ
, ARGS
, 1, "<sp> decimal-integer", 0, },
1260 { "CCC", CCC
, NOARGS
, 1, "(Disable data protection)", 0, },
1261 { "MIC", MIC
, STR1
, 4, "<sp> base64data", 0, },
1262 { "CONF", CONF
, STR1
, 4, "<sp> base64data", 0, },
1263 { "ENC", ENC
, STR1
, 4, "<sp> base64data", 0, },
1265 /* From RFC 2389, in order defined */
1266 { "FEAT", FEAT
, NOARGS
, 1, "(display extended features)", 0, },
1267 { "OPTS", OPTS
, STR1
, 1, "<sp> command [ <sp> options ]", 0, },
1269 /* From RFC 3659, in order defined */
1270 { "MDTM", MDTM
, OSTR
, 1, "<sp> path-name", 0, },
1271 { "SIZE", SIZE
, OSTR
, 1, "<sp> path-name", 0, },
1272 { "MLST", MLST
, OSTR
, 2, "[ <sp> path-name ]", 0, },
1273 { "MLSD", MLSD
, OSTR
, 1, "[ <sp> directory-name ]", 0, },
1275 /* obsolete commands */
1276 { "MAIL", MAIL
, OSTR
, 0, "(mail to user)", 0, },
1277 { "MLFL", MLFL
, OSTR
, 0, "(mail file)", 0, },
1278 { "MRCP", MRCP
, STR1
, 0, "(mail recipient)", 0, },
1279 { "MRSQ", MRSQ
, OSTR
, 0, "(mail recipient scheme question)", 0, },
1280 { "MSAM", MSAM
, OSTR
, 0, "(mail send to terminal and mailbox)", 0, },
1281 { "MSND", MSND
, OSTR
, 0, "(mail send to terminal)", 0, },
1282 { "MSOM", MSOM
, OSTR
, 0, "(mail send to terminal or mailbox)", 0, },
1283 { "XCUP", CDUP
, NOARGS
, 1, "(change to parent directory)", 0, },
1284 { "XCWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]", 0, },
1285 { "XMKD", MKD
, STR1
, 1, "<sp> path-name", 0, },
1286 { "XPWD", PWD
, NOARGS
, 1, "(return current directory)", 0, },
1287 { "XRMD", RMD
, STR1
, 1, "<sp> path-name", 0, },
1289 { NULL
, 0, 0, 0, 0, 0, }
1292 struct tab sitetab
[] = {
1293 { "CHMOD", CHMOD
, NSTR
, 1, "<sp> mode <sp> file-name", 0, },
1294 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]", 0, },
1295 { "IDLE", IDLE
, ARGS
, 1, "[ <sp> maximum-idle-time ]", 0, },
1296 { "RATEGET", RATEGET
,OSTR
, 1, "[ <sp> get-throttle-rate ]", 0, },
1297 { "RATEPUT", RATEPUT
,OSTR
, 1, "[ <sp> put-throttle-rate ]", 0, },
1298 { "UMASK", UMASK
, ARGS
, 1, "[ <sp> umask ]", 0, },
1299 { NULL
, 0, 0, 0, 0, 0, }
1303 * Check if a filename is allowed to be modified (isupload == 0) or
1304 * uploaded (isupload == 1), and if necessary, check the filename is `sane'.
1305 * If the filename is NULL, fail.
1306 * If the filename is "", don't do the sane name check.
1309 check_write
(const char *file
, int isupload
)
1314 reply
(530, "Please login with USER and PASS.");
1317 /* checking modify */
1318 if
(! isupload
&& ! CURCLASS_FLAGS_ISSET
(modify
)) {
1319 reply
(502, "No permission to use this command.");
1322 /* checking upload */
1323 if
(isupload
&& ! CURCLASS_FLAGS_ISSET
(upload
)) {
1324 reply
(502, "No permission to use this command.");
1328 /* checking sanenames */
1329 if
(file
[0] != '\0' && CURCLASS_FLAGS_ISSET
(sanenames
)) {
1334 for
(p
= file
; *p
; p
++) {
1335 if
(isalnum
((unsigned char)*p
) ||
*p
== '-' ||
*p
== '+' ||
1336 *p
== ',' ||
*p
== '.' ||
*p
== '_')
1339 reply
(553, "File name `%s' not allowed.", file
);
1347 lookup
(struct tab
*p
, const char *cmd
)
1350 for
(; p
->name
!= NULL
; p
++)
1351 if
(strcasecmp
(cmd
, p
->name
) == 0)
1356 #include <arpa/telnet.h>
1359 * get_line - a hacked up version of fgets to ignore TELNET escape codes.
1360 * `s' is the buffer to read into.
1361 * `n' is the 1 less than the size of the buffer, to allow trailing NUL
1362 * `iop' is the FILE to read from.
1363 * Returns 0 on success, -1 on EOF, -2 if the command was too long.
1366 get_line
(char *s
, int n
, FILE *iop
)
1372 /* tmpline may contain saved command from urgent mode interruption */
1373 for
(c
= 0; tmpline
[c
] != '\0' && --n
> 0; ++c
) {
1375 if
(tmpline
[c
] == '\n') {
1378 syslog
(LOG_DEBUG
, "command: %s", s
);
1385 while
((c
= getc
(iop
)) != EOF
) {
1390 if
((c
= getc
(iop
)) != EOF
) {
1400 cprintf
(stdout
, "%c%c%c", IAC
, DONT
, 0377&c
);
1401 (void) fflush
(stdout
);
1408 cprintf
(stdout
, "%c%c%c", IAC
, WONT
, 0377&c
);
1409 (void) fflush
(stdout
);
1414 continue
; /* ignore command */
1421 * If command doesn't fit into buffer, discard the
1422 * rest of the command and indicate truncation.
1423 * This prevents the command to be split up into
1424 * multiple commands.
1428 "command too long, last char: %d", c
);
1429 while
(c
!= '\n' && (c
= getc
(iop
)) != EOF
)
1436 if
(c
== EOF
&& cs
== s
)
1440 if
((curclass.type
!= CLASS_GUEST
&&
1441 strncasecmp
(s
, "PASS ", 5) == 0) ||
1442 strncasecmp
(s
, "ACCT ", 5) == 0) {
1443 /* Don't syslog passwords */
1444 syslog
(LOG_DEBUG
, "command: %.4s ???", s
);
1449 /* Don't syslog trailing CR-LF */
1452 while
(cp
>= s
&& (*cp
== '\n' ||
*cp
== '\r')) {
1456 syslog
(LOG_DEBUG
, "command: %.*s", len
, s
);
1463 ftp_handle_line
(char *cp
)
1476 (void) alarm
(curclass.timeout
);
1477 ret
= get_line
(cbuf
, sizeof
(cbuf
)-1, stdin
);
1480 reply
(221, "You could at least say goodbye.");
1482 } else if
(ret
== -2) {
1483 reply
(500, "Command too long.");
1485 ftp_handle_line
(cbuf
);
1494 static int cpos
, state
;
1504 if
((cp
= strchr
(cmdp
, '\r'))) {
1506 #if defined(HAVE_SETPROCTITLE)
1507 if
(strncasecmp
(cmdp
, "PASS", 4) != 0 &&
1508 strncasecmp
(cmdp
, "ACCT", 4) != 0)
1509 setproctitle
("%s: %s", proctitle
, cmdp
);
1510 #endif /* defined(HAVE_SETPROCTITLE) */
1514 if
((cp
= strpbrk
(cmdp
, " \n")))
1520 p
= lookup
(cmdtab
, cmdp
);
1523 if
(is_oob
&& ! CMD_OOB
(p
)) {
1524 /* command will be handled in-band */
1526 } else if
(! CMD_IMPLEMENTED
(p
)) {
1527 reply
(502, "%s command not implemented.",
1533 yylval.cs
= p
->name
;
1539 if
(cmdp
[cpos
] == ' ') {
1544 if
((cp2
= strpbrk
(cp
, " \n")))
1548 p
= lookup
(sitetab
, cp
);
1551 if
(!CMD_IMPLEMENTED
(p
)) {
1552 reply
(502, "SITE %s command not implemented.",
1558 yylval.cs
= p
->name
;
1564 if
(cmdp
[cpos
] == '\n') {
1573 if
(cmdp
[cpos
] == ' ') {
1575 state
= state
== OSTR ? STR2
: state
+1;
1581 if
(cmdp
[cpos
] == '\n') {
1592 * Make sure the string is nonempty and \n terminated.
1594 if
(n
> 1 && cmdp
[cpos
] == '\n') {
1596 yylval.s
= ftpd_strdup
(cp
);
1604 if
(cmdp
[cpos
] == ' ') {
1608 if
(isdigit
((unsigned char)cmdp
[cpos
])) {
1610 while
(isdigit
((unsigned char)cmdp
[++cpos
]))
1614 yylval.u.i
= atoi
(cp
);
1623 if
(isdigit
((unsigned char)cmdp
[cpos
])) {
1625 while
(isdigit
((unsigned char)cmdp
[++cpos
]))
1629 yylval.u.i
= atoi
(cp
);
1630 yylval.u.ll
= STRTOLL
(cp
, (char **)NULL
, 10);
1634 if
(strncasecmp
(&cmdp
[cpos
], "ALL", 3) == 0
1635 && !isalnum
((unsigned char)cmdp
[cpos
+ 3])) {
1639 switch
(cmdp
[cpos
++]) {
1703 if
(cmdp
[cpos
] == '\n') {
1709 reply
(501, "'%s' command does not take any arguments.", cmdp
);
1719 fatal
("Unknown state in scanner.");
1728 yyerror(const char *s
)
1732 if
(hasyyerrored || is_oob
)
1734 if
((cp
= strchr
(cmdp
,'\n')) != NULL
)
1736 reply
(500, "'%s': command not understood.", cmdp
);
1741 help
(struct tab
*ctab
, const char *s
)
1747 if
(ctab
== sitetab
)
1751 width
= 0, NCMDS
= 0;
1752 for
(c
= ctab
; c
->name
!= NULL
; c
++) {
1753 int len
= strlen
(c
->name
);
1759 width
= (width
+ 8) &~
7;
1764 reply
(-214, "%s", "");
1765 reply
(0, "The following %scommands are recognized.", htype
);
1766 reply
(0, "(`-' = not implemented, `+' = supports options)");
1767 columns
= 76 / width
;
1770 lines
= (NCMDS
+ columns
- 1) / columns
;
1771 for
(i
= 0; i
< lines
; i
++) {
1772 cprintf
(stdout
, " ");
1773 for
(j
= 0; j
< columns
; j
++) {
1774 c
= ctab
+ j
* lines
+ i
;
1775 cprintf
(stdout
, "%s", c
->name
);
1776 w
= strlen
(c
->name
);
1777 if
(! CMD_IMPLEMENTED
(c
)) {
1781 if
(CMD_HAS_OPTIONS
(c
)) {
1785 if
(c
+ lines
>= &ctab
[NCMDS
])
1792 cprintf
(stdout
, "\r\n");
1794 (void) fflush
(stdout
);
1795 reply
(214, "Direct comments to ftp-bugs@%s.", hostname
);
1798 c
= lookup
(ctab
, s
);
1799 if
(c
== (struct tab
*)0) {
1800 reply
(502, "Unknown command '%s'.", s
);
1803 if
(CMD_IMPLEMENTED
(c
))
1804 reply
(214, "Syntax: %s%s %s", htype
, c
->name
, c
->help
);
1806 reply
(504, "%s%-*s\t%s; not implemented.", htype
, width
,
1811 * Check that the structures used for a PORT, LPRT or EPRT command are
1812 * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks.
1813 * If family != -1 check that his_addr.su_family == family.
1816 port_check
(const char *cmd
, int family
)
1818 char h1
[NI_MAXHOST
], h2
[NI_MAXHOST
];
1819 char s1
[NI_MAXHOST
], s2
[NI_MAXHOST
];
1820 #ifdef NI_WITHSCOPEID
1821 const int niflags
= NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID
;
1823 const int niflags
= NI_NUMERICHOST | NI_NUMERICSERV
;
1827 reply
(501, "%s disallowed after EPSV ALL", cmd
);
1831 if
(family
!= -1 && his_addr.su_family
!= family
) {
1833 reply
(500, "Illegal %s command rejected", cmd
);
1837 if
(data_dest.su_family
!= his_addr.su_family
)
1838 goto port_check_fail
;
1840 /* be paranoid, if told so */
1841 if
(CURCLASS_FLAGS_ISSET
(checkportcmd
)) {
1844 * be paranoid, there are getnameinfo implementation that does
1845 * not present scopeid portion
1847 if
(data_dest.su_family
== AF_INET6
&&
1848 data_dest.su_scope_id
!= his_addr.su_scope_id
)
1849 goto port_check_fail
;
1852 if
(getnameinfo
((struct sockaddr
*)&data_dest
, data_dest.su_len
,
1853 h1
, sizeof
(h1
), s1
, sizeof
(s1
), niflags
))
1854 goto port_check_fail
;
1855 if
(getnameinfo
((struct sockaddr
*)&his_addr
, his_addr.su_len
,
1856 h2
, sizeof
(h2
), s2
, sizeof
(s2
), niflags
))
1857 goto port_check_fail
;
1859 if
(atoi
(s1
) < IPPORT_RESERVED || strcmp
(h1
, h2
) != 0)
1860 goto port_check_fail
;
1865 (void) close
(pdata
);
1868 reply
(200, "%s command successful.", cmd
);