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[];
100 extern int noguestretr;
101 extern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */
107 static int cmd_bytesz;
110 char *fromname = NULL;
131 USER PASS ACCT REIN QUIT PORT
132 PASV TYPE STRU MODE RETR STOR
133 APPE MLFL MAIL MSND MSOM MSAM
134 MRSQ MRCP ALLO REST RNFR RNTO
135 ABOR DELE CWD LIST NLST SITE
136 STAT HELP NOOP MKD RMD PWD
137 CDUP STOU SMNT SYST SIZE MDTM
140 UMASK IDLE CHMOD MDFIVE
147 %type <u.i> check_login octal_number byte_size
148 %type <u.i> check_login_ro check_login_epsv
149 %type <u.i> struct_code mode_code type_code form_code
150 %type <s> pathstring pathname password username
151 %type <s> ALL NOTIMPL
170 : USER SP username CRLF
175 | PASS SP password CRLF
184 | PORT check_login SP host_port CRLF
187 reply(501, "No PORT allowed after EPSV ALL.");
192 if (port_check("PORT") == 1)
195 if ((his_addr.su_family != AF_INET6 ||
196 !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) {
197 /* shoud never happen */
199 reply(500, "Invalid address rejected.");
202 port_check_v6("pcmd");
207 | LPRT check_login SP host_long_port CRLF
210 reply(501, "No LPRT allowed after EPSV ALL.");
215 if (port_check("LPRT") == 1)
218 if (his_addr.su_family != AF_INET6) {
220 reply(500, "Invalid address rejected.");
223 if (port_check_v6("LPRT") == 1)
229 | EPRT check_login SP STRING CRLF
235 struct addrinfo hints;
236 struct addrinfo *res;
240 reply(501, "No EPRT allowed after EPSV ALL.");
246 memset(&data_dest, 0, sizeof(data_dest));
249 syslog(LOG_DEBUG, "%s", tmp);
251 fatalerror("not enough core");
257 memset(result, 0, sizeof(result));
258 for (i = 0; i < 3; i++) {
259 q = strchr(p, delim);
260 if (!q || *q != delim) {
263 "Invalid argument, rejected.");
272 syslog(LOG_DEBUG, "%d: %s", i, p);
276 /* some more sanity check */
291 memset(&hints, 0, sizeof(hints));
292 if (atoi(result[0]) == 1)
293 hints.ai_family = PF_INET;
295 else if (atoi(result[0]) == 2)
296 hints.ai_family = PF_INET6;
299 hints.ai_family = PF_UNSPEC; /*XXX*/
300 hints.ai_socktype = SOCK_STREAM;
301 i = getaddrinfo(result[1], result[2], &hints, &res);
304 memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
306 if (his_addr.su_family == AF_INET6
307 && data_dest.su_family == AF_INET6) {
308 /* XXX more sanity checks! */
309 data_dest.su_sin6.sin6_scope_id =
310 his_addr.su_sin6.sin6_scope_id;
316 if (port_check("EPRT") == 1)
319 if (his_addr.su_family != AF_INET6) {
321 reply(500, "Invalid address rejected.");
324 if (port_check_v6("EPRT") == 1)
330 | PASV check_login CRLF
333 reply(501, "No PASV allowed after EPSV ALL.");
337 | LPSV check_login CRLF
340 reply(501, "No LPSV allowed after EPSV ALL.");
342 long_passive("LPSV", PF_UNSPEC);
344 | EPSV check_login_epsv SP NUMBER CRLF
358 pf = -1; /*junk value*/
361 long_passive("EPSV", pf);
364 | EPSV check_login_epsv SP ALL CRLF
367 reply(200, "EPSV ALL command successful.");
371 | EPSV check_login_epsv CRLF
374 long_passive("EPSV", PF_UNSPEC);
376 | TYPE check_login SP type_code CRLF
382 if (cmd_form == FORM_N) {
383 reply(200, "Type set to A.");
387 reply(504, "Form must be N.");
391 reply(504, "Type E not implemented.");
395 reply(200, "Type set to I.");
401 if (cmd_bytesz == 8) {
403 "Type set to L (byte size 8).");
406 reply(504, "Byte size must be 8.");
407 #else /* CHAR_BIT == 8 */
408 UNIMPLEMENTED for CHAR_BIT != 8
409 #endif /* CHAR_BIT == 8 */
413 | STRU check_login SP struct_code CRLF
419 reply(200, "STRU F accepted.");
423 reply(504, "Unimplemented STRU type.");
427 | MODE check_login SP mode_code CRLF
433 reply(200, "MODE S accepted.");
437 reply(502, "Unimplemented MODE type.");
441 | ALLO check_login SP NUMBER CRLF
444 reply(202, "ALLO command ignored.");
447 | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
450 reply(202, "ALLO command ignored.");
453 | RETR check_login SP pathname CRLF
455 if (noretr || (guest && noguestretr))
456 reply(500, "RETR command disabled.");
457 else if ($2 && $4 != NULL)
463 | STOR check_login_ro SP pathname CRLF
465 if ($2 && $4 != NULL)
470 | APPE check_login_ro SP pathname CRLF
472 if ($2 && $4 != NULL)
477 | NLST check_login CRLF
482 | NLST check_login SP pathstring CRLF
488 | LIST check_login CRLF
491 retrieve(_PATH_LS " -lgA", "");
493 | LIST check_login SP pathstring CRLF
496 retrieve(_PATH_LS " -lgA %s", $4);
499 | STAT check_login SP pathname CRLF
501 if ($2 && $4 != NULL)
506 | STAT check_login CRLF
512 | DELE check_login_ro SP pathname CRLF
514 if ($2 && $4 != NULL)
519 | RNTO check_login_ro SP pathname CRLF
521 if ($2 && $4 != NULL) {
523 renamecmd(fromname, $4);
527 reply(503, "Bad sequence of commands.");
533 | ABOR check_login CRLF
536 reply(225, "ABOR command successful.");
538 | CWD check_login CRLF
544 | CWD check_login SP pathname CRLF
546 if ($2 && $4 != NULL)
555 | HELP SP STRING CRLF
559 if (strncasecmp(cp, "SITE", 4) == 0) {
573 reply(200, "NOOP command successful.");
575 | MKD check_login_ro SP pathname CRLF
577 if ($2 && $4 != NULL)
582 | RMD check_login_ro SP pathname CRLF
584 if ($2 && $4 != NULL)
589 | PWD check_login CRLF
594 | CDUP check_login CRLF
603 | SITE SP HELP SP STRING CRLF
608 | SITE SP MDFIVE check_login SP pathname CRLF
615 reply(200, "MD5(%s) = %s", $6, p);
617 perror_reply(550, $6);
622 | SITE SP UMASK check_login CRLF
628 (void) umask(oldmask);
629 reply(200, "Current UMASK is %03o.", oldmask);
632 | SITE SP UMASK check_login SP octal_number CRLF
637 if (($6 == -1) || ($6 > 0777)) {
638 reply(501, "Bad UMASK value.");
642 "UMASK set to %03o (was %03o).",
647 | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF
649 if ($4 && ($8 != NULL)) {
650 if (($6 == -1 ) || ($6 > 0777))
651 reply(501, "Bad mode value.");
652 else if (chmod($8, $6) < 0)
653 perror_reply(550, $8);
655 reply(200, "CHMOD command successful.");
660 | SITE SP check_login IDLE CRLF
664 "Current IDLE time limit is %d seconds; max %d.",
665 timeout, maxtimeout);
667 | SITE SP check_login IDLE SP NUMBER CRLF
670 if ($6.i < 30 || $6.i > maxtimeout) {
672 "Maximum IDLE time must be between 30 and %d seconds.",
676 (void) alarm(timeout);
678 "Maximum IDLE time set to %d seconds.",
683 | STOU check_login_ro SP pathname CRLF
685 if ($2 && $4 != NULL)
690 | SYST check_login CRLF
695 reply(215, "UNIX Type: L%d Version: BSD-%d",
698 reply(215, "UNIX Type: L%d", CHAR_BIT);
701 reply(215, "UNKNOWN Type: L%d", CHAR_BIT);
706 * SIZE is not in RFC959, but Postel has blessed it and
707 * it will be in the updated RFC.
709 * Return size of file in a format suitable for
710 * using with RESTART (we just count bytes).
712 | SIZE check_login SP pathname CRLF
714 if ($2 && $4 != NULL)
721 * MDTM is not in RFC959, but Postel has blessed it and
722 * it will be in the updated RFC.
724 * Return modification time of file as an ISO 3307
725 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
726 * where xxx is the fractional second (of any precision,
727 * not necessarily 3 digits)
729 | MDTM check_login SP pathname CRLF
731 if ($2 && $4 != NULL) {
733 if (stat($4, &stbuf) < 0)
734 perror_reply(550, $4);
735 else if (!S_ISREG(stbuf.st_mode)) {
736 reply(550, "%s: not a plain file.", $4);
739 t = gmtime(&stbuf.st_mtime);
741 "%04d%02d%02d%02d%02d%02d",
743 t->tm_mon+1, t->tm_mday,
744 t->tm_hour, t->tm_min, t->tm_sec);
752 reply(221, "Goodbye.");
761 yyclearin; /* discard lookahead data */
762 yyerrok; /* clear error condition */
763 state = CMD; /* reset lexer state */
767 : RNFR check_login_ro SP pathname CRLF
782 | REST check_login SP NUMBER CRLF
788 restart_point = $4.o;
789 reply(350, "Restarting at %jd. %s",
790 (intmax_t)restart_point,
791 "Send STORE or RETRIEVE to initiate transfer.");
803 $$ = (char *)calloc(1, sizeof(char));
816 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
821 data_dest.su_len = sizeof(struct sockaddr_in);
822 data_dest.su_family = AF_INET;
823 p = (char *)&data_dest.su_sin.sin_port;
824 p[0] = $9.i; p[1] = $11.i;
825 a = (char *)&data_dest.su_sin.sin_addr;
826 a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i;
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
834 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
835 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
840 memset(&data_dest, 0, sizeof(data_dest));
841 data_dest.su_len = sizeof(struct sockaddr_in6);
842 data_dest.su_family = AF_INET6;
843 p = (char *)&data_dest.su_port;
844 p[0] = $39.i; p[1] = $41.i;
845 a = (char *)&data_dest.su_sin6.sin6_addr;
846 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
847 a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i;
848 a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i;
849 a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i;
850 if (his_addr.su_family == AF_INET6) {
851 /* XXX more sanity checks! */
852 data_dest.su_sin6.sin6_scope_id =
853 his_addr.su_sin6.sin6_scope_id;
855 if ($1.i != 6 || $3.i != 16 || $37.i != 2)
856 memset(&data_dest, 0, sizeof(data_dest));
858 | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
859 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
864 memset(&data_dest, 0, sizeof(data_dest));
865 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
866 data_dest.su_family = AF_INET;
867 p = (char *)&data_dest.su_port;
868 p[0] = $15.i; p[1] = $17.i;
869 a = (char *)&data_dest.su_sin.sin_addr;
870 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i;
871 if ($1.i != 4 || $3.i != 4 || $13.i != 2)
872 memset(&data_dest, 0, sizeof(data_dest));
919 cmd_bytesz = CHAR_BIT;
926 /* this is for a bug in the BBN ftp */
967 if (logged_in && $1) {
971 * Expand ~user manually since glob(3)
972 * will return the unexpanded pathname
973 * if the corresponding file/directory
974 * doesn't exist yet. Using sole glob(3)
975 * would break natural commands like
980 if ((p = exptilde($1)) != NULL) {
998 int ret, dec, multby, digit;
1001 * Convert a number that was read as decimal number
1002 * to what it would be if it had been read as octal.
1013 ret += digit * multby;
1025 $$ = check_login1();
1033 reply(500, "EPSV command disabled.");
1037 $$ = check_login1();
1045 reply(550, "Permission denied.");
1049 $$ = check_login1();
1055 #define CMD 0 /* beginning of command */
1056 #define ARGS 1 /* expect miscellaneous arguments */
1057 #define STR1 2 /* expect SP followed by STRING */
1058 #define STR2 3 /* expect STRING */
1059 #define OSTR 4 /* optional SP then STRING */
1060 #define ZSTR1 5 /* optional SP then optional STRING */
1061 #define ZSTR2 6 /* optional STRING after SP */
1062 #define SITECMD 7 /* SITE command */
1063 #define NSTR 8 /* Number followed by a string */
1065 #define MAXGLOBARGS 1000
1067 #define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */
1073 short implemented; /* 1 if command is implemented */
1077 struct tab cmdtab[] = { /* In order defined in RFC 765 */
1078 { "USER", USER, STR1, 1, "<sp> username" },
1079 { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" },
1080 { "ACCT", ACCT, STR1, 0, "(specify account)" },
1081 { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1082 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
1083 { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
1084 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" },
1085 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1086 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1087 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
1088 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1089 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1090 { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" },
1091 { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1092 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1093 { "RETR", RETR, STR1, 1, "<sp> file-name" },
1094 { "STOR", STOR, STR1, 1, "<sp> file-name" },
1095 { "APPE", APPE, STR1, 1, "<sp> file-name" },
1096 { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1097 { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1098 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1099 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1100 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1101 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1102 { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1103 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1104 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1105 { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1106 { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1107 { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
1108 { "DELE", DELE, STR1, 1, "<sp> file-name" },
1109 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1110 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1111 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1112 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1113 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1114 { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
1115 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
1116 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1117 { "NOOP", NOOP, ARGS, 1, "" },
1118 { "MKD", MKD, STR1, 1, "<sp> path-name" },
1119 { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1120 { "RMD", RMD, STR1, 1, "<sp> path-name" },
1121 { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1122 { "PWD", PWD, ARGS, 1, "(return current directory)" },
1123 { "XPWD", PWD, ARGS, 1, "(return current directory)" },
1124 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
1125 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
1126 { "STOU", STOU, STR1, 1, "<sp> file-name" },
1127 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1128 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1129 { NULL, 0, 0, 0, 0 }
1132 struct tab sitetab[] = {
1133 { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" },
1134 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1135 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1136 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1137 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1138 { NULL, 0, 0, 0, 0 }
1141 static char *copy(char *);
1142 static char *expglob(char *);
1143 static char *exptilde(char *);
1144 static void help(struct tab *, char *);
1146 lookup(struct tab *, char *);
1147 static int port_check(const char *);
1148 static int port_check_v6(const char *);
1149 static void sizecmd(char *);
1150 static void toolong(int);
1151 static void v4map_data_dest(void);
1152 static int yylex(void);
1155 lookup(struct tab *p, char *cmd)
1158 for (; p->name != NULL; p++)
1159 if (strcmp(cmd, p->name) == 0)
1164 #include <arpa/telnet.h>
1167 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1170 getline(char *s, int n, FILE *iop)
1174 sigset_t sset, osset;
1177 /* tmpline may contain saved command from urgent mode interruption */
1178 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1180 if (tmpline[c] == '\n') {
1183 syslog(LOG_DEBUG, "command: %s", s);
1190 /* SIGURG would interrupt stdio if not blocked during the read loop */
1192 sigaddset(&sset, SIGURG);
1193 sigprocmask(SIG_BLOCK, &sset, &osset);
1194 while ((c = getc(iop)) != EOF) {
1197 if ((c = getc(iop)) == EOF)
1203 if ((c = getc(iop)) == EOF)
1205 printf("%c%c%c", IAC, DONT, 0377&c);
1206 (void) fflush(stdout);
1210 if ((c = getc(iop)) == EOF)
1212 printf("%c%c%c", IAC, WONT, 0377&c);
1213 (void) fflush(stdout);
1218 continue; /* ignore command */
1222 if (--n <= 0 || c == '\n')
1226 sigprocmask(SIG_SETMASK, &osset, NULL);
1227 if (c == EOF && cs == s)
1231 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1232 /* Don't syslog passwords */
1233 syslog(LOG_DEBUG, "command: %.5s ???", s);
1238 /* Don't syslog trailing CR-LF */
1241 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1245 syslog(LOG_DEBUG, "command: %.*s", len, s);
1256 "Timeout (%d seconds): closing control connection.", timeout);
1258 syslog(LOG_INFO, "User %s timed out after %d seconds",
1259 (pw ? pw -> pw_name : "unknown"), timeout);
1276 (void) signal(SIGALRM, toolong);
1277 (void) alarm(timeout);
1278 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1279 reply(221, "You could at least say goodbye.");
1284 if (strncasecmp(cbuf, "PASS", 4) != 0)
1285 setproctitle("%s: %s", proctitle, cbuf);
1286 #endif /* SETPROCTITLE */
1287 if ((cp = strchr(cbuf, '\r'))) {
1291 if ((cp = strpbrk(cbuf, " \n")))
1298 p = lookup(cmdtab, cbuf);
1302 if (!p->implemented)
1303 return (NOTIMPL); /* state remains CMD */
1310 if (cbuf[cpos] == ' ') {
1315 if ((cp2 = strpbrk(cp, " \n")))
1320 p = lookup(sitetab, cp);
1322 if (guest == 0 && p != 0) {
1324 if (!p->implemented) {
1336 if (cbuf[cpos] == '\n') {
1344 if (cbuf[cpos] == ' ') {
1346 state = state == OSTR ? STR2 : state+1;
1352 if (cbuf[cpos] == '\n') {
1363 * Make sure the string is nonempty and \n terminated.
1365 if (n > 1 && cbuf[cpos] == '\n') {
1367 yylval.s = copy(cp);
1375 if (cbuf[cpos] == ' ') {
1379 if (isdigit(cbuf[cpos])) {
1381 while (isdigit(cbuf[++cpos]))
1385 yylval.u.i = atoi(cp);
1394 if (isdigit(cbuf[cpos])) {
1396 while (isdigit(cbuf[++cpos]))
1400 yylval.u.i = atoi(cp);
1401 yylval.u.o = strtoull(cp, NULL, 10);
1405 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1406 && !isalnum(cbuf[cpos + 3])) {
1410 switch (cbuf[cpos++]) {
1474 fatalerror("Unknown state in scanner.");
1484 while (*s != '\0') {
1496 p = malloc(strlen(s) + 1);
1498 fatalerror("Ran out of memory.");
1499 (void) strcpy(p, s);
1504 help(struct tab *ctab, char *s)
1510 if (ctab == sitetab)
1514 width = 0, NCMDS = 0;
1515 for (c = ctab; c->name != NULL; c++) {
1516 int len = strlen(c->name);
1522 width = (width + 8) &~ 7;
1527 lreply(214, "The following %scommands are recognized %s.",
1528 type, "(* =>'s unimplemented)");
1529 columns = 76 / width;
1532 lines = (NCMDS + columns - 1) / columns;
1533 for (i = 0; i < lines; i++) {
1535 for (j = 0; j < columns; j++) {
1536 c = ctab + j * lines + i;
1537 printf("%s%c", c->name,
1538 c->implemented ? ' ' : '*');
1539 if (c + lines >= &ctab[NCMDS])
1541 w = strlen(c->name) + 1;
1549 (void) fflush(stdout);
1551 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1557 c = lookup(ctab, s);
1559 reply(502, "Unknown command %s.", s);
1563 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1565 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1570 sizecmd(char *filename)
1576 if (stat(filename, &stbuf) < 0)
1577 perror_reply(550, filename);
1578 else if (!S_ISREG(stbuf.st_mode))
1579 reply(550, "%s: not a plain file.", filename);
1581 reply(213, "%jd", (intmax_t)stbuf.st_size);
1588 fin = fopen(filename, "r");
1590 perror_reply(550, filename);
1593 if (fstat(fileno(fin), &stbuf) < 0) {
1594 perror_reply(550, filename);
1597 } else if (!S_ISREG(stbuf.st_mode)) {
1598 reply(550, "%s: not a plain file.", filename);
1601 } else if (stbuf.st_size > MAXASIZE) {
1602 reply(550, "%s: too large for type A SIZE.", filename);
1608 while((c=getc(fin)) != EOF) {
1609 if (c == '\n') /* will get expanded to \r\n */
1615 reply(213, "%jd", (intmax_t)count);
1618 reply(504, "SIZE not implemented for type %s.",
1623 /* Return 1, if port check is done. Return 0, if not yet. */
1625 port_check(const char *pcmd)
1627 if (his_addr.su_family == AF_INET) {
1628 if (data_dest.su_family != AF_INET) {
1630 reply(500, "Invalid address rejected.");
1634 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1635 memcmp(&data_dest.su_sin.sin_addr,
1636 &his_addr.su_sin.sin_addr,
1637 sizeof(data_dest.su_sin.sin_addr)))) {
1639 reply(500, "Illegal PORT range rejected.");
1643 (void) close(pdata);
1646 reply(200, "%s command successful.", pcmd);
1659 reply(530, "Please login with USER and PASS.");
1665 * Replace leading "~user" in a pathname by the user's login directory.
1666 * Returned string will be in a freshly malloced buffer unless it's NULL.
1675 if ((p = strdup(s)) == NULL)
1680 user = p + 1; /* skip tilde */
1681 if ((path = strchr(p, '/')) != NULL)
1682 *(path++) = '\0'; /* separate ~user from the rest of path */
1683 if (*user == '\0') /* no user specified, use the current user */
1685 /* read passwd even for the current user since we may be chrooted */
1686 if ((ppw = getpwnam(user)) != NULL) {
1687 /* user found, substitute login directory for ~user */
1689 asprintf(&q, "%s/%s", ppw->pw_dir, path);
1691 q = strdup(ppw->pw_dir);
1695 /* user not found, undo the damage */
1703 * Expand glob(3) patterns possibly present in a pathname.
1704 * Avoid expanding to a pathname including '\r' or '\n' in order to
1705 * not disrupt the FTP protocol.
1706 * The expansion found must be unique.
1707 * Return the result as a malloced string, or NULL if an error occured.
1709 * Problem: this production is used for all pathname
1710 * processing, but only gives a 550 error reply.
1711 * This is a valid reply in some cases but not in others.
1716 char *p, **pp, *rval;
1717 int flags = GLOB_BRACE | GLOB_NOCHECK;
1721 memset(&gl, 0, sizeof(gl));
1722 flags |= GLOB_LIMIT;
1723 gl.gl_matchc = MAXGLOBARGS;
1724 if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) {
1725 for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++)
1726 if (*(*pp + strcspn(*pp, "\r\n")) == '\0') {
1735 reply(550, "Wildcard is ambiguous.");
1739 reply(550, "Wildcard expansion error.");
1747 /* Return 1, if port check is done. Return 0, if not yet. */
1749 port_check_v6(const char *pcmd)
1751 if (his_addr.su_family == AF_INET6) {
1752 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1753 /* Convert data_dest into v4 mapped sockaddr.*/
1755 if (data_dest.su_family != AF_INET6) {
1757 reply(500, "Invalid address rejected.");
1761 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1762 memcmp(&data_dest.su_sin6.sin6_addr,
1763 &his_addr.su_sin6.sin6_addr,
1764 sizeof(data_dest.su_sin6.sin6_addr)))) {
1766 reply(500, "Illegal PORT range rejected.");
1770 (void) close(pdata);
1773 reply(200, "%s command successful.", pcmd);
1781 v4map_data_dest(void)
1783 struct in_addr savedaddr;
1786 if (data_dest.su_family != AF_INET) {
1788 reply(500, "Invalid address rejected.");
1792 savedaddr = data_dest.su_sin.sin_addr;
1793 savedport = data_dest.su_port;
1795 memset(&data_dest, 0, sizeof(data_dest));
1796 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1797 data_dest.su_sin6.sin6_family = AF_INET6;
1798 data_dest.su_sin6.sin6_port = savedport;
1799 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1800 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1801 (caddr_t)&savedaddr, sizeof(savedaddr));