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. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
34 * $FreeBSD: src/libexec/ftpd/ftpcmd.y,v 1.67 2008/12/23 01:23:09 cperciva Exp $
35 * $DragonFly: src/libexec/ftpd/ftpcmd.y,v 1.4 2004/06/19 20:36:04 joerg Exp $
39 * Grammar for FTP commands.
45 #include <sys/param.h>
46 #include <sys/socket.h>
49 #include <netinet/in.h>
70 #include "pathnames.h"
72 extern
union sockunion data_dest
, his_addr
;
75 extern
struct passwd
*pw
;
84 extern
int maxtimeout
;
86 extern
char *hostname
;
87 extern
char proctitle
[];
88 extern
int usedefault
;
89 extern
char tmpline
[];
91 extern
int assumeutf8
;
94 extern
int noguestretr
;
95 extern
char *typenames
[]; /* defined in <arpa/ftp.h> included from ftpd.c */
101 static int cmd_bytesz
;
104 char *fromname
= NULL
;
125 USER PASS ACCT REIN QUIT PORT
126 PASV TYPE STRU MODE RETR STOR
127 APPE MLFL MAIL MSND MSOM MSAM
128 MRSQ MRCP ALLO REST RNFR RNTO
129 ABOR DELE CWD LIST NLST SITE
130 STAT HELP NOOP MKD RMD PWD
131 CDUP STOU SMNT SYST SIZE MDTM
132 LPRT LPSV EPRT EPSV FEAT
134 UMASK IDLE CHMOD MDFIVE
141 %type
<u.i
> check_login octal_number byte_size
142 %type
<u.i
> check_login_ro check_login_epsv
143 %type
<u.i
> struct_code mode_code type_code form_code
144 %type
<s
> pathstring pathname password username
145 %type
<s
> ALL NOTIMPL
164 : USER SP username CRLF
169 | PASS SP password CRLF
178 | PORT check_login SP host_port CRLF
181 reply
(501, "No PORT allowed after EPSV ALL.");
186 if
(port_check
("PORT") == 1)
189 if
((his_addr.su_family
!= AF_INET6 ||
190 !IN6_IS_ADDR_V4MAPPED
(&his_addr.su_sin6.sin6_addr
))) {
191 /* shoud never happen */
193 reply
(500, "Invalid address rejected.");
196 port_check_v6
("pcmd");
201 | LPRT check_login SP host_long_port CRLF
204 reply
(501, "No LPRT allowed after EPSV ALL.");
209 if
(port_check
("LPRT") == 1)
212 if
(his_addr.su_family
!= AF_INET6
) {
214 reply
(500, "Invalid address rejected.");
217 if
(port_check_v6
("LPRT") == 1)
223 | EPRT check_login SP STRING CRLF
229 struct addrinfo hints
;
230 struct addrinfo
*res
;
234 reply
(501, "No EPRT allowed after EPSV ALL.");
240 memset
(&data_dest
, 0, sizeof
(data_dest
));
243 syslog
(LOG_DEBUG
, "%s", tmp
);
245 fatalerror
("not enough core");
251 memset
(result
, 0, sizeof
(result
));
252 for
(i
= 0; i
< 3; i
++) {
253 q
= strchr
(p
, delim
);
254 if
(!q ||
*q
!= delim
) {
257 "Invalid argument, rejected.");
266 syslog
(LOG_DEBUG
, "%d: %s", i
, p
);
270 /* some more sanity check */
285 memset
(&hints
, 0, sizeof
(hints
));
286 if
(atoi
(result
[0]) == 1)
287 hints.ai_family
= PF_INET
;
289 else if
(atoi
(result
[0]) == 2)
290 hints.ai_family
= PF_INET6
;
293 hints.ai_family
= PF_UNSPEC
; /*XXX*/
294 hints.ai_socktype
= SOCK_STREAM
;
295 i
= getaddrinfo
(result
[1], result
[2], &hints
, &res
);
298 memcpy
(&data_dest
, res
->ai_addr
, res
->ai_addrlen
);
300 if
(his_addr.su_family
== AF_INET6
301 && data_dest.su_family
== AF_INET6
) {
302 /* XXX more sanity checks! */
303 data_dest.su_sin6.sin6_scope_id
=
304 his_addr.su_sin6.sin6_scope_id
;
310 if
(port_check
("EPRT") == 1)
313 if
(his_addr.su_family
!= AF_INET6
) {
315 reply
(500, "Invalid address rejected.");
318 if
(port_check_v6
("EPRT") == 1)
324 | PASV check_login CRLF
327 reply
(501, "No PASV allowed after EPSV ALL.");
331 | LPSV check_login CRLF
334 reply
(501, "No LPSV allowed after EPSV ALL.");
336 long_passive
("LPSV", PF_UNSPEC
);
338 | EPSV check_login_epsv SP NUMBER CRLF
352 pf
= -1; /*junk value*/
355 long_passive
("EPSV", pf
);
358 | EPSV check_login_epsv SP ALL CRLF
361 reply
(200, "EPSV ALL command successful.");
365 | EPSV check_login_epsv CRLF
368 long_passive
("EPSV", PF_UNSPEC
);
370 | TYPE check_login SP type_code CRLF
376 if
(cmd_form
== FORM_N
) {
377 reply
(200, "Type set to A.");
381 reply
(504, "Form must be N.");
385 reply
(504, "Type E not implemented.");
389 reply
(200, "Type set to I.");
395 if
(cmd_bytesz
== 8) {
397 "Type set to L (byte size 8).");
400 reply
(504, "Byte size must be 8.");
401 #else /* CHAR_BIT == 8 */
402 UNIMPLEMENTED for CHAR_BIT
!= 8
403 #endif /* CHAR_BIT == 8 */
407 | STRU check_login SP struct_code CRLF
413 reply
(200, "STRU F accepted.");
417 reply
(504, "Unimplemented STRU type.");
421 | MODE check_login SP mode_code CRLF
427 reply
(200, "MODE S accepted.");
431 reply
(502, "Unimplemented MODE type.");
435 | ALLO check_login SP NUMBER CRLF
438 reply
(202, "ALLO command ignored.");
441 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
444 reply
(202, "ALLO command ignored.");
447 | RETR check_login SP pathname CRLF
449 if
(noretr ||
(guest
&& noguestretr
))
450 reply
(500, "RETR command disabled.");
451 else if
($2 && $4 != NULL
)
457 | STOR check_login_ro SP pathname CRLF
459 if
($2 && $4 != NULL
)
464 | APPE check_login_ro SP pathname CRLF
466 if
($2 && $4 != NULL
)
471 | NLST check_login CRLF
476 | NLST check_login SP pathstring CRLF
482 | LIST check_login CRLF
485 retrieve
(_PATH_LS
" -lgA", "");
487 | LIST check_login SP pathstring CRLF
490 retrieve
(_PATH_LS
" -lgA %s", $4);
493 | STAT check_login SP pathname CRLF
495 if
($2 && $4 != NULL
)
500 | STAT check_login CRLF
506 | DELE check_login_ro SP pathname CRLF
508 if
($2 && $4 != NULL
)
513 | RNTO check_login_ro SP pathname CRLF
515 if
($2 && $4 != NULL
) {
517 renamecmd
(fromname
, $4);
521 reply
(503, "Bad sequence of commands.");
527 | ABOR check_login CRLF
530 reply
(225, "ABOR command successful.");
532 | CWD check_login CRLF
538 | CWD check_login SP pathname CRLF
540 if
($2 && $4 != NULL
)
549 | HELP SP STRING CRLF
553 if
(strncasecmp
(cp
, "SITE", 4) == 0) {
567 reply
(200, "NOOP command successful.");
569 | MKD check_login_ro SP pathname CRLF
571 if
($2 && $4 != NULL
)
576 | RMD check_login_ro SP pathname CRLF
578 if
($2 && $4 != NULL
)
583 | PWD check_login CRLF
588 | CDUP check_login CRLF
597 | SITE SP HELP SP STRING CRLF
602 | SITE SP MDFIVE check_login SP pathname CRLF
609 reply
(200, "MD5(%s) = %s", $6, p
);
611 perror_reply
(550, $6);
616 | SITE SP UMASK check_login CRLF
623 reply
(200, "Current UMASK is %03o.", oldmask
);
626 | SITE SP UMASK check_login SP octal_number CRLF
631 if
(($6 == -1) ||
($6 > 0777)) {
632 reply
(501, "Bad UMASK value.");
636 "UMASK set to %03o (was %03o).",
641 | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
643 if
($4 && ($8 != NULL
)) {
644 if
(($6 == -1 ) ||
($6 > 0777))
645 reply
(501, "Bad mode value.");
646 else if
(chmod
($8, $6) < 0)
647 perror_reply
(550, $8);
649 reply
(200, "CHMOD command successful.");
654 | SITE SP check_login IDLE CRLF
658 "Current IDLE time limit is %d seconds; max %d.",
659 timeout
, maxtimeout
);
661 | SITE SP check_login IDLE SP NUMBER CRLF
664 if
($6.i
< 30 ||
$6.i
> maxtimeout
) {
666 "Maximum IDLE time must be between 30 and %d seconds.",
672 "Maximum IDLE time set to %d seconds.",
677 | STOU check_login_ro SP pathname CRLF
679 if
($2 && $4 != NULL
)
686 lreply
(211, "Extensions supported:");
688 /* XXX these two keywords are non-standard */
694 printf
(" REST STREAM\r\n");
697 /* TVFS requires UTF8, see RFC 3659 */
703 | SYST check_login CRLF
708 reply
(215, "UNIX Type: L%d Version: BSD-%d",
711 reply
(215, "UNIX Type: L%d", CHAR_BIT
);
714 reply
(215, "UNKNOWN Type: L%d", CHAR_BIT
);
719 * SIZE is not in RFC959, but Postel has blessed it and
720 * it will be in the updated RFC.
722 * Return size of file in a format suitable for
723 * using with RESTART (we just count bytes).
725 | SIZE check_login SP pathname CRLF
727 if
($2 && $4 != NULL
)
734 * MDTM is not in RFC959, but Postel has blessed it and
735 * it will be in the updated RFC.
737 * Return modification time of file as an ISO 3307
738 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
739 * where xxx is the fractional second (of any precision,
740 * not necessarily 3 digits)
742 | MDTM check_login SP pathname CRLF
744 if
($2 && $4 != NULL
) {
746 if
(stat
($4, &stbuf
) < 0)
747 perror_reply
(550, $4);
748 else if
(!S_ISREG
(stbuf.st_mode
)) {
749 reply
(550, "%s: not a plain file.", $4);
752 t
= gmtime
(&stbuf.st_mtime
);
754 "%04d%02d%02d%02d%02d%02d",
756 t
->tm_mon
+1, t
->tm_mday
,
757 t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
765 reply
(221, "Goodbye.");
774 yyclearin; /* discard lookahead data */
775 yyerrok; /* clear error condition */
776 state
= CMD
; /* reset lexer state */
780 : RNFR check_login_ro SP pathname CRLF
795 | REST check_login SP NUMBER CRLF
801 restart_point
= $4.o
;
802 reply
(350, "Restarting at %jd. %s",
803 (intmax_t)restart_point
,
804 "Send STORE or RETRIEVE to initiate transfer.");
816 $$
= (char *)calloc
(1, sizeof
(char));
829 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
834 data_dest.su_len
= sizeof
(struct sockaddr_in
);
835 data_dest.su_family
= AF_INET
;
836 p
= (char *)&data_dest.su_sin.sin_port
;
837 p
[0] = $9.i
; p
[1] = $11.i
;
838 a
= (char *)&data_dest.su_sin.sin_addr
;
839 a
[0] = $1.i
; a
[1] = $3.i
; a
[2] = $5.i
; a
[3] = $7.i
;
844 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
845 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
846 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
847 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
848 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
853 memset
(&data_dest
, 0, sizeof
(data_dest
));
854 data_dest.su_len
= sizeof
(struct sockaddr_in6
);
855 data_dest.su_family
= AF_INET6
;
856 p
= (char *)&data_dest.su_port
;
857 p
[0] = $39.i
; p
[1] = $41.i
;
858 a
= (char *)&data_dest.su_sin6.sin6_addr
;
859 a
[0] = $5.i
; a
[1] = $7.i
; a
[2] = $9.i
; a
[3] = $11.i
;
860 a
[4] = $13.i
; a
[5] = $15.i
; a
[6] = $17.i
; a
[7] = $19.i
;
861 a
[8] = $21.i
; a
[9] = $23.i
; a
[10] = $25.i
; a
[11] = $27.i
;
862 a
[12] = $29.i
; a
[13] = $31.i
; a
[14] = $33.i
; a
[15] = $35.i
;
863 if
(his_addr.su_family
== AF_INET6
) {
864 /* XXX more sanity checks! */
865 data_dest.su_sin6.sin6_scope_id
=
866 his_addr.su_sin6.sin6_scope_id
;
868 if
($1.i
!= 6 ||
$3.i
!= 16 ||
$37.i
!= 2)
869 memset
(&data_dest
, 0, sizeof
(data_dest
));
871 | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
872 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
877 memset
(&data_dest
, 0, sizeof
(data_dest
));
878 data_dest.su_sin.sin_len
= sizeof
(struct sockaddr_in
);
879 data_dest.su_family
= AF_INET
;
880 p
= (char *)&data_dest.su_port
;
881 p
[0] = $15.i
; p
[1] = $17.i
;
882 a
= (char *)&data_dest.su_sin.sin_addr
;
883 a
[0] = $5.i
; a
[1] = $7.i
; a
[2] = $9.i
; a
[3] = $11.i
;
884 if
($1.i
!= 4 ||
$3.i
!= 4 ||
$13.i
!= 2)
885 memset
(&data_dest
, 0, sizeof
(data_dest
));
932 cmd_bytesz
= CHAR_BIT
;
939 /* this is for a bug in the BBN ftp */
980 if
(logged_in
&& $1) {
984 * Expand ~user manually since glob(3)
985 * will return the unexpanded pathname
986 * if the corresponding file/directory
987 * doesn't exist yet. Using sole glob(3)
988 * would break natural commands like
993 if
((p
= exptilde
($1)) != NULL
) {
1011 int ret
, dec
, multby
, digit
;
1014 * Convert a number that was read as decimal number
1015 * to what it would be if it had been read as octal.
1026 ret
+= digit
* multby
;
1038 $$
= check_login1
();
1046 reply
(500, "EPSV command disabled.");
1050 $$
= check_login1
();
1058 reply
(550, "Permission denied.");
1062 $$
= check_login1
();
1068 #define CMD 0 /* beginning of command */
1069 #define ARGS 1 /* expect miscellaneous arguments */
1070 #define STR1 2 /* expect SP followed by STRING */
1071 #define STR2 3 /* expect STRING */
1072 #define OSTR 4 /* optional SP then STRING */
1073 #define ZSTR1 5 /* optional SP then optional STRING */
1074 #define ZSTR2 6 /* optional STRING after SP */
1075 #define SITECMD 7 /* SITE command */
1076 #define NSTR 8 /* Number followed by a string */
1078 #define MAXGLOBARGS 1000
1080 #define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */
1086 short implemented
; /* 1 if command is implemented */
1090 struct tab cmdtab
[] = { /* In order defined in RFC 765 */
1091 { "USER", USER
, STR1
, 1, "<sp> username" },
1092 { "PASS", PASS
, ZSTR1
, 1, "[<sp> [password]]" },
1093 { "ACCT", ACCT
, STR1
, 0, "(specify account)" },
1094 { "SMNT", SMNT
, ARGS
, 0, "(structure mount)" },
1095 { "REIN", REIN
, ARGS
, 0, "(reinitialize server state)" },
1096 { "QUIT", QUIT
, ARGS
, 1, "(terminate service)", },
1097 { "PORT", PORT
, ARGS
, 1, "<sp> b0, b1, b2, b3, b4, b5" },
1098 { "LPRT", LPRT
, ARGS
, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1099 { "EPRT", EPRT
, STR1
, 1, "<sp> |af|addr|port|" },
1100 { "PASV", PASV
, ARGS
, 1, "(set server in passive mode)" },
1101 { "LPSV", LPSV
, ARGS
, 1, "(set server in passive mode)" },
1102 { "EPSV", EPSV
, ARGS
, 1, "[<sp> af|ALL]" },
1103 { "TYPE", TYPE
, ARGS
, 1, "<sp> { A | E | I | L }" },
1104 { "STRU", STRU
, ARGS
, 1, "(specify file structure)" },
1105 { "MODE", MODE
, ARGS
, 1, "(specify transfer mode)" },
1106 { "RETR", RETR
, STR1
, 1, "<sp> file-name" },
1107 { "STOR", STOR
, STR1
, 1, "<sp> file-name" },
1108 { "APPE", APPE
, STR1
, 1, "<sp> file-name" },
1109 { "MLFL", MLFL
, OSTR
, 0, "(mail file)" },
1110 { "MAIL", MAIL
, OSTR
, 0, "(mail to user)" },
1111 { "MSND", MSND
, OSTR
, 0, "(mail send to terminal)" },
1112 { "MSOM", MSOM
, OSTR
, 0, "(mail send to terminal or mailbox)" },
1113 { "MSAM", MSAM
, OSTR
, 0, "(mail send to terminal and mailbox)" },
1114 { "MRSQ", MRSQ
, OSTR
, 0, "(mail recipient scheme question)" },
1115 { "MRCP", MRCP
, STR1
, 0, "(mail recipient)" },
1116 { "ALLO", ALLO
, ARGS
, 1, "allocate storage (vacuously)" },
1117 { "REST", REST
, ARGS
, 1, "<sp> offset (restart command)" },
1118 { "RNFR", RNFR
, STR1
, 1, "<sp> file-name" },
1119 { "RNTO", RNTO
, STR1
, 1, "<sp> file-name" },
1120 { "ABOR", ABOR
, ARGS
, 1, "(abort operation)" },
1121 { "DELE", DELE
, STR1
, 1, "<sp> file-name" },
1122 { "CWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
1123 { "XCWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
1124 { "LIST", LIST
, OSTR
, 1, "[ <sp> path-name ]" },
1125 { "NLST", NLST
, OSTR
, 1, "[ <sp> path-name ]" },
1126 { "SITE", SITE
, SITECMD
, 1, "site-cmd [ <sp> arguments ]" },
1127 { "SYST", SYST
, ARGS
, 1, "(get type of operating system)" },
1128 { "FEAT", FEAT
, ARGS
, 1, "(get extended features)" },
1129 { "STAT", STAT
, OSTR
, 1, "[ <sp> path-name ]" },
1130 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
1131 { "NOOP", NOOP
, ARGS
, 1, "" },
1132 { "MKD", MKD
, STR1
, 1, "<sp> path-name" },
1133 { "XMKD", MKD
, STR1
, 1, "<sp> path-name" },
1134 { "RMD", RMD
, STR1
, 1, "<sp> path-name" },
1135 { "XRMD", RMD
, STR1
, 1, "<sp> path-name" },
1136 { "PWD", PWD
, ARGS
, 1, "(return current directory)" },
1137 { "XPWD", PWD
, ARGS
, 1, "(return current directory)" },
1138 { "CDUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
1139 { "XCUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
1140 { "STOU", STOU
, STR1
, 1, "<sp> file-name" },
1141 { "SIZE", SIZE
, OSTR
, 1, "<sp> path-name" },
1142 { "MDTM", MDTM
, OSTR
, 1, "<sp> path-name" },
1143 { NULL
, 0, 0, 0, 0 }
1146 struct tab sitetab
[] = {
1147 { "MD5", MDFIVE
, STR1
, 1, "[ <sp> file-name ]" },
1148 { "UMASK", UMASK
, ARGS
, 1, "[ <sp> umask ]" },
1149 { "IDLE", IDLE
, ARGS
, 1, "[ <sp> maximum-idle-time ]" },
1150 { "CHMOD", CHMOD
, NSTR
, 1, "<sp> mode <sp> file-name" },
1151 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
1152 { NULL
, 0, 0, 0, 0 }
1155 static char *copy
(char *);
1156 static char *expglob
(char *);
1157 static char *exptilde
(char *);
1158 static void help
(struct tab
*, char *);
1160 lookup
(struct tab
*, char *);
1161 static int port_check
(const char *);
1163 static int port_check_v6
(const char *);
1165 static void sizecmd
(char *);
1166 static void toolong
(int);
1168 static void v4map_data_dest
(void);
1170 static int yylex(void);
1173 lookup
(struct tab
*p
, char *cmd
)
1176 for
(; p
->name
!= NULL
; p
++)
1177 if
(strcmp
(cmd
, p
->name
) == 0)
1182 #include <arpa/telnet.h>
1185 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1188 getline
(char *s
, int n
, FILE *iop
)
1192 sigset_t sset
, osset
;
1195 /* tmpline may contain saved command from urgent mode interruption */
1196 for
(c
= 0; tmpline
[c
] != '\0' && --n
> 0; ++c
) {
1198 if
(tmpline
[c
] == '\n') {
1201 syslog
(LOG_DEBUG
, "command: %s", s
);
1208 /* SIGURG would interrupt stdio if not blocked during the read loop */
1210 sigaddset
(&sset
, SIGURG
);
1211 sigprocmask
(SIG_BLOCK
, &sset
, &osset
);
1212 while
((c
= getc
(iop
)) != EOF
) {
1215 if
((c
= getc
(iop
)) == EOF
)
1221 if
((c
= getc
(iop
)) == EOF
)
1223 printf
("%c%c%c", IAC
, DONT
, 0377&c
);
1228 if
((c
= getc
(iop
)) == EOF
)
1230 printf
("%c%c%c", IAC
, WONT
, 0377&c
);
1236 continue
; /* ignore command */
1242 * If command doesn't fit into buffer, discard the
1243 * rest of the command and indicate truncation.
1244 * This prevents the command to be split up into
1245 * multiple commands.
1247 while
(c
!= '\n' && (c
= getc
(iop
)) != EOF
)
1255 sigprocmask
(SIG_SETMASK
, &osset
, NULL
);
1256 if
(c
== EOF
&& cs
== s
)
1260 if
(!guest
&& strncasecmp
("pass ", s
, 5) == 0) {
1261 /* Don't syslog passwords */
1262 syslog
(LOG_DEBUG
, "command: %.5s ???", s
);
1267 /* Don't syslog trailing CR-LF */
1270 while
(cp
>= s
&& (*cp
== '\n' ||
*cp
== '\r')) {
1274 syslog
(LOG_DEBUG
, "command: %.*s", len
, s
);
1285 "Timeout (%d seconds): closing control connection.", timeout
);
1287 syslog
(LOG_INFO
, "User %s timed out after %d seconds",
1288 (pw ? pw
-> pw_name
: "unknown"), timeout
);
1305 signal
(SIGALRM
, toolong
);
1307 n
= getline
(cbuf
, sizeof
(cbuf
)-1, stdin
);
1309 reply
(221, "You could at least say goodbye.");
1311 } else if
(n
== -2) {
1312 reply
(500, "Command too long.");
1318 if
(strncasecmp
(cbuf
, "PASS", 4) != 0)
1319 setproctitle
("%s: %s", proctitle
, cbuf
);
1320 #endif /* SETPROCTITLE */
1321 if
((cp
= strchr
(cbuf
, '\r'))) {
1325 if
((cp
= strpbrk
(cbuf
, " \n")))
1332 p
= lookup
(cmdtab
, cbuf
);
1336 if
(!p
->implemented
)
1337 return
(NOTIMPL
); /* state remains CMD */
1344 if
(cbuf
[cpos
] == ' ') {
1349 if
((cp2
= strpbrk
(cp
, " \n")))
1354 p
= lookup
(sitetab
, cp
);
1356 if
(guest
== 0 && p
!= 0) {
1358 if
(!p
->implemented
) {
1370 if
(cbuf
[cpos
] == '\n') {
1378 if
(cbuf
[cpos
] == ' ') {
1380 state
= state
== OSTR ? STR2
: state
+1;
1386 if
(cbuf
[cpos
] == '\n') {
1397 * Make sure the string is nonempty and \n terminated.
1399 if
(n
> 1 && cbuf
[cpos
] == '\n') {
1401 yylval.s
= copy
(cp
);
1409 if
(cbuf
[cpos
] == ' ') {
1413 if
(isdigit
(cbuf
[cpos
])) {
1415 while
(isdigit
(cbuf
[++cpos
]))
1419 yylval.u.i
= atoi
(cp
);
1428 if
(isdigit
(cbuf
[cpos
])) {
1430 while
(isdigit
(cbuf
[++cpos
]))
1434 yylval.u.i
= atoi
(cp
);
1435 yylval.u.o
= strtoull
(cp
, NULL
, 10);
1439 if
(strncasecmp
(&cbuf
[cpos
], "ALL", 3) == 0
1440 && !isalnum
(cbuf
[cpos
+ 3])) {
1444 switch
(cbuf
[cpos
++]) {
1508 fatalerror
("Unknown state in scanner.");
1518 while
(*s
!= '\0') {
1530 p
= malloc
(strlen
(s
) + 1);
1532 fatalerror
("Ran out of memory.");
1538 help
(struct tab
*ctab
, char *s
)
1544 if
(ctab
== sitetab
)
1548 width
= 0, NCMDS
= 0;
1549 for
(c
= ctab
; c
->name
!= NULL
; c
++) {
1550 int len
= strlen
(c
->name
);
1556 width
= (width
+ 8) &~
7;
1561 lreply
(214, "The following %scommands are recognized %s.",
1562 type
, "(* =>'s unimplemented)");
1563 columns
= 76 / width
;
1566 lines
= (NCMDS
+ columns
- 1) / columns
;
1567 for
(i
= 0; i
< lines
; i
++) {
1569 for
(j
= 0; j
< columns
; j
++) {
1570 c
= ctab
+ j
* lines
+ i
;
1571 printf
("%s%c", c
->name
,
1572 c
->implemented ?
' ' : '*');
1573 if
(c
+ lines
>= &ctab
[NCMDS
])
1575 w
= strlen
(c
->name
) + 1;
1585 reply
(214, "Direct comments to ftp-bugs@%s.", hostname
);
1591 c
= lookup
(ctab
, s
);
1593 reply
(502, "Unknown command %s.", s
);
1597 reply
(214, "Syntax: %s%s %s", type
, c
->name
, c
->help
);
1599 reply
(214, "%s%-*s\t%s; unimplemented.", type
, width
,
1604 sizecmd
(char *filename
)
1610 if
(stat
(filename
, &stbuf
) < 0)
1611 perror_reply
(550, filename
);
1612 else if
(!S_ISREG
(stbuf.st_mode
))
1613 reply
(550, "%s: not a plain file.", filename
);
1615 reply
(213, "%jd", (intmax_t)stbuf.st_size
);
1622 fin
= fopen
(filename
, "r");
1624 perror_reply
(550, filename
);
1627 if
(fstat
(fileno
(fin
), &stbuf
) < 0) {
1628 perror_reply
(550, filename
);
1631 } else if
(!S_ISREG
(stbuf.st_mode
)) {
1632 reply
(550, "%s: not a plain file.", filename
);
1635 } else if
(stbuf.st_size
> MAXASIZE
) {
1636 reply
(550, "%s: too large for type A SIZE.", filename
);
1642 while
((c
=getc
(fin
)) != EOF
) {
1643 if
(c
== '\n') /* will get expanded to \r\n */
1649 reply
(213, "%jd", (intmax_t)count
);
1652 reply
(504, "SIZE not implemented for type %s.",
1657 /* Return 1, if port check is done. Return 0, if not yet. */
1659 port_check
(const char *pcmd
)
1661 if
(his_addr.su_family
== AF_INET
) {
1662 if
(data_dest.su_family
!= AF_INET
) {
1664 reply
(500, "Invalid address rejected.");
1668 ((ntohs
(data_dest.su_port
) < IPPORT_RESERVED
) ||
1669 memcmp
(&data_dest.su_sin.sin_addr
,
1670 &his_addr.su_sin.sin_addr
,
1671 sizeof
(data_dest.su_sin.sin_addr
)))) {
1673 reply
(500, "Illegal PORT range rejected.");
1680 reply
(200, "%s command successful.", pcmd
);
1693 reply
(530, "Please login with USER and PASS.");
1699 * Replace leading "~user" in a pathname by the user's login directory.
1700 * Returned string will be in a freshly malloced buffer unless it's NULL.
1709 if
((p
= strdup
(s
)) == NULL
)
1714 user
= p
+ 1; /* skip tilde */
1715 if
((path
= strchr
(p
, '/')) != NULL
)
1716 *(path
++) = '\0'; /* separate ~user from the rest of path */
1717 if
(*user
== '\0') /* no user specified, use the current user */
1719 /* read passwd even for the current user since we may be chrooted */
1720 if
((ppw
= getpwnam
(user
)) != NULL
) {
1721 /* user found, substitute login directory for ~user */
1723 asprintf
(&q
, "%s/%s", ppw
->pw_dir
, path
);
1725 q
= strdup
(ppw
->pw_dir
);
1729 /* user not found, undo the damage */
1737 * Expand glob(3) patterns possibly present in a pathname.
1738 * Avoid expanding to a pathname including '\r' or '\n' in order to
1739 * not disrupt the FTP protocol.
1740 * The expansion found must be unique.
1741 * Return the result as a malloced string, or NULL if an error occured.
1743 * Problem: this production is used for all pathname
1744 * processing, but only gives a 550 error reply.
1745 * This is a valid reply in some cases but not in others.
1750 char *p
, **pp
, *rval
;
1751 int flags
= GLOB_BRACE | GLOB_NOCHECK
;
1755 memset
(&gl
, 0, sizeof
(gl
));
1756 flags |
= GLOB_LIMIT
;
1757 gl.gl_matchc
= MAXGLOBARGS
;
1758 if
(glob
(s
, flags
, NULL
, &gl
) == 0 && gl.gl_pathc
!= 0) {
1759 for
(pp
= gl.gl_pathv
, p
= NULL
, n
= 0; *pp
; pp
++)
1760 if
(*(*pp
+ strcspn
(*pp
, "\r\n")) == '\0') {
1769 reply
(550, "Wildcard is ambiguous.");
1773 reply
(550, "Wildcard expansion error.");
1781 /* Return 1, if port check is done. Return 0, if not yet. */
1783 port_check_v6
(const char *pcmd
)
1785 if
(his_addr.su_family
== AF_INET6
) {
1786 if
(IN6_IS_ADDR_V4MAPPED
(&his_addr.su_sin6.sin6_addr
))
1787 /* Convert data_dest into v4 mapped sockaddr.*/
1789 if
(data_dest.su_family
!= AF_INET6
) {
1791 reply
(500, "Invalid address rejected.");
1795 ((ntohs
(data_dest.su_port
) < IPPORT_RESERVED
) ||
1796 memcmp
(&data_dest.su_sin6.sin6_addr
,
1797 &his_addr.su_sin6.sin6_addr
,
1798 sizeof
(data_dest.su_sin6.sin6_addr
)))) {
1800 reply
(500, "Illegal PORT range rejected.");
1807 reply
(200, "%s command successful.", pcmd
);
1815 v4map_data_dest
(void)
1817 struct in_addr savedaddr
;
1820 if
(data_dest.su_family
!= AF_INET
) {
1822 reply
(500, "Invalid address rejected.");
1826 savedaddr
= data_dest.su_sin.sin_addr
;
1827 savedport
= data_dest.su_port
;
1829 memset
(&data_dest
, 0, sizeof
(data_dest
));
1830 data_dest.su_sin6.sin6_len
= sizeof
(struct sockaddr_in6
);
1831 data_dest.su_sin6.sin6_family
= AF_INET6
;
1832 data_dest.su_sin6.sin6_port
= savedport
;
1833 memset
((caddr_t
)&data_dest.su_sin6.sin6_addr.s6_addr
[10], 0xff, 2);
1834 memcpy
((caddr_t
)&data_dest.su_sin6.sin6_addr.s6_addr
[12],
1835 (caddr_t
)&savedaddr
, sizeof
(savedaddr
));