2 * Copyright (c) 1985, 1988, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
30 * $FreeBSD: src/libexec/ftpd/ftpcmd.y,v 1.67 2008/12/23 01:23:09 cperciva Exp $
34 * Grammar for FTP commands.
40 #include <sys/param.h>
41 #include <sys/socket.h>
44 #include <netinet/in.h>
64 #include "pathnames.h"
66 extern union sockunion data_dest, his_addr;
69 extern struct passwd *pw;
78 extern int maxtimeout;
80 extern char *hostname;
81 extern char proctitle[];
82 extern int usedefault;
83 extern char tmpline[];
85 extern int assumeutf8;
88 extern int noguestretr;
89 extern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */
95 static int cmd_bytesz;
98 char *fromname = NULL;
119 USER PASS ACCT REIN QUIT PORT
120 PASV TYPE STRU MODE RETR STOR
121 APPE MLFL MAIL MSND MSOM MSAM
122 MRSQ MRCP ALLO REST RNFR RNTO
123 ABOR DELE CWD LIST NLST SITE
124 STAT HELP NOOP MKD RMD PWD
125 CDUP STOU SMNT SYST SIZE MDTM
126 LPRT LPSV EPRT EPSV FEAT
128 UMASK IDLE CHMOD MDFIVE
135 %type <u.i> check_login octal_number byte_size
136 %type <u.i> check_login_ro check_login_epsv
137 %type <u.i> struct_code mode_code type_code form_code
138 %type <s> pathstring pathname password username
139 %type <s> ALL NOTIMPL
158 : USER SP username CRLF
163 | PASS SP password CRLF
172 | PORT check_login SP host_port CRLF
175 reply(501, "No PORT allowed after EPSV ALL.");
180 if (port_check("PORT") == 1)
183 if ((his_addr.su_family != AF_INET6 ||
184 !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
185 /* shoud never happen */
187 reply(500, "Invalid address rejected.");
190 port_check_v6("pcmd");
195 | LPRT check_login SP host_long_port CRLF
198 reply(501, "No LPRT allowed after EPSV ALL.");
203 if (port_check("LPRT") == 1)
206 if (his_addr.su_family != AF_INET6) {
208 reply(500, "Invalid address rejected.");
211 if (port_check_v6("LPRT") == 1)
217 | EPRT check_login SP STRING CRLF
223 struct addrinfo hints;
224 struct addrinfo *res;
228 reply(501, "No EPRT allowed after EPSV ALL.");
234 memset(&data_dest, 0, sizeof(data_dest));
237 syslog(LOG_DEBUG, "%s", tmp);
239 fatalerror("not enough core");
245 memset(result, 0, sizeof(result));
246 for (i = 0; i < 3; i++) {
247 q = strchr(p, delim);
248 if (!q || *q != delim) {
251 "Invalid argument, rejected.");
260 syslog(LOG_DEBUG, "%d: %s", i, p);
264 /* some more sanity check */
279 memset(&hints, 0, sizeof(hints));
280 if (atoi(result[0]) == 1)
281 hints.ai_family = PF_INET;
283 else if (atoi(result[0]) == 2)
284 hints.ai_family = PF_INET6;
287 hints.ai_family = PF_UNSPEC; /*XXX*/
288 hints.ai_socktype = SOCK_STREAM;
289 i = getaddrinfo(result[1], result[2], &hints, &res);
292 memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
294 if (his_addr.su_family == AF_INET6
295 && data_dest.su_family == AF_INET6) {
296 /* XXX more sanity checks! */
297 data_dest.su_sin6.sin6_scope_id =
298 his_addr.su_sin6.sin6_scope_id;
304 if (port_check("EPRT") == 1)
307 if (his_addr.su_family != AF_INET6) {
309 reply(500, "Invalid address rejected.");
312 if (port_check_v6("EPRT") == 1)
318 | PASV check_login CRLF
321 reply(501, "No PASV allowed after EPSV ALL.");
325 | LPSV check_login CRLF
328 reply(501, "No LPSV allowed after EPSV ALL.");
330 long_passive("LPSV", PF_UNSPEC);
332 | EPSV check_login_epsv SP NUMBER CRLF
346 pf = -1; /*junk value*/
349 long_passive("EPSV", pf);
352 | EPSV check_login_epsv SP ALL CRLF
355 reply(200, "EPSV ALL command successful.");
359 | EPSV check_login_epsv CRLF
362 long_passive("EPSV", PF_UNSPEC);
364 | TYPE check_login SP type_code CRLF
370 if (cmd_form == FORM_N) {
371 reply(200, "Type set to A.");
375 reply(504, "Form must be N.");
379 reply(504, "Type E not implemented.");
383 reply(200, "Type set to I.");
389 if (cmd_bytesz == 8) {
391 "Type set to L (byte size 8).");
394 reply(504, "Byte size must be 8.");
395 #else /* CHAR_BIT == 8 */
396 UNIMPLEMENTED for CHAR_BIT != 8
397 #endif /* CHAR_BIT == 8 */
401 | STRU check_login SP struct_code CRLF
407 reply(200, "STRU F accepted.");
411 reply(504, "Unimplemented STRU type.");
415 | MODE check_login SP mode_code CRLF
421 reply(200, "MODE S accepted.");
425 reply(502, "Unimplemented MODE type.");
429 | ALLO check_login SP NUMBER CRLF
432 reply(202, "ALLO command ignored.");
435 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
438 reply(202, "ALLO command ignored.");
441 | RETR check_login SP pathname CRLF
443 if (noretr || (guest && noguestretr))
444 reply(500, "RETR command disabled.");
445 else if ($2 && $4 != NULL)
451 | STOR check_login_ro SP pathname CRLF
453 if ($2 && $4 != NULL)
458 | APPE check_login_ro SP pathname CRLF
460 if ($2 && $4 != NULL)
465 | NLST check_login CRLF
470 | NLST check_login SP pathstring CRLF
476 | LIST check_login CRLF
479 retrieve(_PATH_LS " -lgA", "");
481 | LIST check_login SP pathstring CRLF
484 retrieve(_PATH_LS " -lgA %s", $4);
487 | STAT check_login SP pathname CRLF
489 if ($2 && $4 != NULL)
494 | STAT check_login CRLF
500 | DELE check_login_ro SP pathname CRLF
502 if ($2 && $4 != NULL)
507 | RNTO check_login_ro SP pathname CRLF
509 if ($2 && $4 != NULL) {
511 renamecmd(fromname, $4);
515 reply(503, "Bad sequence of commands.");
521 | ABOR check_login CRLF
524 reply(225, "ABOR command successful.");
526 | CWD check_login CRLF
532 | CWD check_login SP pathname CRLF
534 if ($2 && $4 != NULL)
543 | HELP SP STRING CRLF
547 if (strncasecmp(cp, "SITE", 4) == 0) {
561 reply(200, "NOOP command successful.");
563 | MKD check_login_ro SP pathname CRLF
565 if ($2 && $4 != NULL)
570 | RMD check_login_ro SP pathname CRLF
572 if ($2 && $4 != NULL)
577 | PWD check_login CRLF
582 | CDUP check_login CRLF
591 | SITE SP HELP SP STRING CRLF
596 | SITE SP MDFIVE check_login SP pathname CRLF
604 reply(200, "MD5(%s) = %s", $6, p);
606 perror_reply(550, $6);
611 reply(202, "md5 command disabled.");
614 | SITE SP UMASK check_login CRLF
621 reply(200, "Current UMASK is %03o.", oldmask);
624 | SITE SP UMASK check_login SP octal_number CRLF
629 if (($6 == -1) || ($6 > 0777)) {
630 reply(501, "Bad UMASK value.");
634 "UMASK set to %03o (was %03o).",
639 | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
641 if ($4 && ($8 != NULL)) {
642 if (($6 == -1 ) || ($6 > 0777))
643 reply(501, "Bad mode value.");
644 else if (chmod($8, $6) < 0)
645 perror_reply(550, $8);
647 reply(200, "CHMOD command successful.");
652 | SITE SP check_login IDLE CRLF
656 "Current IDLE time limit is %d seconds; max %d.",
657 timeout, maxtimeout);
659 | SITE SP check_login IDLE SP NUMBER CRLF
662 if ($6.i < 30 || $6.i > maxtimeout) {
664 "Maximum IDLE time must be between 30 and %d seconds.",
670 "Maximum IDLE time set to %d seconds.",
675 | STOU check_login_ro SP pathname CRLF
677 if ($2 && $4 != NULL)
684 lreply(211, "Extensions supported:");
686 /* XXX these two keywords are non-standard */
692 printf(" REST STREAM\r\n");
695 /* TVFS requires UTF8, see RFC 3659 */
701 | SYST check_login CRLF
706 reply(215, "UNIX Type: L%d Version: BSD-%d",
709 reply(215, "UNIX Type: L%d", CHAR_BIT);
712 reply(215, "UNKNOWN Type: L%d", CHAR_BIT);
717 * SIZE is not in RFC959, but Postel has blessed it and
718 * it will be in the updated RFC.
720 * Return size of file in a format suitable for
721 * using with RESTART (we just count bytes).
723 | SIZE check_login SP pathname CRLF
725 if ($2 && $4 != NULL)
732 * MDTM is not in RFC959, but Postel has blessed it and
733 * it will be in the updated RFC.
735 * Return modification time of file as an ISO 3307
736 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
737 * where xxx is the fractional second (of any precision,
738 * not necessarily 3 digits)
740 | MDTM check_login SP pathname CRLF
742 if ($2 && $4 != NULL) {
744 if (stat($4, &stbuf) < 0)
745 perror_reply(550, $4);
746 else if (!S_ISREG(stbuf.st_mode)) {
747 reply(550, "%s: not a plain file.", $4);
750 t = gmtime(&stbuf.st_mtime);
752 "%04d%02d%02d%02d%02d%02d",
754 t->tm_mon+1, t->tm_mday,
755 t->tm_hour, t->tm_min, t->tm_sec);
763 reply(221, "Goodbye.");
772 yyclearin; /* discard lookahead data */
773 yyerrok; /* clear error condition */
774 state = CMD; /* reset lexer state */
778 : RNFR check_login_ro SP pathname CRLF
793 | REST check_login SP NUMBER CRLF
799 restart_point = $4.o;
800 reply(350, "Restarting at %jd. %s",
801 (intmax_t)restart_point,
802 "Send STORE or RETRIEVE to initiate transfer.");
814 $$ = (char *)calloc(1, sizeof(char));
827 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
832 data_dest.su_len = sizeof(struct sockaddr_in);
833 data_dest.su_family = AF_INET;
834 p = (char *)&data_dest.su_sin.sin_port;
835 p[0] = $9.i; p[1] = $11.i;
836 a = (char *)&data_dest.su_sin.sin_addr;
837 a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
842 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
843 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
844 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
845 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
846 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
851 memset(&data_dest, 0, sizeof(data_dest));
852 data_dest.su_len = sizeof(struct sockaddr_in6);
853 data_dest.su_family = AF_INET6;
854 p = (char *)&data_dest.su_port;
855 p[0] = $39.i; p[1] = $41.i;
856 a = (char *)&data_dest.su_sin6.sin6_addr;
857 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
858 a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
859 a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
860 a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
861 if (his_addr.su_family == AF_INET6) {
862 /* XXX more sanity checks! */
863 data_dest.su_sin6.sin6_scope_id =
864 his_addr.su_sin6.sin6_scope_id;
866 if ($1.i != 6 || $3.i != 16 || $37.i != 2)
867 memset(&data_dest, 0, sizeof(data_dest));
869 | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
870 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
875 memset(&data_dest, 0, sizeof(data_dest));
876 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
877 data_dest.su_family = AF_INET;
878 p = (char *)&data_dest.su_port;
879 p[0] = $15.i; p[1] = $17.i;
880 a = (char *)&data_dest.su_sin.sin_addr;
881 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
882 if ($1.i != 4 || $3.i != 4 || $13.i != 2)
883 memset(&data_dest, 0, sizeof(data_dest));
930 cmd_bytesz = CHAR_BIT;
937 /* this is for a bug in the BBN ftp */
978 if (logged_in && $1) {
982 * Expand ~user manually since glob(3)
983 * will return the unexpanded pathname
984 * if the corresponding file/directory
985 * doesn't exist yet. Using sole glob(3)
986 * would break natural commands like
991 if ((p = exptilde($1)) != NULL) {
1009 int ret, dec, multby, digit;
1012 * Convert a number that was read as decimal number
1013 * to what it would be if it had been read as octal.
1024 ret += digit * multby;
1036 $$ = check_login1();
1044 reply(500, "EPSV command disabled.");
1048 $$ = check_login1();
1056 reply(550, "Permission denied.");
1060 $$ = check_login1();
1066 #define CMD 0 /* beginning of command */
1067 #define ARGS 1 /* expect miscellaneous arguments */
1068 #define STR1 2 /* expect SP followed by STRING */
1069 #define STR2 3 /* expect STRING */
1070 #define OSTR 4 /* optional SP then STRING */
1071 #define ZSTR1 5 /* optional SP then optional STRING */
1072 #define ZSTR2 6 /* optional STRING after SP */
1073 #define SITECMD 7 /* SITE command */
1074 #define NSTR 8 /* Number followed by a string */
1076 #define MAXGLOBARGS 1000
1078 #define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */
1084 short implemented; /* 1 if command is implemented */
1088 struct tab cmdtab[] = { /* In order defined in RFC 765 */
1089 { "USER", USER, STR1, 1, "<sp> username" },
1090 { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" },
1091 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1092 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1093 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
1094 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
1095 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" },
1096 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1097 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1098 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
1099 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1100 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1101 { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" },
1102 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1103 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1104 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1105 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1106 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1107 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1108 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1109 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1110 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1111 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1112 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1113 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1114 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1115 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1116 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1117 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1118 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
1119 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1120 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1121 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1122 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1123 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1124 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1125 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
1126 { "FEAT", FEAT, ARGS, 1, "(get extended features)" },
1127 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
1128 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1129 { "NOOP", NOOP, ARGS, 1, "" },
1130 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1131 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1132 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1133 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1134 { "PWD", PWD, ARGS, 1, "(return current directory)" },
1135 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
1136 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
1137 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
1138 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1139 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1140 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1141 { NULL, 0, 0, 0, 0 }
1144 struct tab sitetab[] = {
1145 { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" },
1146 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1147 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1148 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1149 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1150 { NULL, 0, 0, 0, 0 }
1153 static char *copy(char *);
1154 static char *expglob(char *);
1155 static char *exptilde(char *);
1156 static void help(struct tab *, char *);
1158 lookup(struct tab *, char *);
1159 static int port_check(const char *);
1161 static int port_check_v6(const char *);
1163 static void sizecmd(char *);
1164 static void toolong(int);
1166 static void v4map_data_dest(void);
1168 static int yylex(void);
1171 lookup(struct tab *p, char *cmd)
1174 for (; p->name != NULL; p++)
1175 if (strcmp(cmd, p->name) == 0)
1180 #include <arpa/telnet.h>
1183 * get_line - a hacked up version of fgets to ignore TELNET escape codes.
1186 get_line(char *s, int n, FILE *iop)
1190 sigset_t sset, osset;
1193 /* tmpline may contain saved command from urgent mode interruption */
1194 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1196 if (tmpline[c] == '\n') {
1199 syslog(LOG_DEBUG, "command: %s", s);
1206 /* SIGURG would interrupt stdio if not blocked during the read loop */
1208 sigaddset(&sset, SIGURG);
1209 sigprocmask(SIG_BLOCK, &sset, &osset);
1210 while ((c = getc(iop)) != EOF) {
1213 if ((c = getc(iop)) == EOF)
1219 if ((c = getc(iop)) == EOF)
1221 printf("%c%c%c", IAC, DONT, 0377&c);
1226 if ((c = getc(iop)) == EOF)
1228 printf("%c%c%c", IAC, WONT, 0377&c);
1234 continue; /* ignore command */
1240 * If command doesn't fit into buffer, discard the
1241 * rest of the command and indicate truncation.
1242 * This prevents the command to be split up into
1243 * multiple commands.
1245 while (c != '\n' && (c = getc(iop)) != EOF)
1253 sigprocmask(SIG_SETMASK, &osset, NULL);
1254 if (c == EOF && cs == s)
1258 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1259 /* Don't syslog passwords */
1260 syslog(LOG_DEBUG, "command: %.5s ???", s);
1265 /* Don't syslog trailing CR-LF */
1268 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1272 syslog(LOG_DEBUG, "command: %.*s", len, s);
1283 "Timeout (%d seconds): closing control connection.", timeout);
1285 syslog(LOG_INFO, "User %s timed out after %d seconds",
1286 (pw ? pw -> pw_name : "unknown"), timeout);
1303 signal(SIGALRM, toolong);
1305 n = get_line(cbuf, sizeof(cbuf)-1, stdin);
1307 reply(221, "You could at least say goodbye.");
1309 } else if (n == -2) {
1310 reply(500, "Command too long.");
1316 if (strncasecmp(cbuf, "PASS", 4) != 0)
1317 setproctitle("%s: %s", proctitle, cbuf);
1318 #endif /* SETPROCTITLE */
1319 if ((cp = strchr(cbuf, '\r'))) {
1323 if ((cp = strpbrk(cbuf, " \n")))
1330 p = lookup(cmdtab, cbuf);
1334 if (!p->implemented)
1335 return (NOTIMPL); /* state remains CMD */
1342 if (cbuf[cpos] == ' ') {
1347 if ((cp2 = strpbrk(cp, " \n")))
1352 p = lookup(sitetab, cp);
1354 if (guest == 0 && p != 0) {
1356 if (!p->implemented) {
1368 if (cbuf[cpos] == '\n') {
1376 if (cbuf[cpos] == ' ') {
1378 state = state == OSTR ? STR2 : state+1;
1384 if (cbuf[cpos] == '\n') {
1395 * Make sure the string is nonempty and \n terminated.
1397 if (n > 1 && cbuf[cpos] == '\n') {
1399 yylval.s = copy(cp);
1407 if (cbuf[cpos] == ' ') {
1411 if (isdigit(cbuf[cpos])) {
1413 while (isdigit(cbuf[++cpos]))
1417 yylval.u.i = atoi(cp);
1426 if (isdigit(cbuf[cpos])) {
1428 while (isdigit(cbuf[++cpos]))
1432 yylval.u.i = atoi(cp);
1433 yylval.u.o = strtoull(cp, NULL, 10);
1437 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1438 && !isalnum(cbuf[cpos + 3])) {
1442 switch (cbuf[cpos++]) {
1506 fatalerror("Unknown state in scanner.");
1516 while (*s != '\0') {
1528 p = malloc(strlen(s) + 1);
1530 fatalerror("Ran out of memory.");
1536 help(struct tab *ctab, char *s)
1542 if (ctab == sitetab)
1546 width = 0, NCMDS = 0;
1547 for (c = ctab; c->name != NULL; c++) {
1548 int len = strlen(c->name);
1554 width = (width + 8) &~ 7;
1559 lreply(214, "The following %scommands are recognized %s.",
1560 type, "(* =>'s unimplemented)");
1561 columns = 76 / width;
1564 lines = (NCMDS + columns - 1) / columns;
1565 for (i = 0; i < lines; i++) {
1567 for (j = 0; j < columns; j++) {
1568 c = ctab + j * lines + i;
1569 printf("%s%c", c->name,
1570 c->implemented ? ' ' : '*');
1571 if (c + lines >= &ctab[NCMDS])
1573 w = strlen(c->name) + 1;
1583 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1589 c = lookup(ctab, s);
1591 reply(502, "Unknown command %s.", s);
1595 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1597 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1602 sizecmd(char *filename)
1608 if (stat(filename, &stbuf) < 0)
1609 perror_reply(550, filename);
1610 else if (!S_ISREG(stbuf.st_mode))
1611 reply(550, "%s: not a plain file.", filename);
1613 reply(213, "%jd", (intmax_t)stbuf.st_size);
1620 fin = fopen(filename, "r");
1622 perror_reply(550, filename);
1625 if (fstat(fileno(fin), &stbuf) < 0) {
1626 perror_reply(550, filename);
1629 } else if (!S_ISREG(stbuf.st_mode)) {
1630 reply(550, "%s: not a plain file.", filename);
1633 } else if (stbuf.st_size > MAXASIZE) {
1634 reply(550, "%s: too large for type A SIZE.", filename);
1640 while((c=getc(fin)) != EOF) {
1641 if (c == '\n') /* will get expanded to \r\n */
1647 reply(213, "%jd", (intmax_t)count);
1650 reply(504, "SIZE not implemented for type %s.",
1655 /* Return 1, if port check is done. Return 0, if not yet. */
1657 port_check(const char *pcmd)
1659 if (his_addr.su_family == AF_INET) {
1660 if (data_dest.su_family != AF_INET) {
1662 reply(500, "Invalid address rejected.");
1666 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1667 memcmp(&data_dest.su_sin.sin_addr,
1668 &his_addr.su_sin.sin_addr,
1669 sizeof(data_dest.su_sin.sin_addr)))) {
1671 reply(500, "Illegal PORT range rejected.");
1678 reply(200, "%s command successful.", pcmd);
1691 reply(530, "Please login with USER and PASS.");
1697 * Replace leading "~user" in a pathname by the user's login directory.
1698 * Returned string will be in a freshly malloced buffer unless it's NULL.
1707 if ((p = strdup(s)) == NULL)
1712 user = p + 1; /* skip tilde */
1713 if ((path = strchr(p, '/')) != NULL)
1714 *(path++) = '\0'; /* separate ~user from the rest of path */
1715 if (*user == '\0') /* no user specified, use the current user */
1717 /* read passwd even for the current user since we may be chrooted */
1718 if ((ppw = getpwnam(user)) != NULL) {
1719 /* user found, substitute login directory for ~user */
1721 asprintf(&q, "%s/%s", ppw->pw_dir, path);
1723 q = strdup(ppw->pw_dir);
1727 /* user not found, undo the damage */
1735 * Expand glob(3) patterns possibly present in a pathname.
1736 * Avoid expanding to a pathname including '\r' or '\n' in order to
1737 * not disrupt the FTP protocol.
1738 * The expansion found must be unique.
1739 * Return the result as a malloced string, or NULL if an error occured.
1741 * Problem: this production is used for all pathname
1742 * processing, but only gives a 550 error reply.
1743 * This is a valid reply in some cases but not in others.
1748 char *p, **pp, *rval;
1749 int flags = GLOB_BRACE | GLOB_NOCHECK;
1753 memset(&gl, 0, sizeof(gl));
1754 /*flags |= GLOB_LIMIT;*/
1755 gl.gl_matchc = MAXGLOBARGS;
1756 if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) {
1757 for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++)
1758 if (*(*pp + strcspn(*pp, "\r\n")) == '\0') {
1767 reply(550, "Wildcard is ambiguous.");
1771 reply(550, "Wildcard expansion error.");
1779 /* Return 1, if port check is done. Return 0, if not yet. */
1781 port_check_v6(const char *pcmd)
1783 if (his_addr.su_family == AF_INET6) {
1784 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1785 /* Convert data_dest into v4 mapped sockaddr.*/
1787 if (data_dest.su_family != AF_INET6) {
1789 reply(500, "Invalid address rejected.");
1793 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1794 memcmp(&data_dest.su_sin6.sin6_addr,
1795 &his_addr.su_sin6.sin6_addr,
1796 sizeof(data_dest.su_sin6.sin6_addr)))) {
1798 reply(500, "Illegal PORT range rejected.");
1805 reply(200, "%s command successful.", pcmd);
1813 v4map_data_dest(void)
1815 struct in_addr savedaddr;
1818 if (data_dest.su_family != AF_INET) {
1820 reply(500, "Invalid address rejected.");
1824 savedaddr = data_dest.su_sin.sin_addr;
1825 savedport = data_dest.su_port;
1827 memset(&data_dest, 0, sizeof(data_dest));
1828 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1829 data_dest.su_sin6.sin6_family = AF_INET6;
1830 data_dest.su_sin6.sin6_port = savedport;
1831 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1832 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1833 (caddr_t)&savedaddr, sizeof(savedaddr));