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
37 * Grammar for FTP commands.
45 static char sccsid
[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94";
49 #include <sys/cdefs.h>
50 __FBSDID
("$FreeBSD$");
52 #include <sys/param.h>
53 #include <sys/socket.h>
56 #include <netinet/in.h>
77 #include "pathnames.h"
79 extern
union sockunion data_dest
, his_addr
;
82 extern
struct passwd
*pw
;
91 extern
int maxtimeout
;
93 extern
char *hostname
;
94 extern
char proctitle
[];
95 extern
int usedefault
;
96 extern
char tmpline
[];
98 extern
int assumeutf8
;
101 extern
int noguestretr
;
102 extern
char *typenames
[]; /* defined in <arpa/ftp.h> included from ftpd.c */
108 static int cmd_bytesz
;
111 char *fromname
= NULL
;
132 USER PASS ACCT REIN QUIT PORT
133 PASV TYPE STRU MODE RETR STOR
134 APPE MLFL MAIL MSND MSOM MSAM
135 MRSQ MRCP ALLO REST RNFR RNTO
136 ABOR DELE CWD LIST NLST SITE
137 STAT HELP NOOP MKD RMD PWD
138 CDUP STOU SMNT SYST SIZE MDTM
139 LPRT LPSV EPRT EPSV FEAT
141 UMASK IDLE CHMOD MDFIVE
148 %type
<u.i
> check_login octal_number byte_size
149 %type
<u.i
> check_login_ro check_login_epsv
150 %type
<u.i
> struct_code mode_code type_code form_code
151 %type
<s
> pathstring pathname password username
152 %type
<s
> ALL NOTIMPL
171 : USER SP username CRLF
176 | PASS SP password CRLF
185 | PORT check_login SP host_port CRLF
188 reply
(501, "No PORT allowed after EPSV ALL.");
193 if
(port_check
("PORT") == 1)
196 if
((his_addr.su_family
!= AF_INET6 ||
197 !IN6_IS_ADDR_V4MAPPED
(&his_addr.su_sin6.sin6_addr
))) {
198 /* shoud never happen */
200 reply
(500, "Invalid address rejected.");
203 port_check_v6
("pcmd");
208 | LPRT check_login SP host_long_port CRLF
211 reply
(501, "No LPRT allowed after EPSV ALL.");
216 if
(port_check
("LPRT") == 1)
219 if
(his_addr.su_family
!= AF_INET6
) {
221 reply
(500, "Invalid address rejected.");
224 if
(port_check_v6
("LPRT") == 1)
230 | EPRT check_login SP STRING CRLF
236 struct addrinfo hints
;
237 struct addrinfo
*res
;
241 reply
(501, "No EPRT allowed after EPSV ALL.");
247 memset
(&data_dest
, 0, sizeof
(data_dest
));
250 syslog
(LOG_DEBUG
, "%s", tmp
);
252 fatalerror
("not enough core");
258 memset
(result
, 0, sizeof
(result
));
259 for
(i
= 0; i
< 3; i
++) {
260 q
= strchr
(p
, delim
);
261 if
(!q ||
*q
!= delim
) {
264 "Invalid argument, rejected.");
273 syslog
(LOG_DEBUG
, "%d: %s", i
, p
);
277 /* some more sanity check */
292 memset
(&hints
, 0, sizeof
(hints
));
293 if
(atoi
(result
[0]) == 1)
294 hints.ai_family
= PF_INET
;
296 else if
(atoi
(result
[0]) == 2)
297 hints.ai_family
= PF_INET6
;
300 hints.ai_family
= PF_UNSPEC
; /*XXX*/
301 hints.ai_socktype
= SOCK_STREAM
;
302 i
= getaddrinfo
(result
[1], result
[2], &hints
, &res
);
305 memcpy
(&data_dest
, res
->ai_addr
, res
->ai_addrlen
);
307 if
(his_addr.su_family
== AF_INET6
308 && data_dest.su_family
== AF_INET6
) {
309 /* XXX more sanity checks! */
310 data_dest.su_sin6.sin6_scope_id
=
311 his_addr.su_sin6.sin6_scope_id
;
317 if
(port_check
("EPRT") == 1)
320 if
(his_addr.su_family
!= AF_INET6
) {
322 reply
(500, "Invalid address rejected.");
325 if
(port_check_v6
("EPRT") == 1)
331 | PASV check_login CRLF
334 reply
(501, "No PASV allowed after EPSV ALL.");
338 | LPSV check_login CRLF
341 reply
(501, "No LPSV allowed after EPSV ALL.");
343 long_passive
("LPSV", PF_UNSPEC
);
345 | EPSV check_login_epsv SP NUMBER CRLF
359 pf
= -1; /*junk value*/
362 long_passive
("EPSV", pf
);
365 | EPSV check_login_epsv SP ALL CRLF
368 reply
(200, "EPSV ALL command successful.");
372 | EPSV check_login_epsv CRLF
375 long_passive
("EPSV", PF_UNSPEC
);
377 | TYPE check_login SP type_code CRLF
383 if
(cmd_form
== FORM_N
) {
384 reply
(200, "Type set to A.");
388 reply
(504, "Form must be N.");
392 reply
(504, "Type E not implemented.");
396 reply
(200, "Type set to I.");
402 if
(cmd_bytesz
== 8) {
404 "Type set to L (byte size 8).");
407 reply
(504, "Byte size must be 8.");
408 #else /* CHAR_BIT == 8 */
409 UNIMPLEMENTED for CHAR_BIT
!= 8
410 #endif /* CHAR_BIT == 8 */
414 | STRU check_login SP struct_code CRLF
420 reply
(200, "STRU F accepted.");
424 reply
(504, "Unimplemented STRU type.");
428 | MODE check_login SP mode_code CRLF
434 reply
(200, "MODE S accepted.");
438 reply
(502, "Unimplemented MODE type.");
442 | ALLO check_login SP NUMBER CRLF
445 reply
(202, "ALLO command ignored.");
448 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
451 reply
(202, "ALLO command ignored.");
454 | RETR check_login SP pathname CRLF
456 if
(noretr ||
(guest
&& noguestretr
))
457 reply
(500, "RETR command disabled.");
458 else if
($2 && $4 != NULL
)
464 | STOR check_login_ro SP pathname CRLF
466 if
($2 && $4 != NULL
)
471 | APPE check_login_ro SP pathname CRLF
473 if
($2 && $4 != NULL
)
478 | NLST check_login CRLF
483 | NLST check_login SP pathstring CRLF
489 | LIST check_login CRLF
492 retrieve
(_PATH_LS
" -lgA", "");
494 | LIST check_login SP pathstring CRLF
497 retrieve
(_PATH_LS
" -lgA %s", $4);
500 | STAT check_login SP pathname CRLF
502 if
($2 && $4 != NULL
)
507 | STAT check_login CRLF
513 | DELE check_login_ro SP pathname CRLF
515 if
($2 && $4 != NULL
)
520 | RNTO check_login_ro SP pathname CRLF
522 if
($2 && $4 != NULL
) {
524 renamecmd
(fromname
, $4);
528 reply
(503, "Bad sequence of commands.");
534 | ABOR check_login CRLF
537 reply
(225, "ABOR command successful.");
539 | CWD check_login CRLF
545 | CWD check_login SP pathname CRLF
547 if
($2 && $4 != NULL
)
556 | HELP SP STRING CRLF
560 if
(strncasecmp
(cp
, "SITE", 4) == 0) {
574 reply
(200, "NOOP command successful.");
576 | MKD check_login_ro SP pathname CRLF
578 if
($2 && $4 != NULL
)
583 | RMD check_login_ro SP pathname CRLF
585 if
($2 && $4 != NULL
)
590 | PWD check_login CRLF
595 | CDUP check_login CRLF
604 | SITE SP HELP SP STRING CRLF
609 | SITE SP MDFIVE check_login SP pathname CRLF
616 reply
(200, "MD5(%s) = %s", $6, p
);
618 perror_reply
(550, $6);
623 | SITE SP UMASK check_login CRLF
629 (void) umask
(oldmask
);
630 reply
(200, "Current UMASK is %03o.", oldmask
);
633 | SITE SP UMASK check_login SP octal_number CRLF
638 if
(($6 == -1) ||
($6 > 0777)) {
639 reply
(501, "Bad UMASK value.");
643 "UMASK set to %03o (was %03o).",
648 | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
650 if
($4 && ($8 != NULL
)) {
651 if
(($6 == -1 ) ||
($6 > 0777))
652 reply
(501, "Bad mode value.");
653 else if
(chmod
($8, $6) < 0)
654 perror_reply
(550, $8);
656 reply
(200, "CHMOD command successful.");
661 | SITE SP check_login IDLE CRLF
665 "Current IDLE time limit is %d seconds; max %d.",
666 timeout
, maxtimeout
);
668 | SITE SP check_login IDLE SP NUMBER CRLF
671 if
($6.i
< 30 ||
$6.i
> maxtimeout
) {
673 "Maximum IDLE time must be between 30 and %d seconds.",
677 (void) alarm
(timeout
);
679 "Maximum IDLE time set to %d seconds.",
684 | STOU check_login_ro SP pathname CRLF
686 if
($2 && $4 != NULL
)
693 lreply
(211, "Extensions supported:");
695 /* XXX these two keywords are non-standard */
701 printf
(" REST STREAM\r\n");
704 /* TVFS requires UTF8, see RFC 3659 */
710 | SYST check_login CRLF
715 reply
(215, "UNIX Type: L%d Version: BSD-%d",
718 reply
(215, "UNIX Type: L%d", CHAR_BIT
);
721 reply
(215, "UNKNOWN Type: L%d", CHAR_BIT
);
726 * SIZE is not in RFC959, but Postel has blessed it and
727 * it will be in the updated RFC.
729 * Return size of file in a format suitable for
730 * using with RESTART (we just count bytes).
732 | SIZE check_login SP pathname CRLF
734 if
($2 && $4 != NULL
)
741 * MDTM is not in RFC959, but Postel has blessed it and
742 * it will be in the updated RFC.
744 * Return modification time of file as an ISO 3307
745 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
746 * where xxx is the fractional second (of any precision,
747 * not necessarily 3 digits)
749 | MDTM check_login SP pathname CRLF
751 if
($2 && $4 != NULL
) {
753 if
(stat
($4, &stbuf
) < 0)
754 perror_reply
(550, $4);
755 else if
(!S_ISREG
(stbuf.st_mode
)) {
756 reply
(550, "%s: not a plain file.", $4);
759 t
= gmtime
(&stbuf.st_mtime
);
761 "%04d%02d%02d%02d%02d%02d",
763 t
->tm_mon
+1, t
->tm_mday
,
764 t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
772 reply
(221, "Goodbye.");
781 yyclearin; /* discard lookahead data */
782 yyerrok; /* clear error condition */
783 state
= CMD
; /* reset lexer state */
787 : RNFR check_login_ro SP pathname CRLF
802 | REST check_login SP NUMBER CRLF
808 restart_point
= $4.o
;
809 reply
(350, "Restarting at %jd. %s",
810 (intmax_t)restart_point
,
811 "Send STORE or RETRIEVE to initiate transfer.");
823 $$
= (char *)calloc
(1, sizeof
(char));
836 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
841 data_dest.su_len
= sizeof
(struct sockaddr_in
);
842 data_dest.su_family
= AF_INET
;
843 p
= (char *)&data_dest.su_sin.sin_port
;
844 p
[0] = $9.i
; p
[1] = $11.i
;
845 a
= (char *)&data_dest.su_sin.sin_addr
;
846 a
[0] = $1.i
; a
[1] = $3.i
; a
[2] = $5.i
; a
[3] = $7.i
;
851 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
852 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
853 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
854 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
855 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
860 memset
(&data_dest
, 0, sizeof
(data_dest
));
861 data_dest.su_len
= sizeof
(struct sockaddr_in6
);
862 data_dest.su_family
= AF_INET6
;
863 p
= (char *)&data_dest.su_port
;
864 p
[0] = $39.i
; p
[1] = $41.i
;
865 a
= (char *)&data_dest.su_sin6.sin6_addr
;
866 a
[0] = $5.i
; a
[1] = $7.i
; a
[2] = $9.i
; a
[3] = $11.i
;
867 a
[4] = $13.i
; a
[5] = $15.i
; a
[6] = $17.i
; a
[7] = $19.i
;
868 a
[8] = $21.i
; a
[9] = $23.i
; a
[10] = $25.i
; a
[11] = $27.i
;
869 a
[12] = $29.i
; a
[13] = $31.i
; a
[14] = $33.i
; a
[15] = $35.i
;
870 if
(his_addr.su_family
== AF_INET6
) {
871 /* XXX more sanity checks! */
872 data_dest.su_sin6.sin6_scope_id
=
873 his_addr.su_sin6.sin6_scope_id
;
875 if
($1.i
!= 6 ||
$3.i
!= 16 ||
$37.i
!= 2)
876 memset
(&data_dest
, 0, sizeof
(data_dest
));
878 | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
879 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
884 memset
(&data_dest
, 0, sizeof
(data_dest
));
885 data_dest.su_sin.sin_len
= sizeof
(struct sockaddr_in
);
886 data_dest.su_family
= AF_INET
;
887 p
= (char *)&data_dest.su_port
;
888 p
[0] = $15.i
; p
[1] = $17.i
;
889 a
= (char *)&data_dest.su_sin.sin_addr
;
890 a
[0] = $5.i
; a
[1] = $7.i
; a
[2] = $9.i
; a
[3] = $11.i
;
891 if
($1.i
!= 4 ||
$3.i
!= 4 ||
$13.i
!= 2)
892 memset
(&data_dest
, 0, sizeof
(data_dest
));
939 cmd_bytesz
= CHAR_BIT
;
946 /* this is for a bug in the BBN ftp */
987 if
(logged_in
&& $1) {
991 * Expand ~user manually since glob(3)
992 * will return the unexpanded pathname
993 * if the corresponding file/directory
994 * doesn't exist yet. Using sole glob(3)
995 * would break natural commands like
1000 if
((p
= exptilde
($1)) != NULL
) {
1018 int ret
, dec
, multby
, digit
;
1021 * Convert a number that was read as decimal number
1022 * to what it would be if it had been read as octal.
1033 ret
+= digit
* multby
;
1045 $$
= check_login1
();
1053 reply
(500, "EPSV command disabled.");
1057 $$
= check_login1
();
1065 reply
(550, "Permission denied.");
1069 $$
= check_login1
();
1075 #define CMD 0 /* beginning of command */
1076 #define ARGS 1 /* expect miscellaneous arguments */
1077 #define STR1 2 /* expect SP followed by STRING */
1078 #define STR2 3 /* expect STRING */
1079 #define OSTR 4 /* optional SP then STRING */
1080 #define ZSTR1 5 /* optional SP then optional STRING */
1081 #define ZSTR2 6 /* optional STRING after SP */
1082 #define SITECMD 7 /* SITE command */
1083 #define NSTR 8 /* Number followed by a string */
1085 #define MAXGLOBARGS 1000
1087 #define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */
1093 short implemented
; /* 1 if command is implemented */
1097 struct tab cmdtab
[] = { /* In order defined in RFC 765 */
1098 { "USER", USER
, STR1
, 1, "<sp> username" },
1099 { "PASS", PASS
, ZSTR1
, 1, "[<sp> [password]]" },
1100 { "ACCT", ACCT
, STR1
, 0, "(specify account)" },
1101 { "SMNT", SMNT
, ARGS
, 0, "(structure mount)" },
1102 { "REIN", REIN
, ARGS
, 0, "(reinitialize server state)" },
1103 { "QUIT", QUIT
, ARGS
, 1, "(terminate service)", },
1104 { "PORT", PORT
, ARGS
, 1, "<sp> b0, b1, b2, b3, b4, b5" },
1105 { "LPRT", LPRT
, ARGS
, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1106 { "EPRT", EPRT
, STR1
, 1, "<sp> |af|addr|port|" },
1107 { "PASV", PASV
, ARGS
, 1, "(set server in passive mode)" },
1108 { "LPSV", LPSV
, ARGS
, 1, "(set server in passive mode)" },
1109 { "EPSV", EPSV
, ARGS
, 1, "[<sp> af|ALL]" },
1110 { "TYPE", TYPE
, ARGS
, 1, "<sp> { A | E | I | L }" },
1111 { "STRU", STRU
, ARGS
, 1, "(specify file structure)" },
1112 { "MODE", MODE
, ARGS
, 1, "(specify transfer mode)" },
1113 { "RETR", RETR
, STR1
, 1, "<sp> file-name" },
1114 { "STOR", STOR
, STR1
, 1, "<sp> file-name" },
1115 { "APPE", APPE
, STR1
, 1, "<sp> file-name" },
1116 { "MLFL", MLFL
, OSTR
, 0, "(mail file)" },
1117 { "MAIL", MAIL
, OSTR
, 0, "(mail to user)" },
1118 { "MSND", MSND
, OSTR
, 0, "(mail send to terminal)" },
1119 { "MSOM", MSOM
, OSTR
, 0, "(mail send to terminal or mailbox)" },
1120 { "MSAM", MSAM
, OSTR
, 0, "(mail send to terminal and mailbox)" },
1121 { "MRSQ", MRSQ
, OSTR
, 0, "(mail recipient scheme question)" },
1122 { "MRCP", MRCP
, STR1
, 0, "(mail recipient)" },
1123 { "ALLO", ALLO
, ARGS
, 1, "allocate storage (vacuously)" },
1124 { "REST", REST
, ARGS
, 1, "<sp> offset (restart command)" },
1125 { "RNFR", RNFR
, STR1
, 1, "<sp> file-name" },
1126 { "RNTO", RNTO
, STR1
, 1, "<sp> file-name" },
1127 { "ABOR", ABOR
, ARGS
, 1, "(abort operation)" },
1128 { "DELE", DELE
, STR1
, 1, "<sp> file-name" },
1129 { "CWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
1130 { "XCWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
1131 { "LIST", LIST
, OSTR
, 1, "[ <sp> path-name ]" },
1132 { "NLST", NLST
, OSTR
, 1, "[ <sp> path-name ]" },
1133 { "SITE", SITE
, SITECMD
, 1, "site-cmd [ <sp> arguments ]" },
1134 { "SYST", SYST
, ARGS
, 1, "(get type of operating system)" },
1135 { "FEAT", FEAT
, ARGS
, 1, "(get extended features)" },
1136 { "STAT", STAT
, OSTR
, 1, "[ <sp> path-name ]" },
1137 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
1138 { "NOOP", NOOP
, ARGS
, 1, "" },
1139 { "MKD", MKD
, STR1
, 1, "<sp> path-name" },
1140 { "XMKD", MKD
, STR1
, 1, "<sp> path-name" },
1141 { "RMD", RMD
, STR1
, 1, "<sp> path-name" },
1142 { "XRMD", RMD
, STR1
, 1, "<sp> path-name" },
1143 { "PWD", PWD
, ARGS
, 1, "(return current directory)" },
1144 { "XPWD", PWD
, ARGS
, 1, "(return current directory)" },
1145 { "CDUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
1146 { "XCUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
1147 { "STOU", STOU
, STR1
, 1, "<sp> file-name" },
1148 { "SIZE", SIZE
, OSTR
, 1, "<sp> path-name" },
1149 { "MDTM", MDTM
, OSTR
, 1, "<sp> path-name" },
1150 { NULL
, 0, 0, 0, 0 }
1153 struct tab sitetab
[] = {
1154 { "MD5", MDFIVE
, STR1
, 1, "[ <sp> file-name ]" },
1155 { "UMASK", UMASK
, ARGS
, 1, "[ <sp> umask ]" },
1156 { "IDLE", IDLE
, ARGS
, 1, "[ <sp> maximum-idle-time ]" },
1157 { "CHMOD", CHMOD
, NSTR
, 1, "<sp> mode <sp> file-name" },
1158 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
1159 { NULL
, 0, 0, 0, 0 }
1162 static char *copy
(char *);
1163 static char *expglob
(char *);
1164 static char *exptilde
(char *);
1165 static void help
(struct tab
*, char *);
1167 lookup
(struct tab
*, char *);
1168 static int port_check
(const char *);
1170 static int port_check_v6
(const char *);
1172 static void sizecmd
(char *);
1173 static void toolong
(int);
1175 static void v4map_data_dest
(void);
1177 static int yylex(void);
1180 lookup
(struct tab
*p
, char *cmd
)
1183 for
(; p
->name
!= NULL
; p
++)
1184 if
(strcmp
(cmd
, p
->name
) == 0)
1189 #include <arpa/telnet.h>
1192 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1195 getline
(char *s
, int n
, FILE *iop
)
1199 sigset_t sset
, osset
;
1202 /* tmpline may contain saved command from urgent mode interruption */
1203 for
(c
= 0; tmpline
[c
] != '\0' && --n
> 0; ++c
) {
1205 if
(tmpline
[c
] == '\n') {
1208 syslog
(LOG_DEBUG
, "command: %s", s
);
1215 /* SIGURG would interrupt stdio if not blocked during the read loop */
1217 sigaddset
(&sset
, SIGURG
);
1218 sigprocmask
(SIG_BLOCK
, &sset
, &osset
);
1219 while
((c
= getc
(iop
)) != EOF
) {
1222 if
((c
= getc
(iop
)) == EOF
)
1228 if
((c
= getc
(iop
)) == EOF
)
1230 printf
("%c%c%c", IAC
, DONT
, 0377&c
);
1231 (void) fflush
(stdout
);
1235 if
((c
= getc
(iop
)) == EOF
)
1237 printf
("%c%c%c", IAC
, WONT
, 0377&c
);
1238 (void) fflush
(stdout
);
1243 continue
; /* ignore command */
1247 if
(--n
<= 0 || c
== '\n')
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 (void) signal
(SIGALRM
, toolong
);
1302 (void) alarm
(timeout
);
1303 if
(getline
(cbuf
, sizeof
(cbuf
)-1, stdin
) == NULL
) {
1304 reply
(221, "You could at least say goodbye.");
1309 if
(strncasecmp
(cbuf
, "PASS", 4) != 0)
1310 setproctitle
("%s: %s", proctitle
, cbuf
);
1311 #endif /* SETPROCTITLE */
1312 if
((cp
= strchr
(cbuf
, '\r'))) {
1316 if
((cp
= strpbrk
(cbuf
, " \n")))
1323 p
= lookup
(cmdtab
, cbuf
);
1327 if
(!p
->implemented
)
1328 return
(NOTIMPL
); /* state remains CMD */
1335 if
(cbuf
[cpos
] == ' ') {
1340 if
((cp2
= strpbrk
(cp
, " \n")))
1345 p
= lookup
(sitetab
, cp
);
1347 if
(guest
== 0 && p
!= 0) {
1349 if
(!p
->implemented
) {
1361 if
(cbuf
[cpos
] == '\n') {
1369 if
(cbuf
[cpos
] == ' ') {
1371 state
= state
== OSTR ? STR2
: state
+1;
1377 if
(cbuf
[cpos
] == '\n') {
1388 * Make sure the string is nonempty and \n terminated.
1390 if
(n
> 1 && cbuf
[cpos
] == '\n') {
1392 yylval.s
= copy
(cp
);
1400 if
(cbuf
[cpos
] == ' ') {
1404 if
(isdigit
(cbuf
[cpos
])) {
1406 while
(isdigit
(cbuf
[++cpos
]))
1410 yylval.u.i
= atoi
(cp
);
1419 if
(isdigit
(cbuf
[cpos
])) {
1421 while
(isdigit
(cbuf
[++cpos
]))
1425 yylval.u.i
= atoi
(cp
);
1426 yylval.u.o
= strtoull
(cp
, NULL
, 10);
1430 if
(strncasecmp
(&cbuf
[cpos
], "ALL", 3) == 0
1431 && !isalnum
(cbuf
[cpos
+ 3])) {
1435 switch
(cbuf
[cpos
++]) {
1499 fatalerror
("Unknown state in scanner.");
1509 while
(*s
!= '\0') {
1521 p
= malloc
(strlen
(s
) + 1);
1523 fatalerror
("Ran out of memory.");
1524 (void) strcpy
(p
, s
);
1529 help
(struct tab
*ctab
, char *s
)
1535 if
(ctab
== sitetab
)
1539 width
= 0, NCMDS
= 0;
1540 for
(c
= ctab
; c
->name
!= NULL
; c
++) {
1541 int len
= strlen
(c
->name
);
1547 width
= (width
+ 8) &~
7;
1552 lreply
(214, "The following %scommands are recognized %s.",
1553 type
, "(* =>'s unimplemented)");
1554 columns
= 76 / width
;
1557 lines
= (NCMDS
+ columns
- 1) / columns
;
1558 for
(i
= 0; i
< lines
; i
++) {
1560 for
(j
= 0; j
< columns
; j
++) {
1561 c
= ctab
+ j
* lines
+ i
;
1562 printf
("%s%c", c
->name
,
1563 c
->implemented ?
' ' : '*');
1564 if
(c
+ lines
>= &ctab
[NCMDS
])
1566 w
= strlen
(c
->name
) + 1;
1574 (void) fflush
(stdout
);
1576 reply
(214, "Direct comments to ftp-bugs@%s.", hostname
);
1582 c
= lookup
(ctab
, s
);
1584 reply
(502, "Unknown command %s.", s
);
1588 reply
(214, "Syntax: %s%s %s", type
, c
->name
, c
->help
);
1590 reply
(214, "%s%-*s\t%s; unimplemented.", type
, width
,
1595 sizecmd
(char *filename
)
1601 if
(stat
(filename
, &stbuf
) < 0)
1602 perror_reply
(550, filename
);
1603 else if
(!S_ISREG
(stbuf.st_mode
))
1604 reply
(550, "%s: not a plain file.", filename
);
1606 reply
(213, "%jd", (intmax_t)stbuf.st_size
);
1613 fin
= fopen
(filename
, "r");
1615 perror_reply
(550, filename
);
1618 if
(fstat
(fileno
(fin
), &stbuf
) < 0) {
1619 perror_reply
(550, filename
);
1622 } else if
(!S_ISREG
(stbuf.st_mode
)) {
1623 reply
(550, "%s: not a plain file.", filename
);
1626 } else if
(stbuf.st_size
> MAXASIZE
) {
1627 reply
(550, "%s: too large for type A SIZE.", filename
);
1633 while
((c
=getc
(fin
)) != EOF
) {
1634 if
(c
== '\n') /* will get expanded to \r\n */
1640 reply
(213, "%jd", (intmax_t)count
);
1643 reply
(504, "SIZE not implemented for type %s.",
1648 /* Return 1, if port check is done. Return 0, if not yet. */
1650 port_check
(const char *pcmd
)
1652 if
(his_addr.su_family
== AF_INET
) {
1653 if
(data_dest.su_family
!= AF_INET
) {
1655 reply
(500, "Invalid address rejected.");
1659 ((ntohs
(data_dest.su_port
) < IPPORT_RESERVED
) ||
1660 memcmp
(&data_dest.su_sin.sin_addr
,
1661 &his_addr.su_sin.sin_addr
,
1662 sizeof
(data_dest.su_sin.sin_addr
)))) {
1664 reply
(500, "Illegal PORT range rejected.");
1668 (void) close
(pdata
);
1671 reply
(200, "%s command successful.", pcmd
);
1684 reply
(530, "Please login with USER and PASS.");
1690 * Replace leading "~user" in a pathname by the user's login directory.
1691 * Returned string will be in a freshly malloced buffer unless it's NULL.
1700 if
((p
= strdup
(s
)) == NULL
)
1705 user
= p
+ 1; /* skip tilde */
1706 if
((path
= strchr
(p
, '/')) != NULL
)
1707 *(path
++) = '\0'; /* separate ~user from the rest of path */
1708 if
(*user
== '\0') /* no user specified, use the current user */
1710 /* read passwd even for the current user since we may be chrooted */
1711 if
((ppw
= getpwnam
(user
)) != NULL
) {
1712 /* user found, substitute login directory for ~user */
1714 asprintf
(&q
, "%s/%s", ppw
->pw_dir
, path
);
1716 q
= strdup
(ppw
->pw_dir
);
1720 /* user not found, undo the damage */
1728 * Expand glob(3) patterns possibly present in a pathname.
1729 * Avoid expanding to a pathname including '\r' or '\n' in order to
1730 * not disrupt the FTP protocol.
1731 * The expansion found must be unique.
1732 * Return the result as a malloced string, or NULL if an error occured.
1734 * Problem: this production is used for all pathname
1735 * processing, but only gives a 550 error reply.
1736 * This is a valid reply in some cases but not in others.
1741 char *p
, **pp
, *rval
;
1742 int flags
= GLOB_BRACE | GLOB_NOCHECK
;
1746 memset
(&gl
, 0, sizeof
(gl
));
1747 flags |
= GLOB_LIMIT
;
1748 gl.gl_matchc
= MAXGLOBARGS
;
1749 if
(glob
(s
, flags
, NULL
, &gl
) == 0 && gl.gl_pathc
!= 0) {
1750 for
(pp
= gl.gl_pathv
, p
= NULL
, n
= 0; *pp
; pp
++)
1751 if
(*(*pp
+ strcspn
(*pp
, "\r\n")) == '\0') {
1760 reply
(550, "Wildcard is ambiguous.");
1764 reply
(550, "Wildcard expansion error.");
1772 /* Return 1, if port check is done. Return 0, if not yet. */
1774 port_check_v6
(const char *pcmd
)
1776 if
(his_addr.su_family
== AF_INET6
) {
1777 if
(IN6_IS_ADDR_V4MAPPED
(&his_addr.su_sin6.sin6_addr
))
1778 /* Convert data_dest into v4 mapped sockaddr.*/
1780 if
(data_dest.su_family
!= AF_INET6
) {
1782 reply
(500, "Invalid address rejected.");
1786 ((ntohs
(data_dest.su_port
) < IPPORT_RESERVED
) ||
1787 memcmp
(&data_dest.su_sin6.sin6_addr
,
1788 &his_addr.su_sin6.sin6_addr
,
1789 sizeof
(data_dest.su_sin6.sin6_addr
)))) {
1791 reply
(500, "Illegal PORT range rejected.");
1795 (void) close
(pdata
);
1798 reply
(200, "%s command successful.", pcmd
);
1806 v4map_data_dest
(void)
1808 struct in_addr savedaddr
;
1811 if
(data_dest.su_family
!= AF_INET
) {
1813 reply
(500, "Invalid address rejected.");
1817 savedaddr
= data_dest.su_sin.sin_addr
;
1818 savedport
= data_dest.su_port
;
1820 memset
(&data_dest
, 0, sizeof
(data_dest
));
1821 data_dest.su_sin6.sin6_len
= sizeof
(struct sockaddr_in6
);
1822 data_dest.su_sin6.sin6_family
= AF_INET6
;
1823 data_dest.su_sin6.sin6_port
= savedport
;
1824 memset
((caddr_t
)&data_dest.su_sin6.sin6_addr.s6_addr
[10], 0xff, 2);
1825 memcpy
((caddr_t
)&data_dest.su_sin6.sin6_addr.s6_addr
[12],
1826 (caddr_t
)&savedaddr
, sizeof
(savedaddr
));