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 *);
1149 static int port_check_v6(const char *);
1151 static void sizecmd(char *);
1152 static void toolong(int);
1154 static void v4map_data_dest(void);
1156 static int yylex(void);
1159 lookup(struct tab *p, char *cmd)
1162 for (; p->name != NULL; p++)
1163 if (strcmp(cmd, p->name) == 0)
1168 #include <arpa/telnet.h>
1171 * getline - a hacked up version of fgets to ignore TELNET escape codes.
1174 getline(char *s, int n, FILE *iop)
1178 sigset_t sset, osset;
1181 /* tmpline may contain saved command from urgent mode interruption */
1182 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1184 if (tmpline[c] == '\n') {
1187 syslog(LOG_DEBUG, "command: %s", s);
1194 /* SIGURG would interrupt stdio if not blocked during the read loop */
1196 sigaddset(&sset, SIGURG);
1197 sigprocmask(SIG_BLOCK, &sset, &osset);
1198 while ((c = getc(iop)) != EOF) {
1201 if ((c = getc(iop)) == EOF)
1207 if ((c = getc(iop)) == EOF)
1209 printf("%c%c%c", IAC, DONT, 0377&c);
1210 (void) fflush(stdout);
1214 if ((c = getc(iop)) == EOF)
1216 printf("%c%c%c", IAC, WONT, 0377&c);
1217 (void) fflush(stdout);
1222 continue; /* ignore command */
1226 if (--n <= 0 || c == '\n')
1230 sigprocmask(SIG_SETMASK, &osset, NULL);
1231 if (c == EOF && cs == s)
1235 if (!guest && strncasecmp("pass ", s, 5) == 0) {
1236 /* Don't syslog passwords */
1237 syslog(LOG_DEBUG, "command: %.5s ???", s);
1242 /* Don't syslog trailing CR-LF */
1245 while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1249 syslog(LOG_DEBUG, "command: %.*s", len, s);
1260 "Timeout (%d seconds): closing control connection.", timeout);
1262 syslog(LOG_INFO, "User %s timed out after %d seconds",
1263 (pw ? pw -> pw_name : "unknown"), timeout);
1280 (void) signal(SIGALRM, toolong);
1281 (void) alarm(timeout);
1282 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1283 reply(221, "You could at least say goodbye.");
1288 if (strncasecmp(cbuf, "PASS", 4) != 0)
1289 setproctitle("%s: %s", proctitle, cbuf);
1290 #endif /* SETPROCTITLE */
1291 if ((cp = strchr(cbuf, '\r'))) {
1295 if ((cp = strpbrk(cbuf, " \n")))
1302 p = lookup(cmdtab, cbuf);
1306 if (!p->implemented)
1307 return (NOTIMPL); /* state remains CMD */
1314 if (cbuf[cpos] == ' ') {
1319 if ((cp2 = strpbrk(cp, " \n")))
1324 p = lookup(sitetab, cp);
1326 if (guest == 0 && p != 0) {
1328 if (!p->implemented) {
1340 if (cbuf[cpos] == '\n') {
1348 if (cbuf[cpos] == ' ') {
1350 state = state == OSTR ? STR2 : state+1;
1356 if (cbuf[cpos] == '\n') {
1367 * Make sure the string is nonempty and \n terminated.
1369 if (n > 1 && cbuf[cpos] == '\n') {
1371 yylval.s = copy(cp);
1379 if (cbuf[cpos] == ' ') {
1383 if (isdigit(cbuf[cpos])) {
1385 while (isdigit(cbuf[++cpos]))
1389 yylval.u.i = atoi(cp);
1398 if (isdigit(cbuf[cpos])) {
1400 while (isdigit(cbuf[++cpos]))
1404 yylval.u.i = atoi(cp);
1405 yylval.u.o = strtoull(cp, NULL, 10);
1409 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0
1410 && !isalnum(cbuf[cpos + 3])) {
1414 switch (cbuf[cpos++]) {
1478 fatalerror("Unknown state in scanner.");
1488 while (*s != '\0') {
1500 p = malloc(strlen(s) + 1);
1502 fatalerror("Ran out of memory.");
1503 (void) strcpy(p, s);
1508 help(struct tab *ctab, char *s)
1514 if (ctab == sitetab)
1518 width = 0, NCMDS = 0;
1519 for (c = ctab; c->name != NULL; c++) {
1520 int len = strlen(c->name);
1526 width = (width + 8) &~ 7;
1531 lreply(214, "The following %scommands are recognized %s.",
1532 type, "(* =>'s unimplemented)");
1533 columns = 76 / width;
1536 lines = (NCMDS + columns - 1) / columns;
1537 for (i = 0; i < lines; i++) {
1539 for (j = 0; j < columns; j++) {
1540 c = ctab + j * lines + i;
1541 printf("%s%c", c->name,
1542 c->implemented ? ' ' : '*');
1543 if (c + lines >= &ctab[NCMDS])
1545 w = strlen(c->name) + 1;
1553 (void) fflush(stdout);
1555 reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1561 c = lookup(ctab, s);
1563 reply(502, "Unknown command %s.", s);
1567 reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1569 reply(214, "%s%-*s\t%s; unimplemented.", type, width,
1574 sizecmd(char *filename)
1580 if (stat(filename, &stbuf) < 0)
1581 perror_reply(550, filename);
1582 else if (!S_ISREG(stbuf.st_mode))
1583 reply(550, "%s: not a plain file.", filename);
1585 reply(213, "%jd", (intmax_t)stbuf.st_size);
1592 fin = fopen(filename, "r");
1594 perror_reply(550, filename);
1597 if (fstat(fileno(fin), &stbuf) < 0) {
1598 perror_reply(550, filename);
1601 } else if (!S_ISREG(stbuf.st_mode)) {
1602 reply(550, "%s: not a plain file.", filename);
1605 } else if (stbuf.st_size > MAXASIZE) {
1606 reply(550, "%s: too large for type A SIZE.", filename);
1612 while((c=getc(fin)) != EOF) {
1613 if (c == '\n') /* will get expanded to \r\n */
1619 reply(213, "%jd", (intmax_t)count);
1622 reply(504, "SIZE not implemented for type %s.",
1627 /* Return 1, if port check is done. Return 0, if not yet. */
1629 port_check(const char *pcmd)
1631 if (his_addr.su_family == AF_INET) {
1632 if (data_dest.su_family != AF_INET) {
1634 reply(500, "Invalid address rejected.");
1638 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1639 memcmp(&data_dest.su_sin.sin_addr,
1640 &his_addr.su_sin.sin_addr,
1641 sizeof(data_dest.su_sin.sin_addr)))) {
1643 reply(500, "Illegal PORT range rejected.");
1647 (void) close(pdata);
1650 reply(200, "%s command successful.", pcmd);
1663 reply(530, "Please login with USER and PASS.");
1669 * Replace leading "~user" in a pathname by the user's login directory.
1670 * Returned string will be in a freshly malloced buffer unless it's NULL.
1679 if ((p = strdup(s)) == NULL)
1684 user = p + 1; /* skip tilde */
1685 if ((path = strchr(p, '/')) != NULL)
1686 *(path++) = '\0'; /* separate ~user from the rest of path */
1687 if (*user == '\0') /* no user specified, use the current user */
1689 /* read passwd even for the current user since we may be chrooted */
1690 if ((ppw = getpwnam(user)) != NULL) {
1691 /* user found, substitute login directory for ~user */
1693 asprintf(&q, "%s/%s", ppw->pw_dir, path);
1695 q = strdup(ppw->pw_dir);
1699 /* user not found, undo the damage */
1707 * Expand glob(3) patterns possibly present in a pathname.
1708 * Avoid expanding to a pathname including '\r' or '\n' in order to
1709 * not disrupt the FTP protocol.
1710 * The expansion found must be unique.
1711 * Return the result as a malloced string, or NULL if an error occured.
1713 * Problem: this production is used for all pathname
1714 * processing, but only gives a 550 error reply.
1715 * This is a valid reply in some cases but not in others.
1720 char *p, **pp, *rval;
1721 int flags = GLOB_BRACE | GLOB_NOCHECK;
1725 memset(&gl, 0, sizeof(gl));
1726 flags |= GLOB_LIMIT;
1727 gl.gl_matchc = MAXGLOBARGS;
1728 if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) {
1729 for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++)
1730 if (*(*pp + strcspn(*pp, "\r\n")) == '\0') {
1739 reply(550, "Wildcard is ambiguous.");
1743 reply(550, "Wildcard expansion error.");
1751 /* Return 1, if port check is done. Return 0, if not yet. */
1753 port_check_v6(const char *pcmd)
1755 if (his_addr.su_family == AF_INET6) {
1756 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))
1757 /* Convert data_dest into v4 mapped sockaddr.*/
1759 if (data_dest.su_family != AF_INET6) {
1761 reply(500, "Invalid address rejected.");
1765 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) ||
1766 memcmp(&data_dest.su_sin6.sin6_addr,
1767 &his_addr.su_sin6.sin6_addr,
1768 sizeof(data_dest.su_sin6.sin6_addr)))) {
1770 reply(500, "Illegal PORT range rejected.");
1774 (void) close(pdata);
1777 reply(200, "%s command successful.", pcmd);
1785 v4map_data_dest(void)
1787 struct in_addr savedaddr;
1790 if (data_dest.su_family != AF_INET) {
1792 reply(500, "Invalid address rejected.");
1796 savedaddr = data_dest.su_sin.sin_addr;
1797 savedport = data_dest.su_port;
1799 memset(&data_dest, 0, sizeof(data_dest));
1800 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6);
1801 data_dest.su_sin6.sin6_family = AF_INET6;
1802 data_dest.su_sin6.sin6_port = savedport;
1803 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2);
1804 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12],
1805 (caddr_t)&savedaddr, sizeof(savedaddr));