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
35 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
36 * $FreeBSD: src/libexec/ftpd/ftpcmd.y,v 1.16.2.19 2003/02/11 14:28:28 yar Exp $
37 * $DragonFly: src/libexec/ftpd/ftpcmd.y,v 1.4 2004/06/19 20:36:04 joerg Exp $
41 * Grammar for FTP commands.
47 #include <sys/param.h>
48 #include <sys/socket.h>
51 #include <netinet/in.h>
71 #include "pathnames.h"
73 extern
union sockunion data_dest
, his_addr
;
76 extern
struct passwd
*pw
;
85 extern
int maxtimeout
;
87 extern
char *hostname
;
88 extern
char remotehost
[];
89 extern
char proctitle
[];
90 extern
int usedefault
;
92 extern
char tmpline
[];
96 extern
int noguestretr
;
97 extern
char *typenames
[]; /* defined in <arpa/ftp.h> included from ftpd.c */
103 static int cmd_bytesz
;
106 char *fromname
= (char *) 0;
127 USER PASS ACCT REIN QUIT PORT
128 PASV TYPE STRU MODE RETR STOR
129 APPE MLFL MAIL MSND MSOM MSAM
130 MRSQ MRCP ALLO REST RNFR RNTO
131 ABOR DELE CWD LIST NLST SITE
132 STAT HELP NOOP MKD RMD PWD
133 CDUP STOU SMNT SYST SIZE MDTM
136 UMASK IDLE CHMOD MDFIVE
143 %type
<u.i
> check_login octal_number byte_size
144 %type
<u.i
> check_login_ro check_login_epsv
145 %type
<u.i
> struct_code mode_code type_code form_code
146 %type
<s
> pathstring pathname password username
147 %type
<s
> ALL NOTIMPL
159 fromname
= (char *) 0;
160 restart_point
= (off_t
) 0;
166 : USER SP username CRLF
171 | PASS SP password CRLF
180 | PORT check_login SP host_port CRLF
183 reply
(501, "no PORT allowed after EPSV ALL");
188 if
(port_check
("PORT") == 1)
191 if
((his_addr.su_family
!= AF_INET6 ||
192 !IN6_IS_ADDR_V4MAPPED
(&his_addr.su_sin6.sin6_addr
))) {
193 /* shoud never happen */
195 reply
(500, "Invalid address rejected.");
198 port_check_v6
("pcmd");
203 | LPRT check_login SP host_long_port CRLF
206 reply
(501, "no LPRT allowed after EPSV ALL");
211 if
(port_check
("LPRT") == 1)
214 if
(his_addr.su_family
!= AF_INET6
) {
216 reply
(500, "Invalid address rejected.");
219 if
(port_check_v6
("LPRT") == 1)
225 | EPRT check_login SP STRING CRLF
231 struct addrinfo hints
;
232 struct addrinfo
*res
;
236 reply
(501, "no EPRT allowed after EPSV ALL");
242 memset
(&data_dest
, 0, sizeof
(data_dest
));
245 syslog
(LOG_DEBUG
, "%s", tmp
);
247 fatalerror
("not enough core");
253 memset
(result
, 0, sizeof
(result
));
254 for
(i
= 0; i
< 3; i
++) {
255 q
= strchr
(p
, delim
);
256 if
(!q ||
*q
!= delim
) {
259 "Invalid argument, rejected.");
268 syslog
(LOG_DEBUG
, "%d: %s", i
, p
);
272 /* some more sanity check */
287 memset
(&hints
, 0, sizeof
(hints
));
288 if
(atoi
(result
[0]) == 1)
289 hints.ai_family
= PF_INET
;
291 else if
(atoi
(result
[0]) == 2)
292 hints.ai_family
= PF_INET6
;
295 hints.ai_family
= PF_UNSPEC
; /*XXX*/
296 hints.ai_socktype
= SOCK_STREAM
;
297 i
= getaddrinfo
(result
[1], result
[2], &hints
, &res
);
300 memcpy
(&data_dest
, res
->ai_addr
, res
->ai_addrlen
);
302 if
(his_addr.su_family
== AF_INET6
303 && data_dest.su_family
== AF_INET6
) {
304 /* XXX more sanity checks! */
305 data_dest.su_sin6.sin6_scope_id
=
306 his_addr.su_sin6.sin6_scope_id
;
312 if
(port_check
("EPRT") == 1)
315 if
(his_addr.su_family
!= AF_INET6
) {
317 reply
(500, "Invalid address rejected.");
320 if
(port_check_v6
("EPRT") == 1)
326 | PASV check_login CRLF
329 reply
(501, "no PASV allowed after EPSV ALL");
333 | LPSV check_login CRLF
336 reply
(501, "no LPSV allowed after EPSV ALL");
338 long_passive
("LPSV", PF_UNSPEC
);
340 | EPSV check_login_epsv SP NUMBER CRLF
354 pf
= -1; /*junk value*/
357 long_passive
("EPSV", pf
);
360 | EPSV check_login_epsv SP ALL CRLF
364 "EPSV ALL command successful.");
368 | EPSV check_login_epsv CRLF
371 long_passive
("EPSV", PF_UNSPEC
);
373 | TYPE check_login SP type_code CRLF
379 if
(cmd_form
== FORM_N
) {
380 reply
(200, "Type set to A.");
384 reply
(504, "Form must be N.");
388 reply
(504, "Type E not implemented.");
392 reply
(200, "Type set to I.");
398 if
(cmd_bytesz
== 8) {
400 "Type set to L (byte size 8).");
403 reply
(504, "Byte size must be 8.");
404 #else /* NBBY == 8 */
405 UNIMPLEMENTED for NBBY
!= 8
406 #endif /* NBBY == 8 */
410 | STRU check_login SP struct_code CRLF
416 reply
(200, "STRU F ok.");
420 reply
(504, "Unimplemented STRU type.");
424 | MODE check_login SP mode_code CRLF
430 reply
(200, "MODE S ok.");
434 reply
(502, "Unimplemented MODE type.");
438 | ALLO check_login SP NUMBER CRLF
441 reply
(202, "ALLO command ignored.");
444 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
447 reply
(202, "ALLO command ignored.");
450 | RETR check_login SP pathname CRLF
452 if
(noretr ||
(guest
&& noguestretr
))
453 reply
(500, "RETR command is disabled");
454 else if
($2 && $4 != NULL
)
455 retrieve
((char *) 0, $4);
460 | STOR check_login_ro SP pathname CRLF
462 if
($2 && $4 != NULL
)
467 | APPE check_login_ro SP pathname CRLF
469 if
($2 && $4 != NULL
)
474 | NLST check_login CRLF
479 | NLST check_login SP pathstring CRLF
485 | LIST check_login CRLF
488 retrieve
(_PATH_LS
" -lgA", "");
490 | LIST check_login SP pathstring CRLF
493 retrieve
(_PATH_LS
" -lgA %s", $4);
496 | STAT check_login SP pathname CRLF
498 if
($2 && $4 != NULL
)
503 | STAT check_login CRLF
509 | DELE check_login_ro SP pathname CRLF
511 if
($2 && $4 != NULL
)
516 | RNTO check_login_ro SP pathname CRLF
518 if
($2 && $4 != NULL
) {
520 renamecmd
(fromname
, $4);
522 fromname
= (char *) 0;
524 reply
(503, "Bad sequence of commands.");
530 | ABOR check_login CRLF
533 reply
(225, "ABOR command successful.");
535 | CWD check_login CRLF
541 | CWD check_login SP pathname CRLF
543 if
($2 && $4 != NULL
)
550 help
(cmdtab
, (char *) 0);
552 | HELP SP STRING CRLF
556 if
(strncasecmp
(cp
, "SITE", 4) == 0) {
563 help
(sitetab
, (char *) 0);
570 reply
(200, "NOOP command successful.");
572 | MKD check_login_ro SP pathname CRLF
574 if
($2 && $4 != NULL
)
579 | RMD check_login_ro SP pathname CRLF
581 if
($2 && $4 != NULL
)
586 | PWD check_login CRLF
591 | CDUP check_login CRLF
598 help
(sitetab
, (char *) 0);
600 | SITE SP HELP SP STRING CRLF
605 | SITE SP MDFIVE check_login SP pathname CRLF
612 reply
(200, "MD5(%s) = %s", $6, p
);
614 perror_reply
(550, $6);
619 | SITE SP UMASK check_login CRLF
625 (void) umask
(oldmask
);
626 reply
(200, "Current UMASK is %03o", oldmask
);
629 | SITE SP UMASK check_login SP octal_number CRLF
634 if
(($6 == -1) ||
($6 > 0777)) {
635 reply
(501, "Bad UMASK value");
639 "UMASK set to %03o (was %03o)",
644 | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
646 if
($4 && ($8 != NULL
)) {
647 if
(($6 == -1 ) ||
($6 > 0777))
648 reply
(501, "Bad mode value");
649 else if
(chmod
($8, $6) < 0)
650 perror_reply
(550, $8);
652 reply
(200, "CHMOD command successful.");
657 | SITE SP check_login IDLE CRLF
661 "Current IDLE time limit is %d seconds; max %d",
662 timeout
, maxtimeout
);
664 | SITE SP check_login IDLE SP NUMBER CRLF
667 if
($6.i
< 30 ||
$6.i
> maxtimeout
) {
669 "Maximum IDLE time must be between 30 and %d seconds",
673 (void) alarm
((unsigned) timeout
);
675 "Maximum IDLE time set to %d seconds",
680 | STOU check_login_ro SP pathname CRLF
682 if
($2 && $4 != NULL
)
687 | SYST check_login CRLF
692 reply
(215, "UNIX Type: L%d Version: BSD-%d",
695 reply
(215, "UNIX Type: L%d", NBBY
);
698 reply
(215, "UNKNOWN Type: L%d", NBBY
);
703 * SIZE is not in RFC959, but Postel has blessed it and
704 * it will be in the updated RFC.
706 * Return size of file in a format suitable for
707 * using with RESTART (we just count bytes).
709 | SIZE check_login SP pathname CRLF
711 if
($2 && $4 != NULL
)
718 * MDTM is not in RFC959, but Postel has blessed it and
719 * it will be in the updated RFC.
721 * Return modification time of file as an ISO 3307
722 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
723 * where xxx is the fractional second (of any precision,
724 * not necessarily 3 digits)
726 | MDTM check_login SP pathname CRLF
728 if
($2 && $4 != NULL
) {
730 if
(stat
($4, &stbuf
) < 0)
732 $4, strerror
(errno
));
733 else if
(!S_ISREG
(stbuf.st_mode
)) {
734 reply
(550, "%s: not a plain file.", $4);
737 t
= gmtime
(&stbuf.st_mtime
);
739 "%04d%02d%02d%02d%02d%02d",
741 t
->tm_mon
+1, t
->tm_mday
,
742 t
->tm_hour
, t
->tm_min
, t
->tm_sec
);
750 reply
(221, "Goodbye.");
759 yyclearin; /* discard lookahead data */
760 yyerrok; /* clear error condition */
761 state
= CMD
; /* reset lexer state */
765 : RNFR check_login_ro SP pathname CRLF
767 restart_point
= (off_t
) 0;
771 fromname
= (char *) 0;
780 | REST check_login SP NUMBER CRLF
785 fromname
= (char *) 0;
786 restart_point
= $4.o
;
787 reply
(350, "Restarting at %llu. %s",
789 "Send STORE or RETRIEVE to initiate transfer.");
801 $$
= (char *)calloc
(1, sizeof
(char));
814 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
819 data_dest.su_len
= sizeof
(struct sockaddr_in
);
820 data_dest.su_family
= AF_INET
;
821 p
= (char *)&data_dest.su_sin.sin_port
;
822 p
[0] = $9.i
; p
[1] = $11.i
;
823 a
= (char *)&data_dest.su_sin.sin_addr
;
824 a
[0] = $1.i
; a
[1] = $3.i
; a
[2] = $5.i
; a
[3] = $7.i
;
829 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
830 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
831 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
832 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
833 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
838 memset
(&data_dest
, 0, sizeof
(data_dest
));
839 data_dest.su_len
= sizeof
(struct sockaddr_in6
);
840 data_dest.su_family
= AF_INET6
;
841 p
= (char *)&data_dest.su_port
;
842 p
[0] = $39.i
; p
[1] = $41.i
;
843 a
= (char *)&data_dest.su_sin6.sin6_addr
;
844 a
[0] = $5.i
; a
[1] = $7.i
; a
[2] = $9.i
; a
[3] = $11.i
;
845 a
[4] = $13.i
; a
[5] = $15.i
; a
[6] = $17.i
; a
[7] = $19.i
;
846 a
[8] = $21.i
; a
[9] = $23.i
; a
[10] = $25.i
; a
[11] = $27.i
;
847 a
[12] = $29.i
; a
[13] = $31.i
; a
[14] = $33.i
; a
[15] = $35.i
;
848 if
(his_addr.su_family
== AF_INET6
) {
849 /* XXX more sanity checks! */
850 data_dest.su_sin6.sin6_scope_id
=
851 his_addr.su_sin6.sin6_scope_id
;
853 if
($1.i
!= 6 ||
$3.i
!= 16 ||
$37.i
!= 2)
854 memset
(&data_dest
, 0, sizeof
(data_dest
));
856 | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
857 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
862 memset
(&data_dest
, 0, sizeof
(data_dest
));
863 data_dest.su_sin.sin_len
= sizeof
(struct sockaddr_in
);
864 data_dest.su_family
= AF_INET
;
865 p
= (char *)&data_dest.su_port
;
866 p
[0] = $15.i
; p
[1] = $17.i
;
867 a
= (char *)&data_dest.su_sin.sin_addr
;
868 a
[0] = $5.i
; a
[1] = $7.i
; a
[2] = $9.i
; a
[3] = $11.i
;
869 if
($1.i
!= 4 ||
$3.i
!= 4 ||
$13.i
!= 2)
870 memset
(&data_dest
, 0, sizeof
(data_dest
));
924 /* this is for a bug in the BBN ftp */
966 * Problem: this production is used for all pathname
967 * processing, but only gives a 550 error reply.
968 * This is a valid reply in some cases but not in others.
970 if
(logged_in
&& $1) {
974 GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE
;
977 memset
(&gl
, 0, sizeof
(gl
));
979 gl.gl_matchc
= MAXGLOBARGS
;
980 if
(glob
($1, flags
, NULL
, &gl
) ||
982 reply
(550, "wildcard expansion error");
986 for
(pp
= gl.gl_pathv
; *pp
; pp
++)
987 if
(strcspn
(*pp
, "\r\n") ==
997 reply
(550, "ambiguous");
1015 int ret
, dec
, multby
, digit
;
1018 * Convert a number that was read as decimal number
1019 * to what it would be if it had been read as octal.
1030 ret
+= digit
* multby
;
1042 $$
= check_login1
();
1050 reply
(500, "EPSV command disabled");
1054 $$
= check_login1
();
1062 reply
(550, "Permission denied.");
1066 $$
= check_login1
();
1072 #define CMD 0 /* beginning of command */
1073 #define ARGS 1 /* expect miscellaneous arguments */
1074 #define STR1 2 /* expect SP followed by STRING */
1075 #define STR2 3 /* expect STRING */
1076 #define OSTR 4 /* optional SP then STRING */
1077 #define ZSTR1 5 /* optional SP then optional STRING */
1078 #define ZSTR2 6 /* optional STRING after SP */
1079 #define SITECMD 7 /* SITE command */
1080 #define NSTR 8 /* Number followed by a string */
1082 #define MAXGLOBARGS 1000
1084 #define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */
1090 short implemented
; /* 1 if command is implemented */
1094 struct tab cmdtab
[] = { /* In order defined in RFC 765 */
1095 { "USER", USER
, STR1
, 1, "<sp> username" },
1096 { "PASS", PASS
, ZSTR1
, 1, "[<sp> [password]]" },
1097 { "ACCT", ACCT
, STR1
, 0, "(specify account)" },
1098 { "SMNT", SMNT
, ARGS
, 0, "(structure mount)" },
1099 { "REIN", REIN
, ARGS
, 0, "(reinitialize server state)" },
1100 { "QUIT", QUIT
, ARGS
, 1, "(terminate service)", },
1101 { "PORT", PORT
, ARGS
, 1, "<sp> b0, b1, b2, b3, b4, b5" },
1102 { "LPRT", LPRT
, ARGS
, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1103 { "EPRT", EPRT
, STR1
, 1, "<sp> |af|addr|port|" },
1104 { "PASV", PASV
, ARGS
, 1, "(set server in passive mode)" },
1105 { "LPSV", LPSV
, ARGS
, 1, "(set server in passive mode)" },
1106 { "EPSV", EPSV
, ARGS
, 1, "[<sp> af|ALL]" },
1107 { "TYPE", TYPE
, ARGS
, 1, "<sp> { A | E | I | L }" },
1108 { "STRU", STRU
, ARGS
, 1, "(specify file structure)" },
1109 { "MODE", MODE
, ARGS
, 1, "(specify transfer mode)" },
1110 { "RETR", RETR
, STR1
, 1, "<sp> file-name" },
1111 { "STOR", STOR
, STR1
, 1, "<sp> file-name" },
1112 { "APPE", APPE
, STR1
, 1, "<sp> file-name" },
1113 { "MLFL", MLFL
, OSTR
, 0, "(mail file)" },
1114 { "MAIL", MAIL
, OSTR
, 0, "(mail to user)" },
1115 { "MSND", MSND
, OSTR
, 0, "(mail send to terminal)" },
1116 { "MSOM", MSOM
, OSTR
, 0, "(mail send to terminal or mailbox)" },
1117 { "MSAM", MSAM
, OSTR
, 0, "(mail send to terminal and mailbox)" },
1118 { "MRSQ", MRSQ
, OSTR
, 0, "(mail recipient scheme question)" },
1119 { "MRCP", MRCP
, STR1
, 0, "(mail recipient)" },
1120 { "ALLO", ALLO
, ARGS
, 1, "allocate storage (vacuously)" },
1121 { "REST", REST
, ARGS
, 1, "<sp> offset (restart command)" },
1122 { "RNFR", RNFR
, STR1
, 1, "<sp> file-name" },
1123 { "RNTO", RNTO
, STR1
, 1, "<sp> file-name" },
1124 { "ABOR", ABOR
, ARGS
, 1, "(abort operation)" },
1125 { "DELE", DELE
, STR1
, 1, "<sp> file-name" },
1126 { "CWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
1127 { "XCWD", CWD
, OSTR
, 1, "[ <sp> directory-name ]" },
1128 { "LIST", LIST
, OSTR
, 1, "[ <sp> path-name ]" },
1129 { "NLST", NLST
, OSTR
, 1, "[ <sp> path-name ]" },
1130 { "SITE", SITE
, SITECMD
, 1, "site-cmd [ <sp> arguments ]" },
1131 { "SYST", SYST
, ARGS
, 1, "(get type of operating system)" },
1132 { "STAT", STAT
, OSTR
, 1, "[ <sp> path-name ]" },
1133 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
1134 { "NOOP", NOOP
, ARGS
, 1, "" },
1135 { "MKD", MKD
, STR1
, 1, "<sp> path-name" },
1136 { "XMKD", MKD
, STR1
, 1, "<sp> path-name" },
1137 { "RMD", RMD
, STR1
, 1, "<sp> path-name" },
1138 { "XRMD", RMD
, STR1
, 1, "<sp> path-name" },
1139 { "PWD", PWD
, ARGS
, 1, "(return current directory)" },
1140 { "XPWD", PWD
, ARGS
, 1, "(return current directory)" },
1141 { "CDUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
1142 { "XCUP", CDUP
, ARGS
, 1, "(change to parent directory)" },
1143 { "STOU", STOU
, STR1
, 1, "<sp> file-name" },
1144 { "SIZE", SIZE
, OSTR
, 1, "<sp> path-name" },
1145 { "MDTM", MDTM
, OSTR
, 1, "<sp> path-name" },
1146 { NULL
, 0, 0, 0, 0 }
1149 struct tab sitetab
[] = {
1150 { "MD5", MDFIVE
, STR1
, 1, "[ <sp> file-name ]" },
1151 { "UMASK", UMASK
, ARGS
, 1, "[ <sp> umask ]" },
1152 { "IDLE", IDLE
, ARGS
, 1, "[ <sp> maximum-idle-time ]" },
1153 { "CHMOD", CHMOD
, NSTR
, 1, "<sp> mode <sp> file-name" },
1154 { "HELP", HELP
, OSTR
, 1, "[ <sp> <string> ]" },
1155 { NULL
, 0, 0, 0, 0 }
1158 static char *copy
(char *);
1159 static void help
(struct tab
*, char *);
1161 lookup
(struct tab
*, char *);
1162 static int port_check
(const char *);
1163 static int port_check_v6
(const char *);
1164 static void sizecmd
(char *);
1165 static void toolong
(int);
1166 static void v4map_data_dest
(void);
1167 static int yylex (void);
1175 for
(; p
->name
!= NULL
; p
++)
1176 if
(strcmp
(cmd
, p
->name
) == 0)
1181 #include <arpa/telnet.h>
1184 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1196 /* tmpline may contain saved command from urgent mode interruption */
1197 for
(c
= 0; tmpline
[c
] != '\0' && --n
> 0; ++c
) {
1199 if
(tmpline
[c
] == '\n') {
1202 syslog
(LOG_DEBUG
, "command: %s", s
);
1209 while
((c
= getc
(iop
)) != EOF
) {
1212 if
((c
= getc
(iop
)) != EOF
) {
1218 printf
("%c%c%c", IAC
, DONT
, 0377&c
);
1219 (void) fflush
(stdout
);
1224 printf
("%c%c%c", IAC
, WONT
, 0377&c
);
1225 (void) fflush
(stdout
);
1230 continue
; /* ignore command */
1235 if
(--n
<= 0 || c
== '\n')
1238 if
(c
== EOF
&& cs
== s
)
1242 if
(!guest
&& strncasecmp
("pass ", s
, 5) == 0) {
1243 /* Don't syslog passwords */
1244 syslog
(LOG_DEBUG
, "command: %.5s ???", s
);
1249 /* Don't syslog trailing CR-LF */
1252 while
(cp
>= s
&& (*cp
== '\n' ||
*cp
== '\r')) {
1256 syslog
(LOG_DEBUG
, "command: %.*s", len
, s
);
1268 "Timeout (%d seconds): closing control connection.", timeout
);
1270 syslog
(LOG_INFO
, "User %s timed out after %d seconds",
1271 (pw ? pw
-> pw_name
: "unknown"), timeout
);
1288 (void) signal
(SIGALRM
, toolong
);
1289 (void) alarm
((unsigned) timeout
);
1290 if
(getline
(cbuf
, sizeof
(cbuf
)-1, stdin
) == NULL
) {
1291 reply
(221, "You could at least say goodbye.");
1296 if
(strncasecmp
(cbuf
, "PASS", 4) != 0)
1297 setproctitle
("%s: %s", proctitle
, cbuf
);
1298 #endif /* SETPROCTITLE */
1299 if
((cp
= strchr
(cbuf
, '\r'))) {
1303 if
((cp
= strpbrk
(cbuf
, " \n")))
1310 p
= lookup
(cmdtab
, cbuf
);
1314 if
(!p
->implemented
)
1315 return
(NOTIMPL
); /* state remains CMD */
1322 if
(cbuf
[cpos
] == ' ') {
1327 if
((cp2
= strpbrk
(cp
, " \n")))
1332 p
= lookup
(sitetab
, cp
);
1334 if
(guest
== 0 && p
!= 0) {
1336 if
(!p
->implemented
) {
1348 if
(cbuf
[cpos
] == '\n') {
1356 if
(cbuf
[cpos
] == ' ') {
1358 state
= state
== OSTR ? STR2
: state
+1;
1364 if
(cbuf
[cpos
] == '\n') {
1375 * Make sure the string is nonempty and \n terminated.
1377 if
(n
> 1 && cbuf
[cpos
] == '\n') {
1379 yylval.s
= copy
(cp
);
1387 if
(cbuf
[cpos
] == ' ') {
1391 if
(isdigit
(cbuf
[cpos
])) {
1393 while
(isdigit
(cbuf
[++cpos
]))
1397 yylval.u.i
= atoi
(cp
);
1406 if
(isdigit
(cbuf
[cpos
])) {
1408 while
(isdigit
(cbuf
[++cpos
]))
1412 yylval.u.i
= atoi
(cp
);
1413 yylval.u.o
= strtoull
(cp
, (char **)NULL
, 10);
1417 if
(strncasecmp
(&cbuf
[cpos
], "ALL", 3) == 0
1418 && !isalnum
(cbuf
[cpos
+ 3])) {
1422 switch
(cbuf
[cpos
++]) {
1486 fatalerror
("Unknown state in scanner.");
1497 while
(*s
!= '\0') {
1510 p
= malloc
((unsigned) strlen
(s
) + 1);
1512 fatalerror
("Ran out of memory.");
1513 (void) strcpy
(p
, s
);
1526 if
(ctab
== sitetab
)
1530 width
= 0, NCMDS
= 0;
1531 for
(c
= ctab
; c
->name
!= NULL
; c
++) {
1532 int len
= strlen
(c
->name
);
1538 width
= (width
+ 8) &~
7;
1543 lreply
(214, "The following %scommands are recognized %s.",
1544 type
, "(* =>'s unimplemented)");
1545 columns
= 76 / width
;
1548 lines
= (NCMDS
+ columns
- 1) / columns
;
1549 for
(i
= 0; i
< lines
; i
++) {
1551 for
(j
= 0; j
< columns
; j
++) {
1552 c
= ctab
+ j
* lines
+ i
;
1553 printf
("%s%c", c
->name
,
1554 c
->implemented ?
' ' : '*');
1555 if
(c
+ lines
>= &ctab
[NCMDS
])
1557 w
= strlen
(c
->name
) + 1;
1565 (void) fflush
(stdout
);
1567 reply
(214, "Direct comments to ftp-bugs@%s.", hostname
);
1573 c
= lookup
(ctab
, s
);
1574 if
(c
== (struct tab
*)0) {
1575 reply
(502, "Unknown command %s.", s
);
1579 reply
(214, "Syntax: %s%s %s", type
, c
->name
, c
->help
);
1581 reply
(214, "%s%-*s\t%s; unimplemented.", type
, width
,
1593 if
(stat
(filename
, &stbuf
) < 0)
1594 perror_reply
(550, filename
);
1595 else if
(!S_ISREG
(stbuf.st_mode
))
1596 reply
(550, "%s: not a plain file.", filename
);
1598 reply
(213, "%qu", stbuf.st_size
);
1605 fin
= fopen
(filename
, "r");
1607 perror_reply
(550, filename
);
1610 if
(fstat
(fileno
(fin
), &stbuf
) < 0) {
1611 perror_reply
(550, filename
);
1614 } else if
(!S_ISREG
(stbuf.st_mode
)) {
1615 reply
(550, "%s: not a plain file.", filename
);
1618 } else if
(stbuf.st_size
> MAXASIZE
) {
1619 reply
(550, "%s: too large for type A SIZE.", filename
);
1625 while
((c
=getc
(fin
)) != EOF
) {
1626 if
(c
== '\n') /* will get expanded to \r\n */
1632 reply
(213, "%qd", count
);
1635 reply
(504, "SIZE not implemented for type %s.",
1640 /* Return 1, if port check is done. Return 0, if not yet. */
1645 if
(his_addr.su_family
== AF_INET
) {
1646 if
(data_dest.su_family
!= AF_INET
) {
1648 reply
(500, "Invalid address rejected.");
1652 ((ntohs
(data_dest.su_port
) < IPPORT_RESERVED
) ||
1653 memcmp
(&data_dest.su_sin.sin_addr
,
1654 &his_addr.su_sin.sin_addr
,
1655 sizeof
(data_dest.su_sin.sin_addr
)))) {
1657 reply
(500, "Illegal PORT range rejected.");
1661 (void) close
(pdata
);
1664 reply
(200, "%s command successful.", pcmd
);
1677 reply
(530, "Please login with USER and PASS.");
1683 /* Return 1, if port check is done. Return 0, if not yet. */
1688 if
(his_addr.su_family
== AF_INET6
) {
1689 if
(IN6_IS_ADDR_V4MAPPED
(&his_addr.su_sin6.sin6_addr
))
1690 /* Convert data_dest into v4 mapped sockaddr.*/
1692 if
(data_dest.su_family
!= AF_INET6
) {
1694 reply
(500, "Invalid address rejected.");
1698 ((ntohs
(data_dest.su_port
) < IPPORT_RESERVED
) ||
1699 memcmp
(&data_dest.su_sin6.sin6_addr
,
1700 &his_addr.su_sin6.sin6_addr
,
1701 sizeof
(data_dest.su_sin6.sin6_addr
)))) {
1703 reply
(500, "Illegal PORT range rejected.");
1707 (void) close
(pdata
);
1710 reply
(200, "%s command successful.", pcmd
);
1720 struct in_addr savedaddr
;
1723 if
(data_dest.su_family
!= AF_INET
) {
1725 reply
(500, "Invalid address rejected.");
1729 savedaddr
= data_dest.su_sin.sin_addr
;
1730 savedport
= data_dest.su_port
;
1732 memset
(&data_dest
, 0, sizeof
(data_dest
));
1733 data_dest.su_sin6.sin6_len
= sizeof
(struct sockaddr_in6
);
1734 data_dest.su_sin6.sin6_family
= AF_INET6
;
1735 data_dest.su_sin6.sin6_port
= savedport
;
1736 memset
((caddr_t
)&data_dest.su_sin6.sin6_addr.s6_addr
[10], 0xff, 2);
1737 memcpy
((caddr_t
)&data_dest.su_sin6.sin6_addr.s6_addr
[12],
1738 (caddr_t
)&savedaddr
, sizeof
(savedaddr
));