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"
83 static int cmd_bytesz;
86 char *fromname = NULL;
105 USER PASS ACCT REIN QUIT PORT
106 PASV TYPE STRU MODE RETR STOR
107 APPE MLFL MAIL MSND MSOM MSAM
108 MRSQ MRCP ALLO REST RNFR RNTO
109 ABOR DELE CWD LIST NLST SITE
110 STAT HELP NOOP MKD RMD PWD
111 CDUP STOU SMNT SYST SIZE MDTM
112 LPRT LPSV EPRT EPSV FEAT
114 UMASK IDLE CHMOD MDFIVE
121 %type <u.i> check_login octal_number byte_size
122 %type <u.i> check_login_ro check_login_epsv
123 %type <u.i> struct_code mode_code type_code form_code
124 %type <s> pathstring pathname password username
125 %type <s> ALL NOTIMPL
144 : USER SP username CRLF
149 | PASS SP password CRLF
158 | PORT check_login SP host_port CRLF
161 reply(501, "No PORT allowed after EPSV ALL.");
166 if (port_check("PORT") == 1)
169 if ((his_addr.su_family != AF_INET6 ||
170 !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
171 /* shoud never happen */
173 reply(500, "Invalid address rejected.");
176 port_check_v6("pcmd");
181 | LPRT check_login SP host_long_port CRLF
184 reply(501, "No LPRT allowed after EPSV ALL.");
189 if (port_check("LPRT") == 1)
192 if (his_addr.su_family != AF_INET6) {
194 reply(500, "Invalid address rejected.");
197 if (port_check_v6("LPRT") == 1)
203 | EPRT check_login SP STRING CRLF
209 struct addrinfo hints;
210 struct addrinfo *res;
214 reply(501, "No EPRT allowed after EPSV ALL.");
220 memset(&data_dest, 0, sizeof(data_dest));
223 syslog(LOG_DEBUG, "%s", tmp);
225 fatalerror("not enough core");
231 memset(result, 0, sizeof(result));
232 for (i = 0; i < 3; i++) {
233 q = strchr(p, delim);
234 if (!q || *q != delim) {
237 "Invalid argument, rejected.");
246 syslog(LOG_DEBUG, "%d: %s", i, p);
250 /* some more sanity check */
265 memset(&hints, 0, sizeof(hints));
266 if (atoi(result[0]) == 1)
267 hints.ai_family = PF_INET;
269 else if (atoi(result[0]) == 2)
270 hints.ai_family = PF_INET6;
273 hints.ai_family = PF_UNSPEC; /*XXX*/
274 hints.ai_socktype = SOCK_STREAM;
275 i = getaddrinfo(result[1], result[2], &hints, &res);
278 memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
280 if (his_addr.su_family == AF_INET6
281 && data_dest.su_family == AF_INET6) {
282 /* XXX more sanity checks! */
283 data_dest.su_sin6.sin6_scope_id =
284 his_addr.su_sin6.sin6_scope_id;
290 if (port_check("EPRT") == 1)
293 if (his_addr.su_family != AF_INET6) {
295 reply(500, "Invalid address rejected.");
298 if (port_check_v6("EPRT") == 1)
304 | PASV check_login CRLF
307 reply(501, "No PASV allowed after EPSV ALL.");
311 | LPSV check_login CRLF
314 reply(501, "No LPSV allowed after EPSV ALL.");
316 long_passive("LPSV", PF_UNSPEC);
318 | EPSV check_login_epsv SP NUMBER CRLF
332 pf = -1; /*junk value*/
335 long_passive("EPSV", pf);
338 | EPSV check_login_epsv SP ALL CRLF
341 reply(200, "EPSV ALL command successful.");
345 | EPSV check_login_epsv CRLF
348 long_passive("EPSV", PF_UNSPEC);
350 | TYPE check_login SP type_code CRLF
356 if (cmd_form == FORM_N) {
357 reply(200, "Type set to A.");
361 reply(504, "Form must be N.");
365 reply(504, "Type E not implemented.");
369 reply(200, "Type set to I.");
375 if (cmd_bytesz == 8) {
377 "Type set to L (byte size 8).");
380 reply(504, "Byte size must be 8.");
381 #else /* CHAR_BIT == 8 */
382 UNIMPLEMENTED for CHAR_BIT != 8
383 #endif /* CHAR_BIT == 8 */
387 | STRU check_login SP struct_code CRLF
393 reply(200, "STRU F accepted.");
397 reply(504, "Unimplemented STRU type.");
401 | MODE check_login SP mode_code CRLF
407 reply(200, "MODE S accepted.");
411 reply(502, "Unimplemented MODE type.");
415 | ALLO check_login SP NUMBER CRLF
418 reply(202, "ALLO command ignored.");
421 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
424 reply(202, "ALLO command ignored.");
427 | RETR check_login SP pathname CRLF
429 if (noretr || (guest && noguestretr))
430 reply(500, "RETR command disabled.");
431 else if ($2 && $4 != NULL)
437 | STOR check_login_ro SP pathname CRLF
439 if ($2 && $4 != NULL)
444 | APPE check_login_ro SP pathname CRLF
446 if ($2 && $4 != NULL)
451 | NLST check_login CRLF
456 | NLST check_login SP pathstring CRLF
462 | LIST check_login CRLF
465 retrieve(_PATH_LS " -lgA", "");
467 | LIST check_login SP pathstring CRLF
470 retrieve(_PATH_LS " -lgA %s", $4);
473 | STAT check_login SP pathname CRLF
475 if ($2 && $4 != NULL)
480 | STAT check_login CRLF
486 | DELE check_login_ro SP pathname CRLF
488 if ($2 && $4 != NULL)
493 | RNTO check_login_ro SP pathname CRLF
495 if ($2 && $4 != NULL) {
497 renamecmd(fromname, $4);
501 reply(503, "Bad sequence of commands.");
507 | ABOR check_login CRLF
510 reply(225, "ABOR command successful.");
512 | CWD check_login CRLF
518 | CWD check_login SP pathname CRLF
520 if ($2 && $4 != NULL)
529 | HELP SP STRING CRLF
533 if (strncasecmp(cp, "SITE", 4) == 0) {
547 reply(200, "NOOP command successful.");
549 | MKD check_login_ro SP pathname CRLF
551 if ($2 && $4 != NULL)
556 | RMD check_login_ro SP pathname CRLF
558 if ($2 && $4 != NULL)
563 | PWD check_login CRLF
568 | CDUP check_login CRLF
577 | SITE SP HELP SP STRING CRLF
582 | SITE SP MDFIVE check_login SP pathname CRLF
589 reply(200, "MD5(%s) = %s", $6, p);
591 perror_reply(550, $6);
596 | SITE SP UMASK check_login CRLF
602 (void) umask(oldmask);
603 reply(200, "Current UMASK is %03o.", oldmask);
606 | SITE SP UMASK check_login SP octal_number CRLF
611 if (($6 == -1) || ($6 > 0777)) {
612 reply(501, "Bad UMASK value.");
616 "UMASK set to %03o (was %03o).",
621 | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
623 if ($4 && ($8 != NULL)) {
624 if (($6 == -1 ) || ($6 > 0777))
625 reply(501, "Bad mode value.");
626 else if (chmod($8, $6) < 0)
627 perror_reply(550, $8);
629 reply(200, "CHMOD command successful.");
634 | SITE SP check_login IDLE CRLF
638 "Current IDLE time limit is %d seconds; max %d.",
639 timeout, maxtimeout);
641 | SITE SP check_login IDLE SP NUMBER CRLF
644 if ($6.i < 30 || $6.i > maxtimeout) {
646 "Maximum IDLE time must be between 30 and %d seconds.",
650 (void) alarm(timeout);
652 "Maximum IDLE time set to %d seconds.",
657 | STOU check_login_ro SP pathname CRLF
659 if ($2 && $4 != NULL)
666 lreply(211, "Extensions supported:");
668 /* XXX these two keywords are non-standard */
674 printf(" REST STREAM\r\n");
677 /* TVFS requires UTF8, see RFC 3659 */
683 | SYST check_login CRLF
688 reply(215, "UNIX Type: L%d Version: BSD-%d",
691 reply(215, "UNIX Type: L%d", CHAR_BIT);
694 reply(215, "UNKNOWN Type: L%d", CHAR_BIT);
699 * SIZE is not in RFC959, but Postel has blessed it and
700 * it will be in the updated RFC.
702 * Return size of file in a format suitable for
703 * using with RESTART (we just count bytes).
705 | SIZE check_login SP pathname CRLF
707 if ($2 && $4 != NULL)
714 * MDTM is not in RFC959, but Postel has blessed it and
715 * it will be in the updated RFC.
717 * Return modification time of file as an ISO 3307
718 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
719 * where xxx is the fractional second (of any precision,
720 * not necessarily 3 digits)
722 | MDTM check_login SP pathname CRLF
724 if ($2 && $4 != NULL) {
726 if (stat($4, &stbuf) < 0)
727 perror_reply(550, $4);
728 else if (!S_ISREG(stbuf.st_mode)) {
729 reply(550, "%s: not a plain file.", $4);
732 t = gmtime(&stbuf.st_mtime);
734 "%04d%02d%02d%02d%02d%02d",
736 t->tm_mon+1, t->tm_mday,
737 t->tm_hour, t->tm_min, t->tm_sec);
745 reply(221, "Goodbye.");
754 yyclearin; /* discard lookahead data */
755 yyerrok; /* clear error condition */
756 state = CMD; /* reset lexer state */
760 : RNFR check_login_ro SP pathname CRLF
775 | REST check_login SP NUMBER CRLF
781 restart_point = $4.o;
782 reply(350, "Restarting at %jd. %s",
783 (intmax_t)restart_point,
784 "Send STORE or RETRIEVE to initiate transfer.");
796 $$ = (char *)calloc(1, sizeof(char));
809 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
814 data_dest.su_len = sizeof(struct sockaddr_in);
815 data_dest.su_family = AF_INET;
816 p = (char *)&data_dest.su_sin.sin_port;
817 p[0] = $9.i; p[1] = $11.i;
818 a = (char *)&data_dest.su_sin.sin_addr;
819 a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
824 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
825 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
826 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
827 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
828 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
833 memset(&data_dest, 0, sizeof(data_dest));
834 data_dest.su_len = sizeof(struct sockaddr_in6);
835 data_dest.su_family = AF_INET6;
836 p = (char *)&data_dest.su_port;
837 p[0] = $39.i; p[1] = $41.i;
838 a = (char *)&data_dest.su_sin6.sin6_addr;
839 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
840 a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
841 a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
842 a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
843 if (his_addr.su_family == AF_INET6) {
844 /* XXX more sanity checks! */
845 data_dest.su_sin6.sin6_scope_id =
846 his_addr.su_sin6.sin6_scope_id;
848 if ($1.i != 6 || $3.i != 16 || $37.i != 2)
849 memset(&data_dest, 0, sizeof(data_dest));
851 | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
852 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
857 memset(&data_dest, 0, sizeof(data_dest));
858 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
859 data_dest.su_family = AF_INET;
860 p = (char *)&data_dest.su_port;
861 p[0] = $15.i; p[1] = $17.i;
862 a = (char *)&data_dest.su_sin.sin_addr;
863 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
864 if ($1.i != 4 || $3.i != 4 || $13.i != 2)
865 memset(&data_dest, 0, sizeof(data_dest));
912 cmd_bytesz = CHAR_BIT;
919 /* this is for a bug in the BBN ftp */
960 if (logged_in && $1) {
964 * Expand ~user manually since glob(3)
965 * will return the unexpanded pathname
966 * if the corresponding file/directory
967 * doesn't exist yet. Using sole glob(3)
968 * would break natural commands like
973 if ((p = exptilde($1)) != NULL) {
991 int ret, dec, multby, digit;
994 * Convert a number that was read as decimal number
995 * to what it would be if it had been read as octal.
1006 ret += digit * multby;
1018 $$ = check_login1();
1026 reply(500, "EPSV command disabled.");
1030 $$ = check_login1();
1038 reply(550, "Permission denied.");
1042 $$ = check_login1();
1048 #define CMD 0 /* beginning of command */
1049 #define ARGS 1 /* expect miscellaneous arguments */
1050 #define STR1 2 /* expect SP followed by STRING */
1051 #define STR2 3 /* expect STRING */
1052 #define OSTR 4 /* optional SP then STRING */
1053 #define ZSTR1 5 /* optional SP then optional STRING */
1054 #define ZSTR2 6 /* optional STRING after SP */
1055 #define SITECMD 7 /* SITE command */
1056 #define NSTR 8 /* Number followed by a string */
1058 #define MAXGLOBARGS 1000
1060 #define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */
1066 short implemented; /* 1 if command is implemented */
1070 struct tab cmdtab[] = { /* In order defined in RFC 765 */
1071 { "USER", USER, STR1, 1, "<sp> username" },
1072 { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" },
1073 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1074 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1075 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
1076 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
1077 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" },
1078 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1079 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1080 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
1081 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1082 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1083 { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" },
1084 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1085 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1086 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1087 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1088 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1089 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1090 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1091 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1092 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1093 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1094 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1095 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1096 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1097 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1098 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1099 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1100 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
1101 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1102 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1103 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1104 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1105 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1106 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1107 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
1108 { "FEAT", FEAT, ARGS, 1, "(get extended features)" },
1109 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
1110 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1111 { "NOOP", NOOP, ARGS, 1, "" },
1112 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1113 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1114 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1115 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1116 { "PWD", PWD, ARGS, 1, "(return current directory)" },
1117 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
1118 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
1119 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
1120 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1121 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1122 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1123 { NULL, 0, 0, 0, 0 }
1126 struct tab sitetab[] = {
1127 { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" },
1128 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1129 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1130 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1131 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1132 { NULL, 0, 0, 0, 0 }
1135 static char *copy(char *);
1136 static char *expglob(char *);
1137 static char *exptilde(char *);
1138 static void help(struct tab *, char *);
1140 lookup(struct tab *, char *);
1141 static int port_check(const char *);
1143 static int port_check_v6(const char *);
1145 static void sizecmd(char *);
1146 static void toolong(int);
1148 static void v4map_data_dest(void);
1150 static int yylex(void);
1153 lookup(struct tab *p, char *cmd)
1156 for (; p->name != NULL; p++)
1157 if (strcmp(cmd, p->name) == 0)
1162 #include <arpa/telnet.h>
1165 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1168 getline(char *s, int n, FILE *iop)
1172 sigset_t sset, osset;
1175 /* tmpline may contain saved command from urgent mode interruption */
1176 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1178 if (tmpline[c] == '\n') {
1181 syslog(LOG_DEBUG, "command: %s", s);
1188 /* SIGURG would interrupt stdio if not blocked during the read loop */
1190 sigaddset(&sset, SIGURG);
1191 sigprocmask(SIG_BLOCK, &sset, &osset);
1192 while ((c = getc(iop)) != EOF) {
1195 if ((c = getc(iop)) == EOF)
1201 if ((c = getc(iop)) == EOF)
1203 printf("%c%c%c", IAC, DONT, 0377&c);
1204 (void) fflush(stdout);
1208 if ((c = getc(iop)) == EOF)
1210 printf("%c%c%c", IAC, WONT, 0377&c);
1211 (void) fflush(stdout);
1216 continue; /* ignore command */
1222 * If command doesn't fit into buffer, discard the
1223 * rest of the command and indicate truncation.
1224 * This prevents the command to be split up into
1225 * multiple commands.
1227 while (c != '\n' && (c = getc(iop)) != EOF)
1235 sigprocmask(SIG_SETMASK, &osset, NULL);
1236 if (c == EOF && cs == s)
1240 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1241 /* Don't syslog passwords */
1242 syslog(LOG_DEBUG, "command: %.5s ???", s);
1247 /* Don't syslog trailing CR-LF */
1250 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1254 syslog(LOG_DEBUG, "command: %.*s", len, s);
1265 "Timeout (%d seconds): closing control connection.", timeout);
1267 syslog(LOG_INFO, "User %s timed out after %d seconds",
1268 (pw ? pw -> pw_name : "unknown"), timeout);
1285 (void) signal(SIGALRM, toolong);
1286 (void) alarm(timeout);
1287 n = getline(cbuf, sizeof(cbuf)-1, stdin);
1289 reply(221, "You could at least say goodbye.");
1291 } else if (n == -2) {
1292 reply(500, "Command too long.");
1298 if (strncasecmp(cbuf, "PASS", 4) != 0)
1299 setproctitle("%s: %s", proctitle, cbuf);
1300 #endif /* SETPROCTITLE */
1301 if ((cp = strchr(cbuf, '\r'))) {
1305 if ((cp = strpbrk(cbuf, " \n")))
1312 p = lookup(cmdtab, cbuf);
1316 if (!p->implemented)
1317 return (NOTIMPL); /* state remains CMD */
1324 if (cbuf[cpos] == ' ') {
1329 if ((cp2 = strpbrk(cp, " \n")))
1334 p = lookup(sitetab, cp);
1336 if (guest == 0 && p != 0) {
1338 if (!p->implemented) {
1350 if (cbuf[cpos] == '\n') {
1358 if (cbuf[cpos] == ' ') {
1360 state = state == OSTR ? STR2 : state+1;
1366 if (cbuf[cpos] == '\n') {
1377 * Make sure the string is nonempty and \n terminated.
1379 if (n > 1 && cbuf[cpos] == '\n') {
1381 yylval.s = copy(cp);
1389 if (cbuf[cpos] == ' ') {
1393 if (isdigit(cbuf[cpos])) {
1395 while (isdigit(cbuf[++cpos]))
1399 yylval.u.i = atoi(cp);
1408 if (isdigit(cbuf[cpos])) {
1410 while (isdigit(cbuf[++cpos]))
1414 yylval.u.i = atoi(cp);
1415 yylval.u.o = strtoull(cp, NULL, 10);
1419 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1420 && !isalnum(cbuf[cpos + 3])) {
1424 switch (cbuf[cpos++]) {
1488 fatalerror("Unknown state in scanner.");
1498 while (*s != '\0') {
1510 p = malloc(strlen(s) + 1);
1512 fatalerror("Ran out of memory.");
1513 (void) strcpy(p, s);
1518 help(struct tab *ctab, char *s)
1524 if (ctab == sitetab)
1528 width = 0, NCMDS = 0;
1529 for (c = ctab; c->name != NULL; c++) {
1530 int len = strlen(c->name);
1536 width = (width + 8) &~ 7;
1541 lreply(214, "The following %scommands are recognized %s.",
1542 type, "(* =>'s unimplemented)");
1543 columns = 76 / width;
1546 lines = (NCMDS + columns - 1) / columns;
1547 for (i = 0; i < lines; i++) {
1549 for (j = 0; j < columns; j++) {
1550 c = ctab + j * lines + i;
1551 printf("%s%c", c->name,
1552 c->implemented ? ' ' : '*');
1553 if (c + lines >= &ctab[NCMDS])
1555 w = strlen(c->name) + 1;
1563 (void) fflush(stdout);
1565 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1571 c = lookup(ctab, s);
1573 reply(502, "Unknown command %s.", s);
1577 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1579 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1584 sizecmd(char *filename)
1590 if (stat(filename, &stbuf) < 0)
1591 perror_reply(550, filename);
1592 else if (!S_ISREG(stbuf.st_mode))
1593 reply(550, "%s: not a plain file.", filename);
1595 reply(213, "%jd", (intmax_t)stbuf.st_size);
1602 fin = fopen(filename, "r");
1604 perror_reply(550, filename);
1607 if (fstat(fileno(fin), &stbuf) < 0) {
1608 perror_reply(550, filename);
1611 } else if (!S_ISREG(stbuf.st_mode)) {
1612 reply(550, "%s: not a plain file.", filename);
1615 } else if (stbuf.st_size > MAXASIZE) {
1616 reply(550, "%s: too large for type A SIZE.", filename);
1622 while((c=getc(fin)) != EOF) {
1623 if (c == '\n') /* will get expanded to \r\n */
1629 reply(213, "%jd", (intmax_t)count);
1632 reply(504, "SIZE not implemented for type %s.",
1637 /* Return 1, if port check is done. Return 0, if not yet. */
1639 port_check(const char *pcmd)
1641 if (his_addr.su_family == AF_INET) {
1642 if (data_dest.su_family != AF_INET) {
1644 reply(500, "Invalid address rejected.");
1648 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1649 memcmp(&data_dest.su_sin.sin_addr,
1650 &his_addr.su_sin.sin_addr,
1651 sizeof(data_dest.su_sin.sin_addr)))) {
1653 reply(500, "Illegal PORT range rejected.");
1657 (void) close(pdata);
1660 reply(200, "%s command successful.", pcmd);
1673 reply(530, "Please login with USER and PASS.");
1679 * Replace leading "~user" in a pathname by the user's login directory.
1680 * Returned string will be in a freshly malloced buffer unless it's NULL.
1689 if ((p = strdup(s)) == NULL)
1694 user = p + 1; /* skip tilde */
1695 if ((path = strchr(p, '/')) != NULL)
1696 *(path++) = '\0'; /* separate ~user from the rest of path */
1697 if (*user == '\0') /* no user specified, use the current user */
1699 /* read passwd even for the current user since we may be chrooted */
1700 if ((ppw = getpwnam(user)) != NULL) {
1701 /* user found, substitute login directory for ~user */
1703 asprintf(&q, "%s/%s", ppw->pw_dir, path);
1705 q = strdup(ppw->pw_dir);
1709 /* user not found, undo the damage */
1717 * Expand glob(3) patterns possibly present in a pathname.
1718 * Avoid expanding to a pathname including '\r' or '\n' in order to
1719 * not disrupt the FTP protocol.
1720 * The expansion found must be unique.
1721 * Return the result as a malloced string, or NULL if an error occured.
1723 * Problem: this production is used for all pathname
1724 * processing, but only gives a 550 error reply.
1725 * This is a valid reply in some cases but not in others.
1730 char *p, **pp, *rval;
1731 int flags = GLOB_BRACE | GLOB_NOCHECK;
1735 memset(&gl, 0, sizeof(gl));
1736 flags |= GLOB_LIMIT;
1737 gl.gl_matchc = MAXGLOBARGS;
1738 if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) {
1739 for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++)
1740 if (*(*pp + strcspn(*pp, "\r\n")) == '\0') {
1749 reply(550, "Wildcard is ambiguous.");
1753 reply(550, "Wildcard expansion error.");
1761 /* Return 1, if port check is done. Return 0, if not yet. */
1763 port_check_v6(const char *pcmd)
1765 if (his_addr.su_family == AF_INET6) {
1766 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1767 /* Convert data_dest into v4 mapped sockaddr.*/
1769 if (data_dest.su_family != AF_INET6) {
1771 reply(500, "Invalid address rejected.");
1775 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1776 memcmp(&data_dest.su_sin6.sin6_addr,
1777 &his_addr.su_sin6.sin6_addr,
1778 sizeof(data_dest.su_sin6.sin6_addr)))) {
1780 reply(500, "Illegal PORT range rejected.");
1784 (void) close(pdata);
1787 reply(200, "%s command successful.", pcmd);
1795 v4map_data_dest(void)
1797 struct in_addr savedaddr;
1800 if (data_dest.su_family != AF_INET) {
1802 reply(500, "Invalid address rejected.");
1806 savedaddr = data_dest.su_sin.sin_addr;
1807 savedport = data_dest.su_port;
1809 memset(&data_dest, 0, sizeof(data_dest));
1810 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1811 data_dest.su_sin6.sin6_family = AF_INET6;
1812 data_dest.su_sin6.sin6_port = savedport;
1813 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1814 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1815 (caddr_t)&savedaddr, sizeof(savedaddr));