2 * Copyright (c) 1985, 1988, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
30 * $FreeBSD: src/libexec/ftpd/ftpcmd.y,v 1.67 2008/12/23 01:23:09 cperciva Exp $
31 * $DragonFly: src/libexec/ftpd/ftpcmd.y,v 1.4 2004/06/19 20:36:04 joerg Exp $
35 * Grammar for FTP commands.
41 #include <sys/param.h>
42 #include <sys/socket.h>
45 #include <netinet/in.h>
66 #include "pathnames.h"
68 extern
union sockunion data_dest
, his_addr
;
71 extern
struct passwd
*pw
;
80 extern
int maxtimeout
;
82 extern
char *hostname
;
83 extern
char proctitle
[];
84 extern
int usedefault
;
85 extern
char tmpline
[];
87 extern
int assumeutf8
;
90 extern
int noguestretr
;
91 extern
char *typenames
[]; /* defined in <arpa/ftp.h> included from ftpd.c */
97 static int cmd_bytesz
;
100 char *fromname
= NULL
;
121 USER PASS ACCT REIN QUIT PORT
122 PASV TYPE STRU MODE RETR STOR
123 APPE MLFL MAIL MSND MSOM MSAM
124 MRSQ MRCP ALLO REST RNFR RNTO
125 ABOR DELE CWD LIST NLST SITE
126 STAT HELP NOOP MKD RMD PWD
127 CDUP STOU SMNT SYST SIZE MDTM
128 LPRT LPSV EPRT EPSV FEAT
130 UMASK IDLE CHMOD MDFIVE
137 %type
<u.i
> check_login octal_number byte_size
138 %type
<u.i
> check_login_ro check_login_epsv
139 %type
<u.i
> struct_code mode_code type_code form_code
140 %type
<s
> pathstring pathname password username
141 %type
<s
> ALL NOTIMPL
160 : USER SP username CRLF
165 | PASS SP password CRLF
174 | PORT check_login SP host_port CRLF
177 reply
(501, "No PORT allowed after EPSV ALL.");
182 if
(port_check
("PORT") == 1)
185 if
((his_addr.su_family
!= AF_INET6 ||
186 !IN6_IS_ADDR_V4MAPPED
(&his_addr.su_sin6.sin6_addr
))) {
187 /* shoud never happen */
189 reply
(500, "Invalid address rejected.");
192 port_check_v6
("pcmd");
197 | LPRT check_login SP host_long_port CRLF
200 reply
(501, "No LPRT allowed after EPSV ALL.");
205 if
(port_check
("LPRT") == 1)
208 if
(his_addr.su_family
!= AF_INET6
) {
210 reply
(500, "Invalid address rejected.");
213 if
(port_check_v6
("LPRT") == 1)
219 | EPRT check_login SP STRING CRLF
225 struct addrinfo hints
;
226 struct addrinfo
*res
;
230 reply
(501, "No EPRT allowed after EPSV ALL.");
236 memset
(&data_dest
, 0, sizeof
(data_dest
));
239 syslog
(LOG_DEBUG
, "%s", tmp
);
241 fatalerror
("not enough core");
247 memset
(result
, 0, sizeof
(result
));
248 for
(i
= 0; i
< 3; i
++) {
249 q
= strchr
(p
, delim
);
250 if
(!q ||
*q
!= delim
) {
253 "Invalid argument, rejected.");
262 syslog
(LOG_DEBUG
, "%d: %s", i
, p
);
266 /* some more sanity check */
281 memset
(&hints
, 0, sizeof
(hints
));
282 if
(atoi
(result
[0]) == 1)
283 hints.ai_family
= PF_INET
;
285 else if
(atoi
(result
[0]) == 2)
286 hints.ai_family
= PF_INET6
;
289 hints.ai_family
= PF_UNSPEC
; /*XXX*/
290 hints.ai_socktype
= SOCK_STREAM
;
291 i
= getaddrinfo
(result
[1], result
[2], &hints
, &res
);
294 memcpy
(&data_dest
, res
->ai_addr
, res
->ai_addrlen
);
296 if
(his_addr.su_family
== AF_INET6
297 && data_dest.su_family
== AF_INET6
) {
298 /* XXX more sanity checks! */
299 data_dest.su_sin6.sin6_scope_id
=
300 his_addr.su_sin6.sin6_scope_id
;
306 if
(port_check
("EPRT") == 1)
309 if
(his_addr.su_family
!= AF_INET6
) {
311 reply
(500, "Invalid address rejected.");
314 if
(port_check_v6
("EPRT") == 1)
320 | PASV check_login CRLF
323 reply
(501, "No PASV allowed after EPSV ALL.");
327 | LPSV check_login CRLF
330 reply
(501, "No LPSV allowed after EPSV ALL.");
332 long_passive
("LPSV", PF_UNSPEC
);
334 | EPSV check_login_epsv SP NUMBER CRLF
348 pf
= -1; /*junk value*/
351 long_passive
("EPSV", pf
);
354 | EPSV check_login_epsv SP ALL CRLF
357 reply
(200, "EPSV ALL command successful.");
361 | EPSV check_login_epsv CRLF
364 long_passive
("EPSV", PF_UNSPEC
);
366 | TYPE check_login SP type_code CRLF
372 if
(cmd_form
== FORM_N
) {
373 reply
(200, "Type set to A.");
377 reply
(504, "Form must be N.");
381 reply
(504, "Type E not implemented.");
385 reply
(200, "Type set to I.");
391 if
(cmd_bytesz
== 8) {
393 "Type set to L (byte size 8).");
396 reply
(504, "Byte size must be 8.");
397 #else /* CHAR_BIT == 8 */
398 UNIMPLEMENTED for CHAR_BIT
!= 8
399 #endif /* CHAR_BIT == 8 */
403 | STRU check_login SP struct_code CRLF
409 reply
(200, "STRU F accepted.");
413 reply
(504, "Unimplemented STRU type.");
417 | MODE check_login SP mode_code CRLF
423 reply
(200, "MODE S accepted.");
427 reply
(502, "Unimplemented MODE type.");
431 | ALLO check_login SP NUMBER CRLF
434 reply
(202, "ALLO command ignored.");
437 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
440 reply
(202, "ALLO command ignored.");
443 | RETR check_login SP pathname CRLF
445 if
(noretr ||
(guest
&& noguestretr
))
446 reply
(500, "RETR command disabled.");
447 else if
($2 && $4 != NULL
)
453 | STOR check_login_ro SP pathname CRLF
455 if
($2 && $4 != NULL
)
460 | APPE check_login_ro SP pathname CRLF
462 if
($2 && $4 != NULL
)
467 | NLST check_login CRLF
472 | NLST check_login SP pathstring CRLF
478 | LIST check_login CRLF
481 retrieve
(_PATH_LS
" -lgA", "");
483 | LIST check_login SP pathstring CRLF
486 retrieve
(_PATH_LS
" -lgA %s", $4);
489 | STAT check_login SP pathname CRLF
491 if
($2 && $4 != NULL
)
496 | STAT check_login CRLF
502 | DELE check_login_ro SP pathname CRLF
504 if
($2 && $4 != NULL
)
509 | RNTO check_login_ro SP pathname CRLF
511 if
($2 && $4 != NULL
) {
513 renamecmd
(fromname
, $4);
517 reply
(503, "Bad sequence of commands.");
523 | ABOR check_login CRLF
526 reply
(225, "ABOR command successful.");
528 | CWD check_login CRLF
534 | CWD check_login SP pathname CRLF
536 if
($2 && $4 != NULL
)
545 | HELP SP STRING CRLF
549 if
(strncasecmp
(cp
, "SITE", 4) == 0) {
563 reply
(200, "NOOP command successful.");
565 | MKD check_login_ro SP pathname CRLF
567 if
($2 && $4 != NULL
)
572 | RMD check_login_ro SP pathname CRLF
574 if
($2 && $4 != NULL
)
579 | PWD check_login CRLF
584 | CDUP check_login CRLF
593 | SITE SP HELP SP STRING CRLF
598 | SITE SP MDFIVE check_login SP pathname CRLF
605 reply
(200, "MD5(%s) = %s", $6, p
);
607 perror_reply
(550, $6);
612 | SITE SP UMASK check_login CRLF
619 reply
(200, "Current UMASK is %03o.", oldmask
);
622 | SITE SP UMASK check_login SP octal_number CRLF
627 if
(($6 == -1) ||
($6 > 0777)) {
628 reply
(501, "Bad UMASK value.");
632 "UMASK set to %03o (was %03o).",
637 | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
639 if
($4 && ($8 != NULL
)) {
640 if
(($6 == -1 ) ||
($6 > 0777))
641 reply
(501, "Bad mode value.");
642 else if
(chmod
($8, $6) < 0)
643 perror_reply
(550, $8);
645 reply
(200, "CHMOD command successful.");
650 | SITE SP check_login IDLE CRLF
654 "Current IDLE time limit is %d seconds; max %d.",
655 timeout
, maxtimeout
);
657 | SITE SP check_login IDLE SP NUMBER CRLF
660 if
($6.i
< 30 ||
$6.i
> maxtimeout
) {
662 "Maximum IDLE time must be between 30 and %d seconds.",
668 "Maximum IDLE time set to %d seconds.",
673 | STOU check_login_ro SP pathname CRLF
675 if
($2 && $4 != NULL
)
682 lreply
(211, "Extensions supported:");
684 /* XXX these two keywords are non-standard */
690 printf
(" REST STREAM\r\n");
693 /* TVFS requires UTF8, see RFC 3659 */
699 | SYST check_login CRLF
704 reply
(215, "UNIX Type: L%d Version: BSD-%d",
707 reply
(215, "UNIX Type: L%d", CHAR_BIT
);
710 reply
(215, "UNKNOWN Type: L%d", CHAR_BIT
);
715 * SIZE is not in RFC959, but Postel has blessed it and
716 * it will be in the updated RFC.
718 * Return size of file in a format suitable for
719 * using with RESTART (we just count bytes).
721 | SIZE check_login SP pathname CRLF
723 if
($2 && $4 != NULL
)
730 * MDTM is not in RFC959, but Postel has blessed it and
731 * it will be in the updated RFC.
733 * Return modification time of file as an ISO 3307
734 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
735 * where xxx is the fractional second (of any precision,
736 * not necessarily 3 digits)
738 | MDTM check_login SP pathname CRLF
740 if
($2 && $4 != NULL
) {
742 if
(stat
($4, &stbuf
) < 0)
743 perror_reply
(550, $4);
744 else if
(!S_ISREG
(stbuf.st_mode
)) {
745 reply
(550, "%s: not a plain file.", $4);
748 t
= gmtime
(&stbuf.st_mtime
);
750 "%04d%02d%02d%02d%02d%02d",
752 t
->tm_mon
+1, t
->tm_mday
,
753 t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
761 reply
(221, "Goodbye.");
770 yyclearin; /* discard lookahead data */
771 yyerrok; /* clear error condition */
772 state
= CMD
; /* reset lexer state */
776 : RNFR check_login_ro SP pathname CRLF
791 | REST check_login SP NUMBER CRLF
797 restart_point
= $4.o
;
798 reply
(350, "Restarting at %jd. %s",
799 (intmax_t)restart_point
,
800 "Send STORE or RETRIEVE to initiate transfer.");
812 $$
= (char *)calloc
(1, sizeof
(char));
825 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
830 data_dest.su_len
= sizeof
(struct sockaddr_in
);
831 data_dest.su_family
= AF_INET
;
832 p
= (char *)&data_dest.su_sin.sin_port
;
833 p
[0] = $9.i
; p
[1] = $11.i
;
834 a
= (char *)&data_dest.su_sin.sin_addr
;
835 a
[0] = $1.i
; a
[1] = $3.i
; a
[2] = $5.i
; a
[3] = $7.i
;
840 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
841 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
842 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
843 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
844 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
849 memset
(&data_dest
, 0, sizeof
(data_dest
));
850 data_dest.su_len
= sizeof
(struct sockaddr_in6
);
851 data_dest.su_family
= AF_INET6
;
852 p
= (char *)&data_dest.su_port
;
853 p
[0] = $39.i
; p
[1] = $41.i
;
854 a
= (char *)&data_dest.su_sin6.sin6_addr
;
855 a
[0] = $5.i
; a
[1] = $7.i
; a
[2] = $9.i
; a
[3] = $11.i
;
856 a
[4] = $13.i
; a
[5] = $15.i
; a
[6] = $17.i
; a
[7] = $19.i
;
857 a
[8] = $21.i
; a
[9] = $23.i
; a
[10] = $25.i
; a
[11] = $27.i
;
858 a
[12] = $29.i
; a
[13] = $31.i
; a
[14] = $33.i
; a
[15] = $35.i
;
859 if
(his_addr.su_family
== AF_INET6
) {
860 /* XXX more sanity checks! */
861 data_dest.su_sin6.sin6_scope_id
=
862 his_addr.su_sin6.sin6_scope_id
;
864 if
($1.i
!= 6 ||
$3.i
!= 16 ||
$37.i
!= 2)
865 memset
(&data_dest
, 0, sizeof
(data_dest
));
867 | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
868 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
873 memset
(&data_dest
, 0, sizeof
(data_dest
));
874 data_dest.su_sin.sin_len
= sizeof
(struct sockaddr_in
);
875 data_dest.su_family
= AF_INET
;
876 p
= (char *)&data_dest.su_port
;
877 p
[0] = $15.i
; p
[1] = $17.i
;
878 a
= (char *)&data_dest.su_sin.sin_addr
;
879 a
[0] = $5.i
; a
[1] = $7.i
; a
[2] = $9.i
; a
[3] = $11.i
;
880 if
($1.i
!= 4 ||
$3.i
!= 4 ||
$13.i
!= 2)
881 memset
(&data_dest
, 0, sizeof
(data_dest
));
928 cmd_bytesz
= CHAR_BIT
;
935 /* this is for a bug in the BBN ftp */
976 if
(logged_in
&& $1) {
980 * Expand ~user manually since glob(3)
981 * will return the unexpanded pathname
982 * if the corresponding file/directory
983 * doesn't exist yet. Using sole glob(3)
984 * would break natural commands like
989 if
((p
= exptilde
($1)) != NULL
) {
1007 int ret
, dec
, multby
, digit
;
1010 * Convert a number that was read as decimal number
1011 * to what it would be if it had been read as octal.
1022 ret
+= digit
* multby
;
1034 $$
= check_login1
();
1042 reply
(500, "EPSV command disabled.");
1046 $$
= check_login1
();
1054 reply
(550, "Permission denied.");
1058 $$
= check_login1
();
1064 #define CMD 0 /* beginning of command */
1065 #define ARGS 1 /* expect miscellaneous arguments */
1066 #define STR1 2 /* expect SP followed by STRING */
1067 #define STR2 3 /* expect STRING */
1068 #define OSTR 4 /* optional SP then STRING */
1069 #define ZSTR1 5 /* optional SP then optional STRING */
1070 #define ZSTR2 6 /* optional STRING after SP */
1071 #define SITECMD 7 /* SITE command */
1072 #define NSTR 8 /* Number followed by a string */
1074 #define MAXGLOBARGS 1000
1076 #define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */
1082 short implemented
; /* 1 if command is implemented */
1086 struct tab cmdtab
[] = { /* In order defined in RFC 765 */
1087 { "USER", USER
, STR1
, 1, "<sp> username" },
1088 { "PASS", PASS
, ZSTR1
, 1, "[<sp> [password]]" },
1089 { "ACCT", ACCT
, STR1
, 0, "(specify account)" },
1090 { "SMNT", SMNT
, ARGS
, 0, "(structure mount)" },
1091 { "REIN", REIN
, ARGS
, 0, "(reinitialize server state)" },
1092 { "QUIT", QUIT
, ARGS
, 1, "(terminate service)", },
1093 { "PORT", PORT
, ARGS
, 1, "<sp> b0, b1, b2, b3, b4, b5" },
1094 { "LPRT", LPRT
, ARGS
, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1095 { "EPRT", EPRT
, STR1
, 1, "<sp> |af|addr|port|" },
1096 { "PASV", PASV
, ARGS
, 1, "(set server in passive mode)" },
1097 { "LPSV", LPSV
, ARGS
, 1, "(set server in passive mode)" },
1098 { "EPSV", EPSV
, ARGS
, 1, "[<sp> af|ALL]" },
1099 { "TYPE", TYPE
, ARGS
, 1, "<sp> { A | E | I | L }" },
1100 { "STRU", STRU
, ARGS
, 1, "(specify file structure)" },
1101 { "MODE", MODE
, ARGS
, 1, "(specify transfer mode)" },
1102 { "RETR", RETR
, STR1
, 1, "<sp> file-name" },
1103 { "STOR", STOR
, STR1
, 1, "<sp> file-name" },
1104 { "APPE", APPE
, STR1
, 1, "<sp> file-name" },
1105 { "MLFL", MLFL
, OSTR
, 0, "(mail file)" },
1106 { "MAIL", MAIL
, OSTR
, 0, "(mail to user)" },
1107 { "MSND", MSND
, OSTR
, 0, "(mail send to terminal)" },
1108 { "MSOM", MSOM
, OSTR
, 0, "(mail send to terminal or mailbox)" },
1109 { "MSAM", MSAM
, OSTR
, 0, "(mail send to terminal and mailbox)" },
1110 { "MRSQ", MRSQ
, OSTR
, 0, "(mail recipient scheme question)" },
1111 { "MRCP", MRCP
, STR1
, 0, "(mail recipient)" },
1112 { "ALLO", ALLO
, ARGS
, 1, "allocate storage (vacuously)" },
1113 { "REST", REST
, ARGS
, 1, "<sp> offset (restart command)" },
1114 { "RNFR", RNFR
, STR1
, 1, "<sp> file-name" },
1115 { "RNTO", RNTO
, STR1
, 1, "<sp> file-name" },
1116 { "ABOR", ABOR
, ARGS
, 1, "(abort operation)" },
1117 { "DELE", DELE
, STR1
, 1, "<sp> file-name" },
1118 { "CWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
1119 { "XCWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
1120 { "LIST", LIST
, OSTR
, 1, "[ <sp> path-name ]" },
1121 { "NLST", NLST
, OSTR
, 1, "[ <sp> path-name ]" },
1122 { "SITE", SITE
, SITECMD
, 1, "site-cmd [ <sp> arguments ]" },
1123 { "SYST", SYST
, ARGS
, 1, "(get type of operating system)" },
1124 { "FEAT", FEAT
, ARGS
, 1, "(get extended features)" },
1125 { "STAT", STAT
, OSTR
, 1, "[ <sp> path-name ]" },
1126 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
1127 { "NOOP", NOOP
, ARGS
, 1, "" },
1128 { "MKD", MKD
, STR1
, 1, "<sp> path-name" },
1129 { "XMKD", MKD
, STR1
, 1, "<sp> path-name" },
1130 { "RMD", RMD
, STR1
, 1, "<sp> path-name" },
1131 { "XRMD", RMD
, STR1
, 1, "<sp> path-name" },
1132 { "PWD", PWD
, ARGS
, 1, "(return current directory)" },
1133 { "XPWD", PWD
, ARGS
, 1, "(return current directory)" },
1134 { "CDUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
1135 { "XCUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
1136 { "STOU", STOU
, STR1
, 1, "<sp> file-name" },
1137 { "SIZE", SIZE
, OSTR
, 1, "<sp> path-name" },
1138 { "MDTM", MDTM
, OSTR
, 1, "<sp> path-name" },
1139 { NULL
, 0, 0, 0, 0 }
1142 struct tab sitetab
[] = {
1143 { "MD5", MDFIVE
, STR1
, 1, "[ <sp> file-name ]" },
1144 { "UMASK", UMASK
, ARGS
, 1, "[ <sp> umask ]" },
1145 { "IDLE", IDLE
, ARGS
, 1, "[ <sp> maximum-idle-time ]" },
1146 { "CHMOD", CHMOD
, NSTR
, 1, "<sp> mode <sp> file-name" },
1147 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
1148 { NULL
, 0, 0, 0, 0 }
1151 static char *copy
(char *);
1152 static char *expglob
(char *);
1153 static char *exptilde
(char *);
1154 static void help
(struct tab
*, char *);
1156 lookup
(struct tab
*, char *);
1157 static int port_check
(const char *);
1159 static int port_check_v6
(const char *);
1161 static void sizecmd
(char *);
1162 static void toolong
(int);
1164 static void v4map_data_dest
(void);
1166 static int yylex(void);
1169 lookup
(struct tab
*p
, char *cmd
)
1172 for
(; p
->name
!= NULL
; p
++)
1173 if
(strcmp
(cmd
, p
->name
) == 0)
1178 #include <arpa/telnet.h>
1181 * get_line - a hacked up version of fgets to ignore TELNET escape codes.
1184 get_line
(char *s
, int n
, FILE *iop
)
1188 sigset_t sset
, osset
;
1191 /* tmpline may contain saved command from urgent mode interruption */
1192 for
(c
= 0; tmpline
[c
] != '\0' && --n
> 0; ++c
) {
1194 if
(tmpline
[c
] == '\n') {
1197 syslog
(LOG_DEBUG
, "command: %s", s
);
1204 /* SIGURG would interrupt stdio if not blocked during the read loop */
1206 sigaddset
(&sset
, SIGURG
);
1207 sigprocmask
(SIG_BLOCK
, &sset
, &osset
);
1208 while
((c
= getc
(iop
)) != EOF
) {
1211 if
((c
= getc
(iop
)) == EOF
)
1217 if
((c
= getc
(iop
)) == EOF
)
1219 printf
("%c%c%c", IAC
, DONT
, 0377&c
);
1224 if
((c
= getc
(iop
)) == EOF
)
1226 printf
("%c%c%c", IAC
, WONT
, 0377&c
);
1232 continue
; /* ignore command */
1238 * If command doesn't fit into buffer, discard the
1239 * rest of the command and indicate truncation.
1240 * This prevents the command to be split up into
1241 * multiple commands.
1243 while
(c
!= '\n' && (c
= getc
(iop
)) != EOF
)
1251 sigprocmask
(SIG_SETMASK
, &osset
, NULL
);
1252 if
(c
== EOF
&& cs
== s
)
1256 if
(!guest
&& strncasecmp
("pass ", s
, 5) == 0) {
1257 /* Don't syslog passwords */
1258 syslog
(LOG_DEBUG
, "command: %.5s ???", s
);
1263 /* Don't syslog trailing CR-LF */
1266 while
(cp
>= s
&& (*cp
== '\n' ||
*cp
== '\r')) {
1270 syslog
(LOG_DEBUG
, "command: %.*s", len
, s
);
1281 "Timeout (%d seconds): closing control connection.", timeout
);
1283 syslog
(LOG_INFO
, "User %s timed out after %d seconds",
1284 (pw ? pw
-> pw_name
: "unknown"), timeout
);
1301 signal
(SIGALRM
, toolong
);
1303 n
= get_line
(cbuf
, sizeof
(cbuf
)-1, stdin
);
1305 reply
(221, "You could at least say goodbye.");
1307 } else if
(n
== -2) {
1308 reply
(500, "Command too long.");
1314 if
(strncasecmp
(cbuf
, "PASS", 4) != 0)
1315 setproctitle
("%s: %s", proctitle
, cbuf
);
1316 #endif /* SETPROCTITLE */
1317 if
((cp
= strchr
(cbuf
, '\r'))) {
1321 if
((cp
= strpbrk
(cbuf
, " \n")))
1328 p
= lookup
(cmdtab
, cbuf
);
1332 if
(!p
->implemented
)
1333 return
(NOTIMPL
); /* state remains CMD */
1340 if
(cbuf
[cpos
] == ' ') {
1345 if
((cp2
= strpbrk
(cp
, " \n")))
1350 p
= lookup
(sitetab
, cp
);
1352 if
(guest
== 0 && p
!= 0) {
1354 if
(!p
->implemented
) {
1366 if
(cbuf
[cpos
] == '\n') {
1374 if
(cbuf
[cpos
] == ' ') {
1376 state
= state
== OSTR ? STR2
: state
+1;
1382 if
(cbuf
[cpos
] == '\n') {
1393 * Make sure the string is nonempty and \n terminated.
1395 if
(n
> 1 && cbuf
[cpos
] == '\n') {
1397 yylval.s
= copy
(cp
);
1405 if
(cbuf
[cpos
] == ' ') {
1409 if
(isdigit
(cbuf
[cpos
])) {
1411 while
(isdigit
(cbuf
[++cpos
]))
1415 yylval.u.i
= atoi
(cp
);
1424 if
(isdigit
(cbuf
[cpos
])) {
1426 while
(isdigit
(cbuf
[++cpos
]))
1430 yylval.u.i
= atoi
(cp
);
1431 yylval.u.o
= strtoull
(cp
, NULL
, 10);
1435 if
(strncasecmp
(&cbuf
[cpos
], "ALL", 3) == 0
1436 && !isalnum
(cbuf
[cpos
+ 3])) {
1440 switch
(cbuf
[cpos
++]) {
1504 fatalerror
("Unknown state in scanner.");
1514 while
(*s
!= '\0') {
1526 p
= malloc
(strlen
(s
) + 1);
1528 fatalerror
("Ran out of memory.");
1534 help
(struct tab
*ctab
, char *s
)
1540 if
(ctab
== sitetab
)
1544 width
= 0, NCMDS
= 0;
1545 for
(c
= ctab
; c
->name
!= NULL
; c
++) {
1546 int len
= strlen
(c
->name
);
1552 width
= (width
+ 8) &~
7;
1557 lreply
(214, "The following %scommands are recognized %s.",
1558 type
, "(* =>'s unimplemented)");
1559 columns
= 76 / width
;
1562 lines
= (NCMDS
+ columns
- 1) / columns
;
1563 for
(i
= 0; i
< lines
; i
++) {
1565 for
(j
= 0; j
< columns
; j
++) {
1566 c
= ctab
+ j
* lines
+ i
;
1567 printf
("%s%c", c
->name
,
1568 c
->implemented ?
' ' : '*');
1569 if
(c
+ lines
>= &ctab
[NCMDS
])
1571 w
= strlen
(c
->name
) + 1;
1581 reply
(214, "Direct comments to ftp-bugs@%s.", hostname
);
1587 c
= lookup
(ctab
, s
);
1589 reply
(502, "Unknown command %s.", s
);
1593 reply
(214, "Syntax: %s%s %s", type
, c
->name
, c
->help
);
1595 reply
(214, "%s%-*s\t%s; unimplemented.", type
, width
,
1600 sizecmd
(char *filename
)
1606 if
(stat
(filename
, &stbuf
) < 0)
1607 perror_reply
(550, filename
);
1608 else if
(!S_ISREG
(stbuf.st_mode
))
1609 reply
(550, "%s: not a plain file.", filename
);
1611 reply
(213, "%jd", (intmax_t)stbuf.st_size
);
1618 fin
= fopen
(filename
, "r");
1620 perror_reply
(550, filename
);
1623 if
(fstat
(fileno
(fin
), &stbuf
) < 0) {
1624 perror_reply
(550, filename
);
1627 } else if
(!S_ISREG
(stbuf.st_mode
)) {
1628 reply
(550, "%s: not a plain file.", filename
);
1631 } else if
(stbuf.st_size
> MAXASIZE
) {
1632 reply
(550, "%s: too large for type A SIZE.", filename
);
1638 while
((c
=getc
(fin
)) != EOF
) {
1639 if
(c
== '\n') /* will get expanded to \r\n */
1645 reply
(213, "%jd", (intmax_t)count
);
1648 reply
(504, "SIZE not implemented for type %s.",
1653 /* Return 1, if port check is done. Return 0, if not yet. */
1655 port_check
(const char *pcmd
)
1657 if
(his_addr.su_family
== AF_INET
) {
1658 if
(data_dest.su_family
!= AF_INET
) {
1660 reply
(500, "Invalid address rejected.");
1664 ((ntohs
(data_dest.su_port
) < IPPORT_RESERVED
) ||
1665 memcmp
(&data_dest.su_sin.sin_addr
,
1666 &his_addr.su_sin.sin_addr
,
1667 sizeof
(data_dest.su_sin.sin_addr
)))) {
1669 reply
(500, "Illegal PORT range rejected.");
1676 reply
(200, "%s command successful.", pcmd
);
1689 reply
(530, "Please login with USER and PASS.");
1695 * Replace leading "~user" in a pathname by the user's login directory.
1696 * Returned string will be in a freshly malloced buffer unless it's NULL.
1705 if
((p
= strdup
(s
)) == NULL
)
1710 user
= p
+ 1; /* skip tilde */
1711 if
((path
= strchr
(p
, '/')) != NULL
)
1712 *(path
++) = '\0'; /* separate ~user from the rest of path */
1713 if
(*user
== '\0') /* no user specified, use the current user */
1715 /* read passwd even for the current user since we may be chrooted */
1716 if
((ppw
= getpwnam
(user
)) != NULL
) {
1717 /* user found, substitute login directory for ~user */
1719 asprintf
(&q
, "%s/%s", ppw
->pw_dir
, path
);
1721 q
= strdup
(ppw
->pw_dir
);
1725 /* user not found, undo the damage */
1733 * Expand glob(3) patterns possibly present in a pathname.
1734 * Avoid expanding to a pathname including '\r' or '\n' in order to
1735 * not disrupt the FTP protocol.
1736 * The expansion found must be unique.
1737 * Return the result as a malloced string, or NULL if an error occured.
1739 * Problem: this production is used for all pathname
1740 * processing, but only gives a 550 error reply.
1741 * This is a valid reply in some cases but not in others.
1746 char *p
, **pp
, *rval
;
1747 int flags
= GLOB_BRACE | GLOB_NOCHECK
;
1751 memset
(&gl
, 0, sizeof
(gl
));
1752 /*flags |= GLOB_LIMIT;*/
1753 gl.gl_matchc
= MAXGLOBARGS
;
1754 if
(glob
(s
, flags
, NULL
, &gl
) == 0 && gl.gl_pathc
!= 0) {
1755 for
(pp
= gl.gl_pathv
, p
= NULL
, n
= 0; *pp
; pp
++)
1756 if
(*(*pp
+ strcspn
(*pp
, "\r\n")) == '\0') {
1765 reply
(550, "Wildcard is ambiguous.");
1769 reply
(550, "Wildcard expansion error.");
1777 /* Return 1, if port check is done. Return 0, if not yet. */
1779 port_check_v6
(const char *pcmd
)
1781 if
(his_addr.su_family
== AF_INET6
) {
1782 if
(IN6_IS_ADDR_V4MAPPED
(&his_addr.su_sin6.sin6_addr
))
1783 /* Convert data_dest into v4 mapped sockaddr.*/
1785 if
(data_dest.su_family
!= AF_INET6
) {
1787 reply
(500, "Invalid address rejected.");
1791 ((ntohs
(data_dest.su_port
) < IPPORT_RESERVED
) ||
1792 memcmp
(&data_dest.su_sin6.sin6_addr
,
1793 &his_addr.su_sin6.sin6_addr
,
1794 sizeof
(data_dest.su_sin6.sin6_addr
)))) {
1796 reply
(500, "Illegal PORT range rejected.");
1803 reply
(200, "%s command successful.", pcmd
);
1811 v4map_data_dest
(void)
1813 struct in_addr savedaddr
;
1816 if
(data_dest.su_family
!= AF_INET
) {
1818 reply
(500, "Invalid address rejected.");
1822 savedaddr
= data_dest.su_sin.sin_addr
;
1823 savedport
= data_dest.su_port
;
1825 memset
(&data_dest
, 0, sizeof
(data_dest
));
1826 data_dest.su_sin6.sin6_len
= sizeof
(struct sockaddr_in6
);
1827 data_dest.su_sin6.sin6_family
= AF_INET6
;
1828 data_dest.su_sin6.sin6_port
= savedport
;
1829 memset
((caddr_t
)&data_dest.su_sin6.sin6_addr.s6_addr
[10], 0xff, 2);
1830 memcpy
((caddr_t
)&data_dest.su_sin6.sin6_addr.s6_addr
[12],
1831 (caddr_t
)&savedaddr
, sizeof
(savedaddr
));